【问题标题】:Encode / Decode Structs using Encodable / Decodable from Swift 4使用 Swift 4 中的 Encodable/Decodable 对结构进行编码/解码
【发布时间】:2019-03-13 03:06:23
【问题描述】:

我不太习惯结构,我知道使用结构的唯一方法就是这样......

struct UserDetails {
  let name: String
  let message: String

  init(name: String, message: String) {
    self.name = name
    self.message = message
  }
}

一旦我收到namemessage 的值,我就会像这样将它们添加到结构中......

let userDetails = UserDetails(name: theName, message: theMessage)

然后将其添加到结构类型的数组中...

self.userDetailsArray.append(userDetails) 

现在终于,当我想获得个人姓名或信息时,我会这样......

let user = userDetailsArray[indexPath.row]
cell.userNameLabel.text = user.name
cell.messageLabel.text = user.message

这也很好用。但我担心的是……我上面指定的方法是一种非常古老的方法,因为 swift 已经提出了NSCodingCodable 协议。但我不知道如何实现它们。

希望有人可以展示我如何将NSCoding/Codable 协议应用于我上面提到的示例...

【问题讨论】:

  • 你没有展示任何与NSCoding相关的代码,你考虑在哪里使用它?
  • Codable 仅在您需要从/到 JSON 或属性列表数据(反)序列化数据时有用,否则您的 非常旧的方法 非常好。跨度>
  • .@luk2302 编码器和解码器是在我感觉的 init 方法之后出现的......但是我不确定它是如何使用的。编码器和解码器也会自动处理解析部分,对吧..?这就是我想知道的..
  • 我很抱歉@vadian..但是你能解释一下serialize the data to JSON or Property List 到底是什么意思..吗?是不是意味着转换成json..?

标签: ios swift struct


【解决方案1】:

您可以在互联网上找到很多示例。这是使用Codable 协议的通用函数。让我们举个例子:

struct UserDetails: Codable {
  let name: String, message: String
}

现在让我们看看来自gist的精彩扩展:

extension Encodable {
    func encode(with encoder: JSONEncoder = JSONEncoder()) throws -> Data {
        return try encoder.encode(self)
    }
}

extension Decodable {
    static func decode(with decoder: JSONDecoder = JSONDecoder(), from data: Data) throws -> Self {
        return try decoder.decode(Self.self, from: data)
    }
}

它只对基于Class 指定的encodedecode 值执行一个简单的函数。

现在,它变得更简单了,就像您只需传递从 API 接收的 data

if let arrUserDetails: [UserDetails] = try? [UserDetails].decode(from: data!) {
    print("UserDetails \(arrUserDetails)")   
}

这是您可以使用最新的EncodableDecodable 协议的方法。

【讨论】:

  • 感谢@Sohil R. Memon。我浏览了你提供的链接,有这条线..let lang: Language? = try? Language.decode(from: data!)你能说一下打印lang会给我什么..?我会得到lang.name = "Swift"lang.version = "4"...吗?
  • 是的,这就是如何使用的解释!您可以参考我的回答来解决您的问题! @v.bw
  • 好的@Sohil R. Memon。但是有一个问题......@vadian 在 cmets 中指出“当我们想要转换为 json 时使用编码器/解码器”但是在你建议的示例中,我们是否正在转换为 json ......?因为最后我们最终以与我在问题本身中所述的相同方式获得个人价值观......是......?
【解决方案2】:

您的代码很好。要应用 Codable 来解析/存储/检索您的用户详细信息列表,您需要像下面这样定义您的结构,

struct UserDetails: Codable {
    let name: String
    let message: String

    enum CodingKeys: String, CodingKey {
        //Uncomment the following commentted lines, if your JSON formatted data comes with different keys like bellow
        case name       //= "user_name"
        case message    //= "user_message"
    }

    init(name: String, message: String) {
        self.name = name
        self.message = message
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(message, forKey: .message)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        message = try container.decode(String.self, forKey: .message)
    }
}

要解析来自 web api 调用的响应,您可以使用以下函数

func makeAPICall(){
    guard let url = URL(string: "https://www.your-web-api") else { return }
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else { return }
        do {
            let decoder = JSONDecoder()
            let userDetails = try decoder.decode([UserDetails].self, from: data)
            print(userDetails)
        } catch let err {
            print("Error", err)
        }
    }.resume()
}

要存储你的用户详细信息列表,你可以这样写

func storeUserDetails(userDetails: [UserDetails]) {
    do {
        let data = try PropertyListEncoder().encode(userDetails)
        let success = NSKeyedArchiver.archiveRootObject(data, toFile: filePath(key: "file.archive"))
        print(success ? "Successful save" : "Save Failed")
    } catch {
        print("Save Failed")
    }
}

要检索您的用户详细信息列表,请使用此功能

func retrieveUserDetails() -> [UserDetails]? {
    guard let data = NSKeyedUnarchiver.unarchiveObject(withFile: filePath(key: "file.archive")) as? Data else { return nil }
    do {
        let products = try PropertyListDecoder().decode([UserDetails].self, from: data)
        return products
    } catch {
        print("Retrieve Failed")
        return nil
    }
}

以及获取文件路径的辅助函数

func filePath(key:String) -> String {
    let manager = FileManager.default
    let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first
    return (url!.appendingPathComponent(key).path)
}

一些不错的读物:

  1. Encoding and Decoding Custom Types
  2. Encoding, Decoding and Serialization in Swift 4
  3. Everything about Codable in Swift 4

【讨论】:

  • 在这里写自己的encode(to: Encoder)init(from: Decoder) 有点过头了。另外,为了展示自定义编码键的示例,我想最好不要在注释掉的部分重复case,而只是注释掉= "<custom coding key string>",如case name // = "user_name"。或者新手可能只是取消注释那些被注释掉的案例并得到案例重复和编译错误,并且会感到困惑。
  • 提问者得到响应运行时错误是什么,因为他的调用服务器用不同的密钥字符串响应。提问者不会一头雾水吗?至少他可以尝试使用这些选项来了解和了解更多信息。
  • 我不想再添加如何使用自定义键的示例,我只是说在第一种情况下使用case name // = "user_name" // uncomment it, if you need custom key 而不是case name … \n // uncomment it, if you need custom key \n // case name = "user_name" 更具表现力,您只需在之前删除// = 你得到工作代码。在您的情况下,您还必须不要忘记先删除case name
  • 感谢@MehediHasan 的回答。请不要介意我问这个...您能否从您的示例中展示如何最终调用retrieveUserDetails 函数来获取所需的数据..?
  • 如果您正在尝试 iOS 项目,请使用结构定义创建一个 swift 文件并将所有其余函数添加到 ViewController 类中。在 viewDidLoad 函数 if let userDetails = retrieveUserDetails() { print(userDetails) } else{ print("Retrieve Failed") } 中调用如下检索函数
猜你喜欢
  • 2018-01-19
  • 1970-01-01
  • 2018-08-14
  • 2021-08-07
  • 1970-01-01
  • 2019-10-11
  • 2018-04-10
  • 2017-11-16
  • 1970-01-01
相关资源
最近更新 更多