Working with JSON in Swift: Parsing and Serialization
JSON (JavaScript Object Notation) has become the de facto standard for data exchange between client and server in mobile and desktop applications. In the Swift ecosystem (iOS/macOS), working with JSON is implemented at the highest level — from simple JSONSerialization to the powerful Codable protocol. In this article, we will cover all the main ways of parsing and serializing JSON in Swift, provide practical examples, and show how to avoid common mistakes.
1. Basics of Working with JSON in Swift: JSONSerialization
Before Swift 4, the main tool for working with JSON was the JSONSerialization class, which is part of Foundation. It allows you to convert JSON data into Swift objects (dictionaries, arrays, strings, numbers) and back. This method remains relevant if you need full control over the process or are working with legacy code.
Parsing JSON with JSONSerialization
Suppose we received a JSON string from the server:
let jsonString = """{ "name": "Ivan", "age": 30, "isPremium": true, "hobbies": ["reading", "running", "programming"]}"""
guard let jsonData = jsonString.data(using: .utf8) else { fatalError("Error encoding string to Data")}
do { if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] { let name = jsonObject["name"] as? String ?? "" let age = jsonObject["age"] as? Int ?? 0 let isPremium = jsonObject["isPremium"] as? Bool ?? false let hobbies = jsonObject["hobbies"] as? [String] ?? [] print("Name: \\(name), Age: \\(age), Premium: \\(isPremium), Hobbies: \\(hobbies)") }} catch { print("JSON parsing error: \\(error.localizedDescription)")}The disadvantage of this approach is the need to manually cast types and handle optional values. The code becomes cumbersome and error-prone.
Serialization to JSON with JSONSerialization
The reverse process — converting a Swift dictionary into JSON data:
let personDict: [String: Any] = [ "name": "Maria", "age": 25, "isPremium": false, "hobbies": ["drawing", "yoga"]]
do { let jsonData = try JSONSerialization.data(withJSONObject: personDict, options: .prettyPrinted) if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) }} catch { print("Serialization error: \\(error.localizedDescription)")}The .prettyPrinted option makes the output human-readable. For production, [] (no formatting) is typically used to save bandwidth.
2. Modern Approach: Codable Protocol and JSONDecoder
Starting with Swift 4, working with JSON has become much easier thanks to the Codable protocol (which combines Encodable and Decodable). You simply describe the data structure, and the compiler automatically generates the code for encoding/decoding.
Parsing JSON with JSONDecoder
Let's define a model for our JSON:
struct Person: Codable { let name: String let age: Int let isPremium: Bool let hobbies: [String]}
let jsonString = """{ "name": "Ivan", "age": 30, "isPremium": true, "hobbies": ["reading", "running", "programming"]}"""
guard let jsonData = jsonString.data(using: .utf8) else { fatalError("Error encoding string")}
do { let person = try JSONDecoder().decode(Person.self, from: jsonData) print("Name: \\(person.name), Age: \\(person.age), Premium: \\(person.isPremium), Hobbies: \\(person.hobbies)")} catch { print("Decoding error: \\(error.localizedDescription)")}As you can see, the code has become much cleaner. All type casting and checks