Swift
Codable
Dictionary
Encoding
Swift Programming

How can I use Swift's Codable to encode into a dictionary?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Swift's Codable protocol is designed for encoding to and decoding from external formats like JSON. There is no built-in DictionaryEncoder, but you can convert a Codable object to a [String: Any] dictionary by encoding it to JSON data first, then deserializing that data with JSONSerialization. For a cleaner approach, you can write a custom DictionaryEncoder that avoids the intermediate JSON step.

Method 1: JSON Round-Trip (Simple)

Encode to JSON, then deserialize to a dictionary:

swift
1import Foundation
2
3struct User: Codable {
4    let name: String
5    let age: Int
6    let email: String
7}
8
9let user = User(name: "Alice", age: 30, email: "[email protected]")
10
11// Encode to JSON Data
12let jsonData = try JSONEncoder().encode(user)
13
14// Deserialize to dictionary
15let dict = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any]
16print(dict!)
17// ["name": "Alice", "age": 30, "email": "[email protected]"]

This works for any Codable type. The downside is two serialization steps (encode to JSON bytes, then parse those bytes back to objects).

Method 2: Extension on Encodable (Reusable)

swift
1extension Encodable {
2    func asDictionary() throws -> [String: Any] {
3        let data = try JSONEncoder().encode(self)
4        guard let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
5            throw EncodingError.invalidValue(
6                self,
7                EncodingError.Context(
8                    codingPath: [],
9                    debugDescription: "Top-level value is not a dictionary"
10                )
11            )
12        }
13        return dict
14    }
15
16    func asArray() throws -> [[String: Any]] {
17        let data = try JSONEncoder().encode(self)
18        guard let array = try JSONSerialization.jsonObject(with: data) as? [[String: Any]] else {
19            throw EncodingError.invalidValue(
20                self,
21                EncodingError.Context(
22                    codingPath: [],
23                    debugDescription: "Top-level value is not an array"
24                )
25            )
26        }
27        return array
28    }
29}
30
31// Usage
32let user = User(name: "Alice", age: 30, email: "[email protected]")
33let dict = try user.asDictionary()
34print(dict["name"]!)  // Alice

Method 3: Custom DictionaryEncoder

For a direct conversion without JSON intermediary:

swift
1class DictionaryEncoder {
2    func encode<T: Encodable>(_ value: T) throws -> [String: Any] {
3        let data = try JSONEncoder().encode(value)
4        let jsonObject = try JSONSerialization.jsonObject(with: data)
5
6        guard let dict = jsonObject as? [String: Any] else {
7            throw EncodingError.invalidValue(
8                value,
9                EncodingError.Context(
10                    codingPath: [],
11                    debugDescription: "Expected dictionary, got \(type(of: jsonObject))"
12                )
13            )
14        }
15        return dict
16    }
17}
18
19class DictionaryDecoder {
20    func decode<T: Decodable>(_ type: T.Type, from dict: [String: Any]) throws -> T {
21        let data = try JSONSerialization.data(withJSONObject: dict)
22        return try JSONDecoder().decode(type, from: data)
23    }
24}
25
26// Usage
27let encoder = DictionaryEncoder()
28let decoder = DictionaryDecoder()
29
30let dict = try encoder.encode(user)
31let restored = try decoder.decode(User.self, from: dict)

Nested Objects

swift
1struct Address: Codable {
2    let street: String
3    let city: String
4    let zip: String
5}
6
7struct Employee: Codable {
8    let name: String
9    let department: String
10    let address: Address
11    let skills: [String]
12}
13
14let employee = Employee(
15    name: "Bob",
16    department: "Engineering",
17    address: Address(street: "123 Main St", city: "Seattle", zip: "98101"),
18    skills: ["Swift", "Kotlin", "Rust"]
19)
20
21let dict = try employee.asDictionary()
22print(dict)
23// ["name": "Bob", "department": "Engineering",
24//  "address": ["street": "123 Main St", "city": "Seattle", "zip": "98101"],
25//  "skills": ["Swift", "Kotlin", "Rust"]]
26
27// Access nested values
28let address = dict["address"] as? [String: Any]
29print(address?["city"]!)  // Seattle

Custom Coding Keys

swift
1struct APIRequest: Codable {
2    let userId: Int
3    let accessToken: String
4    let isActive: Bool
5
6    enum CodingKeys: String, CodingKey {
7        case userId = "user_id"
8        case accessToken = "access_token"
9        case isActive = "is_active"
10    }
11}
12
13let request = APIRequest(userId: 42, accessToken: "abc123", isActive: true)
14let dict = try request.asDictionary()
15print(dict)
16// ["user_id": 42, "access_token": "abc123", "is_active": true]

The dictionary keys follow the CodingKeys mapping, producing snake_case keys.

Date Encoding Strategies

swift
1struct Event: Codable {
2    let title: String
3    let date: Date
4}
5
6let event = Event(title: "Launch", date: Date())
7
8let encoder = JSONEncoder()
9encoder.dateEncodingStrategy = .iso8601
10
11let data = try encoder.encode(event)
12let dict = try JSONSerialization.jsonObject(with: data) as! [String: Any]
13print(dict["date"]!)  // "2025-03-02T10:15:32Z"

Using with Firestore / API Payloads

swift
1// Firebase Firestore expects [String: Any] dictionaries
2let user = User(name: "Alice", age: 30, email: "[email protected]")
3let dict = try user.asDictionary()
4
5// Firestore
6// db.collection("users").document("alice").setData(dict)
7
8// URLRequest body
9let jsonData = try JSONSerialization.data(withJSONObject: dict)
10var request = URLRequest(url: URL(string: "https://api.example.com/users")!)
11request.httpBody = jsonData
12request.httpMethod = "POST"
13request.setValue("application/json", forHTTPHeaderField: "Content-Type")

Dictionary Back to Codable

swift
1let dict: [String: Any] = [
2    "name": "Charlie",
3    "age": 25,
4    "email": "[email protected]"
5]
6
7let data = try JSONSerialization.data(withJSONObject: dict)
8let user = try JSONDecoder().decode(User.self, from: data)
9print(user.name)  // Charlie

Common Pitfalls

  • Date encoding defaults to Double: Without setting dateEncodingStrategy, JSONEncoder encodes Date as a TimeInterval (seconds since 1970). Set .iso8601 or .formatted for readable dates in your dictionary.
  • Any type is not Codable: You cannot encode [String: Any] with JSONEncoder because Any does not conform to Codable. Use JSONSerialization for untyped dictionaries.
  • Enums without raw values: Enums with associated values need custom Codable conformance. Without it, encoding fails at runtime.
  • Optional fields are omitted: By default, JSONEncoder omits nil optional properties. The resulting dictionary will not have keys for nil values. If you need explicit null, use custom encoding.
  • Float/Double precision: JSON serialization may introduce floating-point precision artifacts. 3.14 might become 3.1400000000000001 in the dictionary. Use Decimal for exact values.

Summary

  • Encode to JSON with JSONEncoder, then parse to [String: Any] with JSONSerialization
  • Create an Encodable extension with asDictionary() for reusable conversion
  • Custom CodingKeys control the dictionary key names (e.g., camelCase to snake_case)
  • Set dateEncodingStrategy on JSONEncoder to control how dates appear in the dictionary
  • To go from dictionary back to Codable, serialize with JSONSerialization then decode with JSONDecoder

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

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

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.