Swift Tutorial · Advanced Swift · Lesson 27 of 30

Swift Codable and JSON

Encode and decode JSON with Codable, custom keys, snake_case conversion and dates.

Create a free account to save this page, take notes and track your progress. Sign in

Codable

Conform a type to Codable and the compiler generates the code to convert it to and from JSON (or property lists). Nested Codable types and arrays work automatically.

Encoding and decoding
import Foundation

struct Item: Codable {
    let name: String
    let price: Double
}

struct Order: Codable {
    let id: Int
    let items: [Item]
    let note: String?
}

let order = Order(id: 42, items: [Item(name: "mug", price: 12.5)], note: nil)
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let data = try encoder.encode(order)
print(String(decoding: data, as: UTF8.self))

let decoded = try JSONDecoder().decode(Order.self, from: data)
print(decoded.items[0].name, decoded.note ?? "no note")

Output

{
  "id" : 42,
  "items" : [
    {
      "name" : "mug",
      "price" : 12.5
    }
  ]
}
mug no note

Run this on your computer — see “Get Started” for setup.

Key strategies and custom keys

keyDecodingStrategy = .convertFromSnakeCase maps created_at to createdAt. CodingKeys renames individual keys, and a missing optional key decodes as nil. Decoding errors say which key was wrong.

snake_case keys, custom keys and errors
import Foundation

struct User: Decodable {
    let id: Int
    let fullName: String
    let isAdmin: Bool
    let nickname: String?

    enum CodingKeys: String, CodingKey {
        case id, fullName, nickname
        case isAdmin = "admin"
    }
}

let json = Data("""
[{"id": 1, "full_name": "Ana Silva", "admin": true},
 {"id": 2, "full_name": "Ben Ode", "admin": false, "nickname": "benny"}]
""".utf8)

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let users = try decoder.decode([User].self, from: json)
for u in users { print(u.id, u.fullName, u.isAdmin, u.nickname ?? "-") }

do {
    _ = try decoder.decode(User.self, from: Data(#"{"id": "one", "full_name": "X", "admin": false}"#.utf8))
} catch DecodingError.typeMismatch(_, let context) {
    print("bad value for:", context.codingPath.map(\.stringValue))
}

Output

1 Ana Silva true -
2 Ben Ode false benny
bad value for: ["id"]

Run this on your computer — see “Get Started” for setup.

Test yourself

What does keyDecodingStrategy = .convertFromSnakeCase do?

  1. Lowercases all values
  2. Maps JSON keys like created_at to Swift properties like createdAt
  3. Sorts the keys
  4. Ignores unknown keys
Show answer

Answer: Maps JSON keys like created_at to Swift properties like createdAt

Was this page helpful?