Swift 4最重大的一个变化就是增加了一个Codable
协议,解决了在Swift中进行字典<->模型转换的问题。
在OC中,因为有runtime,我们能够比较方便的做字典转模型的操作,而在Swift中,没有了runtime,很难对字典转模型做自动操作,虽然网上现在也有一些第三方库,但要么使用繁琐,要么使用的是苹果不太推荐的做法。现在苹果推出了Codable
协议,彻底解决了这个问题。
简介 Codable
协议是Encodable
和Decodable
协议的组合,看命名就可以知道,它们其中一个负责编码,一个负责解码。Swift中的一些基本类型:String
,Int
,Double
,Date
,Data
,URL
都已经遵循了这个协议。而我们自定义的类型想要遵循这个协议也非常简单,不需要写任何多余的代码:
1 2 3 4 5 6 struct Person : Codable { let name: String let age: Int? }
只要一个类型中的属性都是 Codable
类型,那么这个类型就可以遵循 Codable
协议:
1 2 3 4 5 6 struct Book : Codable { let price: Double let author: Person }
Swift自带的 Array
和 Dictionary
也可以遵循 Codable 协议,只要里面的元素都是 codable 类型:
1 2 3 4 5 6 struct Book : Codable { let price: Double let authors: [Person ] }
自动编码、解码 默认Coding Key 一个类型只要遵循了Codable
协议,就能利用PropertyListEncoder
和 JSONEncoder
进行自动解码和编码了,默认的Coding Key就是属性名:
1 2 3 4 5 6 7 let dic = ["name" : "lijun" , "age" : 30 ] as [String : Any ]let data = try ! JSONSerialization .data(withJSONObject: dic, options: [])let person = try ? JSONDecoder ().decode(Person .self , from: data)
自定义Coding Key 大部分情况,从API的字段命名规则和app端是不一样的,所以需要使用自定义的Coding Key。这也很方便,只需要在类型内部写一个 CodingKeys
枚举,并遵循CodingKes
协议即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct Person : Codable { let name: String let age: Int? enum CodingKeys : String , CodingKey { case name = "title" case age } } let dic = ["title" : "lijun" , "age" : 30 ] as [String : Any ]let data = try ! JSONSerialization .data(withJSONObject: dic, options: [])let person = try ? JSONDecoder ().decode(Person .self , from: data)
手动编码、解码 有时候,API返回的数据结构并不一定能和模型的属性一一对上,比如可能只需要某个字段里的某一个字段,这时候就可以自己手动实现Decode
协议:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 struct Person { let name: String let age: Int? enum CodingKeys : String , CodingKey { case name = "title" case extraInfo = "extra_info" } enum ExtraInfoKeys : String , CodingKey { case age } } extension Person : Decodable { init (from decoder: Decoder ) throws { let values = try decoder.container(keyedBy: CodingKeys .self ) name = try values.decode(String .self , forKey: .name) let extraInfo = try values.nestedContainer(keyedBy: ExtraInfoKeys .self , forKey: .extraInfo) age = try ? extraInfo.decode(Int .self , forKey: .age) } } let dic = ["title" : "lijun" , "extra_info" : ["age" : 30 ]] as [String : Any ]let data = try ! JSONSerialization .data(withJSONObject: dic, options: [])let penson = try ? JSONDecoder ().decode(Person .self , from: data)
手动实现Encodable
协议同理:
1 2 3 4 5 6 7 8 9 10 extension Person : Encodable { func encode (to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys .self ) try container.encode(name, forKey: .name) var extraInfo = container.nestedContainer(keyedBy: ExtraInfoKeys .self , forKey: .extraInfo) try ? extraInfo.encode(age, forKey: .age) } }
参考资料