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.