How to Parse JSON in Swift Using Codable: The Complete Guide

Parse JSON in Swift using the Codable protocol with JSONDecoder. Swift 4 introduced Codable, which combines Decodable and Encodable into a single protocol. You describe your data model with structs that conform to Codable, and the Swift compiler generates the parsing logic automatically. This replaces the manual JSONSerialization approach that requires unsafe type casting and fragile dictionary key management.

What Is the Codable Protocol in Swift

Codable is a type alias for the Decodable and Encodable protocols. Decodable converts external representations such as JSON into Swift types. Encodable converts Swift types to external representations. Together they provide complete serialization support. The Swift compiler can synthesize Codable conformance automatically when all stored properties are also Codable. This eliminates boilerplate code for most data models.

How to Decode Simple JSON with Codable

Define a struct that conforms to Codable:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int
}

Create a JSON string and decode it:

let json = """
{
    "name": "Kiva Loan",
    "country": "Kenya",
    "use": "Farm Equipment",
    "amount": 500
}
"""

let decoder = JSONDecoder()
if let jsonData = json.data(using: .utf8) {
    do {
        let loan = try decoder.decode(Loan.self, from: jsonData)
        print(loan.name)
    } catch {
        print("Decoding error: (error)")
    }
}

JSONDecoder maps JSON keys to property names automatically. The property name amount matches the JSON key amount. The property name name matches the JSON key name. No manual mapping code is required.

How to Handle Custom Property Names with CodingKeys

API JSON keys often use snake_case while Swift uses camelCase. Or the JSON key differs entirely from the property name. Define a CodingKeys enum:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int
    
    enum CodingKeys: String, CodingKey {
        case name
        case country
        case use
        case amount = "loan_amount"
    }
}

The enum conforms to CodingKey with a String raw value. Cases that match JSON keys omit the string assignment. Cases that differ specify the JSON key as a string literal. The amount property now maps from the JSON key loan_amount.

How to Decode Nested JSON Objects

Real-world JSON nests related data inside objects. The Kiva API nests country information inside a location object. Handle this with nested containers:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int
    
    enum CodingKeys: String, CodingKey {
        case name
        case use
        case amount = "loan_amount"
        case location
    }
    
    enum LocationKeys: String, CodingKey {
        case country
    }
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        use = try values.decode(String.self, forKey: .use)
        amount = try values.decode(Int.self, forKey: .amount)
        
        let location = try values.nestedContainer(keyedBy: LocationKeys.self, forKey: .location)
        country = try location.decode(String.self, forKey: .country)
    }
}

The nestedContainer method drills into the location object and extracts the country value from within it. You need a custom init(from:) implementation when the JSON structure does not match the flat property layout.

How to Decode JSON Arrays

For JSON arrays at the top level:

let loans = try decoder.decode([Loan].self, from: jsonData)

When JSON wraps the array in a top-level container, create a wrapper struct:

struct LoanDataStore: Codable {
    var loans: [Loan]
}

let loanDataStore = try decoder.decode(LoanDataStore.self, from: data)
loans = loanDataStore.loans

This pattern is common when APIs wrap results in metadata objects containing pagination or status information.

How to Use JSONEncoder for Serialization

Codable works in both directions. Use JSONEncoder to serialize Swift objects for API requests:

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
if let jsonData = try? encoder.encode(loan) {
    let jsonString = String(data: jsonData, encoding: .utf8)
    print(jsonString ?? "")
}

JSONEncoder converts Swift structs and classes back to JSON data for upload requests or local caching.

How to Integrate Codable with URLSession

Fetch JSON from a remote API and decode it:

private let kivaLoanURL = "https://api.kivaws.org/v1/loans/newest.json"

func getLatestLoans() {
    guard let url = URL(string: kivaLoanURL) else { return }
    
    URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            print("Network error: (error)")
            return
        }
        guard let data = data else { return }
        
        let decoder = JSONDecoder()
        if let loanDataStore = try? decoder.decode(LoanDataStore.self, from: data) {
            DispatchQueue.main.async {
                self.loans = loanDataStore.loans
                self.updateSnapshot()
            }
        }
    }.resume()
}

Network responses arrive on a background queue. Use DispatchQueue.main.async for UI updates.

Best Practices for JSON Parsing in Swift

Always decode on a background thread. Network responses arrive on a background queue. Move decoding to a background queue and dispatch UI updates to the main queue. Use optional properties for nullable JSON fields. Declare properties as optional with var use: String? when a field might be missing at runtime. Leverage CodingKeys for API versioning. Add new properties with different CodingKeys when API contracts change. Keep existing properties working without breaking changes. Create separate model files for each data type. Models grow quickly and benefit from isolation. Test with malformed JSON in unit tests. The catch block should handle parsing failures gracefully and log diagnostic information. Prefer Codable over JSONSerialization for all new development. Codable provides type safety, compiler validation, and significantly less boilerplate code.

Summary

This tutorial covered the complete implementation process from setup to working code. You now have a functional implementation that you can extend and customize for your own projects. The same patterns apply to related features in the iOS ecosystem.

Frequently Asked Questions

What are the prerequisites for this tutorial? You need Xcode installed on a Mac and basic familiarity with Swift programming. No prior experience with the specific framework is required.

All articles