Swift Codable
Codable converts Swift values to and from formats such as JSON. Apps talk to servers in JSON, and Codable handles the translation with almost no code. Swift builds the conversion steps automatically when every property of your type is itself convertible.
Picture a translator between two people who speak different languages. Your app speaks Swift structs, and the internet speaks JSON text. Codable plays the translator in both directions.
Diagram: Two-Way Translation
JSON text Swift struct
{"id": 7, --- decode --> User(id: 7,
"name": "Ava"} name: "Ava")
{"id": 7, <-- encode --- User(id: 7,
"name": "Ava"} name: "Ava")Making a Type Codable
Add the Codable protocol to a struct. The protocol combines Encodable for writing and Decodable for reading.
import Foundation
struct User: Codable {
let id: Int
let name: String
let email: String?
}The optional email property accepts JSON that leaves the field out.
Decoding JSON
Turn JSON text into Data and ask JSONDecoder to build the struct.
let json = """
{"id": 7, "name": "Ava", "email": "ava@example.com"}
"""
let data = Data(json.utf8)
do {
let user = try JSONDecoder().decode(User.self, from: data)
print(user.name) // Output: Ava
} catch {
print("Decoding failed: \(error)")
}Decoding can fail. The call needs try inside a do-catch block.
Encoding to JSON
let user = User(id: 7, name: "Ava", email: "ava@example.com")
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
if let output = try? encoder.encode(user),
let text = String(data: output, encoding: .utf8) {
print(text)
}
// Output:
// {
// "email" : "ava@example.com",
// "id" : 7,
// "name" : "Ava"
// }Custom Key Names
Servers often use names like unit_price, which break Swift naming style. A nested CodingKeys enumeration maps the names.
struct Product: Codable {
let title: String
let unitPrice: Double
enum CodingKeys: String, CodingKey {
case title
case unitPrice = "unit_price"
}
}
let raw = Data(#"{"title": "Notebook", "unit_price": 3.5}"#.utf8)
let item = try JSONDecoder().decode(Product.self, from: raw)
print(item.unitPrice) // Output: 3.5A decoder setting offers a shortcut for the common case. Set keyDecodingStrategy to .convertFromSnakeCase and Swift maps every snake_case key to camelCase.
Nested Data and Arrays
Codable handles nested structs and arrays without extra code. Each nested type only needs to adopt Codable too.
struct Team: Codable {
let name: String
let members: [User]
}
let teamJSON = Data("""
{"name": "Blue", "members": [
{"id": 1, "name": "Asha"},
{"id": 2, "name": "Ben"}
]}
""".utf8)
let team = try JSONDecoder().decode(Team.self, from: teamJSON)
print(team.members.count) // Output: 2Understanding Decoding Errors
A wrong type or a missing field throws a DecodingError. The error message names the field, which helps with debugging.
let broken = Data(#"{"id": "seven", "name": "Ava"}"#.utf8)
do {
_ = try JSONDecoder().decode(User.self, from: broken)
} catch let DecodingError.typeMismatch(type, context) {
print("Expected \(type) at \(context.codingPath.map(\.stringValue))")
}
// Output: Expected Int at ["id"]Dates and Special Values
Servers send dates as text. A decoder converts that text into a Date value when you choose a date strategy. The setting .iso8601 reads the common international format, such as 2026-11-12T09:30:00Z.
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
decoder.keyDecodingStrategy = .convertFromSnakeCaseThese two lines remove most manual key mapping and date parsing from a typical networking layer.
Saving Data to Disk
Codable supports storage on the device. Encode a struct to Data and write the bytes to a file. Load the file later and decode the bytes to restore the struct. Apps use this pattern for settings, drafts, and offline caches.
Common Mistakes
A property type that does not match the JSON triggers a decoding error. A JSON number cannot fill a String property, and a JSON string cannot fill an Int property. Mark a property optional when the server may leave it out.
Property names must match the JSON keys exactly, including capital letters. A key named UserName needs a coding key that points to userName, unless a decoder strategy handles the conversion.
Real-World Use
Weather apps, news readers, and online stores all receive JSON from servers. Each response becomes a Swift struct through a single decode call. Developers work with typed values, and the compiler warns about mistakes before users see them.
Optional Fields and Default Values
Servers evolve. A new version of an API may add fields that old apps never read, and Codable ignores unknown fields without complaint. A field that the app expects but the server omits triggers an error unless the property is optional. Many teams mark rarely present fields as optional and supply a fallback value at the point of use.
Keep the Codable struct as a faithful mirror of the server response. Convert it into a friendlier model for the screen in a separate step. This habit isolates server changes in one place.
Key Takeaways
Codablecombines encoding and decoding in one protocol.JSONDecoderreads JSON andJSONEncoderwrites JSON.CodingKeysmaps JSON names to Swift property names.- Nested structs and arrays work automatically when each type is codable.
