Codable Protocol
What is Codable?
Codable is a type alias for Encodable and Decodable protocols combined. When a type conforms to Codable, Swift can automatically convert it to and from JSON (or other formats like plist).
Basic Conformance
struct User: Codable {
let id: Int
let name: String
let email: String
let isActive: Bool
}
With just this declaration, Swift generates the encoding and decoding logic automatically.
Decoding JSON
Convert JSON data into Swift objects:
let json = """
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"isActive": true
}
"""
let data = Data(json.utf8)
let user = try JSONDecoder().decode(User.self, from: data)
print(user.name) // Alice
Encoding to JSON
Convert Swift objects to JSON data:
let user = User(id: 1, name: "Alice", email: "alice@example.com", isActive: true)
let data = try JSONEncoder().encode(user)
let jsonString = String(data: data, encoding: .utf8)
Arrays of Objects
Decode arrays of objects:
let jsonArray = """
[
{"id": 1, "name": "Alice", "email": "a@b.com", "isActive": true},
{"id": 2, "name": "Bob", "email": "b@c.com", "isActive": false}
]
"""
let users = try JSONDecoder().decode([User].self, from: Data(jsonArray.utf8))
Optional Properties
Handle missing JSON keys with optionals:
struct Product: Codable {
let id: Int
let name: String
let description: String?
let discount: Double?
}
If description or discount is missing from the JSON, they become nil in Swift.
Nested Objects
Handle nested JSON structures:
struct Address: Codable {
let street: String
let city: String
let zipCode: String
}
struct Company: Codable {
let name: String
let address: Address
let employeeCount: Int
}
JSON like {"name": "Acme", "address": {"street": "123 Main", "city": "NYC", "zipCode": "10001"}, "employeeCount": 50} decodes directly.
Custom Keys
Why Custom Keys?
JSON keys often use snake_case or different naming conventions than Swift. CodingKeys lets you map between JSON key names and Swift property names.
Basic CodingKeys
struct UserProfile: Codable {
let id: Int
let firstName: String
let lastName: String
let emailAddress: String
let createdAt: Date
enum CodingKeys: String, CodingKey {
case id
case firstName = "first_name"
case lastName = "last_name"
case emailAddress = "email_address"
case createdAt = "created_at"
}
}
Using keyDecodingStrategy
Instead of individual CodingKeys, set a strategy on the decoder:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let user = try decoder.decode(UserProfile.self, from: data)
This automatically converts first_name to firstName.
keyEncodingStrategy
When encoding, convert Swift camelCase to snake_case:
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let data = try encoder.encode(user)
Custom Conversion Keys
For complex transformations, use a custom key strategy:
enum CustomKeyStrategy: JSONDecoder.KeyDecodingStrategy {
static func convert(_ key: CodingKey) -> CodingKey {
let lowered = key.stringValue.lowercased()
return lowered as CodingKey
}
}
Container Types
CodingKeys works with nested containers:
struct APIResponse: Codable {
let status: String
let data: UserData
let pagination: PaginationInfo?
enum CodingKeys: String, CodingKey {
case status, data, pagination
}
}
Date Strategies
Date Decoding Strategies
JSON dates come in many formats. JSONDecoder provides built-in strategies for common formats.
Default Strategy (Unix Timestamp)
By default, dates are decoded as Unix timestamps (seconds since 1970):
let decoder = JSONDecoder()
// Uses .secondsSince1970 by default
ISO 8601 Strategy
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
Handles dates like 2024-01-15T10:30:00Z.
Formatted Date Strategy
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
decoder.dateDecodingStrategy = .formatted(formatter)
Custom Strategy
For complex date formats:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy HH:mm"
formatter.locale = Locale(identifier: "en_US_POSIX")
guard let date = formatter.date(from: dateString) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date format")
}
return date
}
Date Encoding Strategies
Match your encoding strategy to your API's expected format:
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
// Or custom
encoder.dateEncodingStrategy = .formatted(dateFormatter)
// Or milliseconds
encoder.dateEncodingStrategy = .millisecondsSince1970
Handling Multiple Formats
If your API uses different date formats in different endpoints:
struct APIParser {
static let iso8601Decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return decoder
}()
static let timestampDecoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
return decoder
}()
}
Quiz
1. What is Codable?
2. How do you map snake_case JSON keys to camelCase Swift properties?
3. What date format does JSONDecoder use by default?
4. How do you handle missing JSON keys in a Codable struct?
Flashcards
Question
What is Codable?
Click to reveal answer
Answer
A type alias for Encodable and Decodable, enabling automatic JSON serialization/deserialization.
Question
How do you use CodingKeys?
Click to reveal answer
Answer
Define an enum conforming to CodingKey with String raw values to map JSON keys to Swift properties.
Question
What is the simplest way to handle snake_case JSON?
Click to reveal answer
Answer
Set decoder.keyDecodingStrategy = .convertFromSnakeCase on JSONDecoder.
Question
How do you decode optional values from JSON?
Click to reveal answer
Answer
Declare the Swift property as Optional (String?, Int?, etc.). Missing keys decode as nil.
Revision Notes
Key Takeaways
- 1. Codable provides automatic JSON encoding/decoding with minimal boilerplate
- 2. CodingKeys enum maps between different key naming conventions
- 3. keyDecodingStrategy handles snake_case conversion automatically
- 4. Optional properties gracefully handle missing JSON keys
- 5. Configure date strategies to match your API's date format
Interview Tips
- • Explain how Codable automatically generates encoding/decoding
- • Know when to use CodingKeys vs keyDecodingStrategy
- • Describe how to handle different date formats from APIs
- • Discuss strategies for handling nested JSON and optional fields
Cheat Sheet
Codable = Encodable + Decodable.
JSONDecoder().decode(Type.self, from: data) - decode.
JSONEncoder().encode(instance) - encode.
CodingKeys enum maps JSON keys to Swift properties.
keyDecodingStrategy = .convertFromSnakeCase - auto snake_case.
dateDecodingStrategy: .iso8601, .secondsSince1970, .formatted(), .custom.