Working with Codable
1. Conforming to Codable Protocol
| Property | Type | Description |
|---|---|---|
| Codable | protocol |
Combines Encodable and Decodable protocols |
| Encodable | protocol |
Defines types that can be encoded to external representation (e.g., JSON) |
| Decodable | protocol |
Defines types that can be decoded from external representation (e.g., JSON) |
Example: Custom Property Names
struct User: Codable {
@SerializedName("full_name")
let name: String
let email: String
}
Note:
Codable is a type alias for Encodable & Decodable. It allows Swift types to be encoded and decoded using JSONEncoder and JSONDecoder.
2. Using JSONEncoder to Encode
| Property | Value | Description |
|---|---|---|
| encode(_:) | func encode<T>(value: T) throws -> Data |
Encodes a value into JSON data |
| outputFormatting | JSONEncoder.OutputFormatting |
Controls formatting of the output JSON (e.g. pretty-printing) |
| dateEncodingStrategy | JSONEncoder.DateEncodingStrategy |
Defines how dates are encoded (e.g. as ISO8601, milliseconds) |
| dataEncodingStrategy | JSONEncoder.DataEncodingStrategy |
Defines how Data is encoded (e.g. base64) |
Example: Encoding a Person Object
import Foundation
struct Person: Codable {
let name: String
let age: Int
let birthDate: Date
}
let person = Person(name: "Alice", age: 30, birthDate: Date())
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
encoder.dateEncodingStrategy = .iso8601
do {
let jsonData = try encoder.encode(person)
print(String(data: jsonData, encoding: .utf8)!)
} catch {
print("Encoding failed: \(error)")
}
Note: The
encode(_:) method throws an error if encoding fails, so always wrap it in a do-catch block.
Key Points
JSONEncoderis used to convert Swift objects into JSON data.- It supports customizing output formatting, date encoding, and data encoding strategies.
- Encoding can fail due to invalid data or unsupported types, so error handling is crucial.
3. Using JSONDecoder to Decode
| Method | Description | Example |
|---|---|---|
| decode(_:from:) | Decodes a JSON object into a Swift type | |
| decode(_:from:) | Decodes a JSON array into an array of Swift types | |
Example: Decoding a JSON Object
struct Person: Codable {
let name: String
let age: Int
}
let json = "{\\"name\\": \\"John\\", \\"age\\": 30}".data(using: .utf8)!
let person = try JSONDecoder().decode(Person.self, from: json)
print(person.name) // John
Example: Decoding a JSON Array
let jsonArray = "[{\\"name\\": \\"John\\", \\"age\\": 30}, {\\"name\\": \\"Jane\\", \\"age\\": 25}]".data(using: .utf8)!
let people = try JSONDecoder().decode([Person].self, from: jsonArray)
print(people.count) // 2
4. Customizing Coding Keys
| Property | Value | Description |
|---|---|---|
| CodingKey | String |
Used to define the keys for encoding and decoding values |
| Key | String |
Represents the key used in a dictionary or JSON object |
Example: Custom Coding Keys
struct User: Codable {
let name: String
let age: Int
let email: String
enum CodingKeys: String, CodingKey {
case name = "full_name"
case age = "user_age"
case email = "email_address"
}
}
Note: When using custom
CodingKeys, ensure that all properties are mapped to a corresponding key in the enum.5. Using Custom Encoding Logic
| Method | Description | Example |
|---|---|---|
| encode(to:) | Custom encoding logic for a type | |
Example: Custom Encoding with Nested Objects
struct Address: Codable {
let street: String
let city: String
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(street, forKey: .street)
try container.encode(city, forKey: .city)
}
}
struct User: Codable {
let name: String
let address: Address
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(address, forKey: .address)
}
}
Note: When implementing custom encoding logic, ensure all nested types also conform to
Codable.6. Using Custom Decoding Logic
| Method | Description | Example |
|---|---|---|
| init(from: Decoder) | Custom decoding logic using a custom initializer | |
Example: Custom Decoding with Conditional Logic
struct Product: Codable {
let id: Int
let name: String
let price: Double
enum CodingKeys: String, CodingKey {
case id, name, price
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
price = try container.decode(Double.self, forKey: .price)
}
}
Note: When implementing custom decoding logic, ensure all required fields are handled properly to avoid runtime errors.
7. Handling Nested JSON
| Property | Type | Description | Example |
|---|---|---|---|
| nestedObject | MyNestedType |
Property containing a nested object | |
| nestedArray | [String] |
Property containing an array of nested objects | |
Example: Decoding Nested JSON
struct User: Codable {
let name: String
let age: Int
let address: Address
}
struct Address: Codable {
let street: String
let city: String
}
let jsonData = """
{
"name": "John",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}
""".data(using: .utf8)!
do {
let user = try JSONDecoder().decode(User.self, from: jsonData)
print(user.name) // John
} catch {
print("Decoding failed: \(error)")
}
8. Working with Dates
| Property | Type | Notes |
|---|---|---|
| Date | Date |
Standard Swift type for representing dates and times |
| ISO8601DateFormatter | ISO8601DateFormatter |
Formatter for ISO 8601 date strings (e.g., "2025-04-05T14:30:00Z") |
| DateFormatter | DateFormatter |
Customizable date formatter for various formats |
Example: Encoding and decoding a Date
import Foundation
struct Event: Codable {
let name: String
let date: Date
}
let event = Event(name: "Swift Conference", date: Date())
// Encoding to JSON
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
let jsonData = try encoder.encode(event)
// Decoding from JSON
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let decodedEvent = try decoder.decode(Event.self, from: jsonData)
Note: By default,
Date is encoded as a double (Unix timestamp). Use .iso8601 for human-readable date strings.9. Handling Missing or Optional Fields
| Property | Type | Description | Default |
|---|---|---|---|
| CodingKey | String |
Key used to encode/decode values | N/A |
| Optional | Optional<T> |
Wraps a type to allow missing values | nil |
Example: Optional Field
struct User: Codable {
let name: String
let age: Int?
}
let json = "{"name": "John"}".data(using: .utf8)!
let user = try! JSONDecoder().decode(User.self, from: json)
print(user.age) // Optional<Int>(nil)
Example: Default Value for Missing Field
struct User: Codable {
let name: String
let age: Int = 0
}
let json = "{"name": "John"}".data(using: .utf8)!
let user = try! JSONDecoder().decode(User.self, from: json)
print(user.age) // 0
Note: When a field is missing in the JSON, Swift will use the default value if one is provided. Otherwise, it will be
nil for optional types.
10. Using PropertyListEncoder and PropertyListDecoder
| Encoder/Decoder | Format | Description |
|---|---|---|
| PropertyListEncoder | plist |
Encodes Swift objects into Property List format |
| PropertyListDecoder | plist |
Decodes Property List data into Swift objects |
Example: Encoding to Property List
import Foundation
struct Person: Codable {
let name: String
let age: Int
}
let person = Person(name: "Alice", age: 30)
let encoder = PropertyListEncoder()
encoder.outputFormat = .xml
do {
let data = try encoder.encode(person)
print(String(data: data, encoding: .utf8)!)
} catch {
print("Encoding error: " + error.localizedDescription)
}
Example: Decoding from Property List
import Foundation
struct Person: Codable {
let name: String
let age: Int
}
let plistData = <#Property List Data#>
let decoder = PropertyListDecoder()
decoder.format = .xml
do {
let person = try decoder.decode(Person.self, from: plistData)
print("Name: " + person.name)
} catch {
print("Decoding error: " + error.localizedDescription)
}
Note: PropertyListEncoder and PropertyListDecoder support XML and binary formats. The default format is
.xml.