Swift
JSON
object conversion
Swift programming
JSON parsing

Simple and clean way to convert JSON string to Object in Swift

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In modern Swift, the cleanest way to turn a JSON string into an object is Codable with JSONDecoder. It is type-safe, concise, and easier to maintain than older dictionary-based parsing, especially once payloads become non-trivial.

Start With a Codable Model

The usual pattern is:

  1. define a Swift type that matches the JSON shape
  2. convert the JSON string to Data
  3. decode with JSONDecoder
swift
1import Foundation
2
3struct User: Codable {
4    let id: Int
5    let username: String
6    let isActive: Bool
7}
8
9let json = """
10{
11  "id": 42,
12  "username": "mark",
13  "isActive": true
14}
15"""
16
17do {
18    let data = Data(json.utf8)
19    let user = try JSONDecoder().decode(User.self, from: data)
20    print(user.username)
21} catch {
22    print("Decode failed: \(error)")
23}

For most Swift projects, this should be the default answer.

Why Codable Is Cleaner Than Dictionary Parsing

Older Swift code often used JSONSerialization and then manually cast dictionaries:

  • '[String: Any]'
  • nested type casts
  • lots of optional unwrapping

That works, but it becomes fragile quickly. Codable improves things because:

  • the expected structure is declared in one model
  • type mismatches fail clearly
  • nested objects stay manageable
  • the compiler helps you refactor safely

So the benefit is not just shorter code. It is also better structure and fewer runtime surprises.

Handling Snake Case Keys

Many APIs use snake case keys while Swift style prefers camel case. JSONDecoder can bridge that automatically:

swift
1import Foundation
2
3struct Profile: Codable {
4    let userId: Int
5    let displayName: String
6}
7
8let json = """
9{
10  "user_id": 7,
11  "display_name": "Ivy"
12}
13"""
14
15do {
16    let decoder = JSONDecoder()
17    decoder.keyDecodingStrategy = .convertFromSnakeCase
18
19    let profile = try decoder.decode(Profile.self, from: Data(json.utf8))
20    print(profile)
21} catch {
22    print(error)
23}

This avoids a lot of boilerplate when the key transformation is regular.

If the mapping is irregular, use a custom CodingKeys enum instead.

Decoding Nested JSON Objects

The same model-based approach works for nested payloads:

swift
1import Foundation
2
3struct Address: Codable {
4    let city: String
5    let country: String
6}
7
8struct Customer: Codable {
9    let name: String
10    let address: Address
11}
12
13let json = """
14{
15  "name": "Alice",
16  "address": {
17    "city": "Toronto",
18    "country": "Canada"
19  }
20}
21"""
22
23let customer = try JSONDecoder().decode(Customer.self, from: Data(json.utf8))
24print(customer.address.city)

Once the model reflects the JSON structure accurately, the decoding code stays simple even as the payload grows.

Handle Optional Fields Carefully

If a field is sometimes absent or null, model it as optional:

swift
1import Foundation
2
3struct Article: Codable {
4    let title: String
5    let subtitle: String?
6}

If you mark such a field as non-optional and the API omits it, decoding will fail. That is one of the most common Codable mistakes.

The model should reflect the API contract as it actually behaves, not only as you wish it behaved.

Reusable Decode Helper

If you decode many JSON strings with the same conventions, a small helper can keep the code tidy:

swift
1import Foundation
2
3enum DecodeError: Error {
4    case invalidUTF8
5}
6
7func decodeJSON<T: Decodable>(_ type: T.Type, from json: String) throws -> T {
8    guard let data = json.data(using: .utf8) else {
9        throw DecodeError.invalidUTF8
10    }
11
12    let decoder = JSONDecoder()
13    decoder.keyDecodingStrategy = .convertFromSnakeCase
14    return try decoder.decode(T.self, from: data)
15}
16
17let user: User = try decodeJSON(User.self, from: json)
18print(user)

This is clean as long as your APIs share the same decoding rules. If different endpoints need different date or key strategies, keep those decoders separate.

Common Pitfalls

The most common pitfall is jumping straight to [String: Any] parsing when the JSON structure is known. Codable is usually cleaner and safer.

Another mistake is forgetting to convert the JSON string into Data before decoding. JSONDecoder works with bytes, not raw String values.

A third issue is marking occasionally missing fields as non-optional, which causes avoidable decode failures.

Finally, developers often ignore the actual error details. DecodingError usually tells you which key or type mismatch caused the failure, and that information is extremely useful.

Summary

  • In Swift, Codable with JSONDecoder is the cleanest standard way to convert JSON strings into objects.
  • Define models that match the JSON structure and decode from Data.
  • Use keyDecodingStrategy when API keys use snake case.
  • Mark fields optional when the payload may omit them or send null.
  • Prefer model-based decoding over manual dictionary casting for maintainable code.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions