Codeable is a protocol used to convert a Swift object to a Data type. Codeable is a type alias for Encodealbe and Decodable protocal. This is an official and easy-to-use protocal that parsing JSON object from the server into a Swift Object.
Encodable is often used when the Swift object must be serialized and sent to the server. On the other hand, Decodable is used when JSON from Server must be deserialised.
Furthermore, custom objects conform Codable can now be saved and retrieved from UserDefaults directly in just 3 steps. Save a lot of time.
Basic parsing – Simple JSON
1 2 3 4 5 6 7 8 9 10 11 | { "key_str": "string value", "key_int": 1, "key_double": 100.0, "key_int_array": [ 1, 2, 3 ] } |
Let’s take the above JSON as an example and try to parse it into a Swift object. It has 4 fields of types: String, Int, Double and Int Array. They are all common types in JSON.
1 2 3 4 5 6 7 | struct DemoCodableStruct: Codable { let key_str: String let key_int: Int let key_double: Double let key_int_array: [Int] } |
By creating a conform Codable object, JSON can decode directly into a Swift object.
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 30 31 | let basicJSON = """ { "key_str": "string value", "key_int": 1, "key_double": 100.0, "key_int_array": [ 1, 2, 3 ] } """ do { let basicJSONData = basicJSON.data(using: .utf8)! let demoCodableStruct = try JSONDecoder().decode(DemoCodableStruct.self, from: basicJSONData) print("(demoCodableStruct)") /* Log: DemoCodableStruct( key_str: "string value", key_int: 1, key_double: 100.0, key_int_array: [1, 2, 3] ) */ } catch { print("Error during ecoding: (error.localizedDescription)") } |
By default, the variable name in the swift structure will map with the key name in JSON.
Key customisation
According to the coding convention, snake case (eg foo_key) is usually used in JSON but camel case (eg fooKey) is commonly used in Swift. Hence, Apple introduced a CodeingKey protocal that provides the ability to customize key names when we parse JSON.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | struct DemoCodableStruct: Codable { let strProp: String let intProp: Int let doubleProp: Double let intArrayProp: [Int] enum CustomMappingKey: String, CodingKey { case strPropKey = "key_str" case intPropKey = "key_int" case doublePropKey = "key_double" case intArrayPropKey = "key_int_array" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CustomMappingKey.self) strProp = try container.decode(String.self, forKey: .strPropKey) intProp = try container.decode(Int.self, forKey: .intPropKey) doubleProp = try container.decode(Double.self, forKey: .doublePropKey) intArrayProp = try container.decode([Int].self, forKey: .intArrayPropKey) } } |
Encoding customisation
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 | struct DemoCodableStruct: Codable { let strProp: String let intProp: Int let doubleProp: Double let intArrayProp: [Int] enum CustomMappingKey: String, CodingKey { case strPropKey = "key_str" case intPropKey = "key_int" case doublePropKey = "key_double" case intArrayPropKey = "key_int_array" } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CustomMappingKey.self) try container.encode(strProp, forKey: .strPropKey) try container.encode(intProp, forKey: .intPropKey) /* Ignored properties: ``` try container.encode(strProp, doubleProp: .doublePropKey) try container.encode(strProp, intArrayProp: .intArrayPropKey) ``` */ } } |
To reduce traffic to the server, only the required fields can be Serialized and sent to the server, encoding (to encoder: Encoder) provides the ability to customize serialization and ignore redundant fields. In the above example, only strProp and intProp are serialized and doubleProp and intArrayProp are ignored.