Swift
JSON
Serialization
Swift Programming
Data Conversion

How to serialize or convert Swift objects to JSON?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Converting Swift objects to JSON is a standard step for API requests, local caching, and event logging. The modern and safe approach is to model your data with Codable and encode with JSONEncoder instead of building JSON strings manually. This keeps type checks at compile time and makes failures easier to debug.

Start with Codable Models

The simplest path is to define your object as Codable, create a JSONEncoder, and encode to Data. You can then convert that data to a UTF-8 string for inspection or send it through URLSession.

swift
1import Foundation
2
3struct UserProfile: Codable {
4    let id: Int
5    let name: String
6    let isPremium: Bool
7}
8
9let profile = UserProfile(id: 42, name: "Ari", isPremium: true)
10
11let encoder = JSONEncoder()
12encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
13
14let jsonData = try encoder.encode(profile)
15let jsonString = String(data: jsonData, encoding: .utf8) ?? ""
16print(jsonString)

This produces predictable output and avoids manual quoting mistakes. When objects grow over time, Codable keeps field mapping readable and testable.

Customize Keys, Dates, and Optional Fields

Real APIs often require snake case keys, ISO-8601 timestamps, and omission of null values. JSONEncoder supports these patterns directly.

swift
1import Foundation
2
3struct SessionPayload: Codable {
4    let userId: Int
5    let sessionToken: String
6    let expiresAt: Date
7    let deviceName: String?
8}
9
10let formatter = ISO8601DateFormatter()
11let expires = formatter.date(from: "2026-03-04T12:00:00Z")!
12
13let payload = SessionPayload(
14    userId: 99,
15    sessionToken: "abc123",
16    expiresAt: expires,
17    deviceName: nil
18)
19
20let encoder = JSONEncoder()
21encoder.keyEncodingStrategy = .convertToSnakeCase
22encoder.dateEncodingStrategy = .iso8601
23encoder.outputFormatting = [.sortedKeys]
24
25let data = try encoder.encode(payload)
26print(String(data: data, encoding: .utf8)!)

With these settings, userId becomes user_id, dates use a standard timestamp format, and optional values are encoded according to model behavior. If an API has strict rules, this is far safer than post processing a JSON string.

Build HTTP Requests with Encoded JSON

A common endpoint workflow is encode your model, attach it to an HTTP request body, and set headers. Keep request assembly close to encoding so errors are easy to trace.

swift
1import Foundation
2
3struct CreateOrder: Codable {
4    let orderId: String
5    let amountCents: Int
6}
7
8func buildRequest() throws -> URLRequest {
9    let body = CreateOrder(orderId: "ord-1001", amountCents: 2599)
10
11    var request = URLRequest(url: URL(string: "https://api.example.com/orders")!)
12    request.httpMethod = "POST"
13    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
14
15    let encoder = JSONEncoder()
16    request.httpBody = try encoder.encode(body)
17    return request
18}
19
20let request = try buildRequest()
21print("method:", request.httpMethod ?? "")
22print("content type:", request.value(forHTTPHeaderField: "Content-Type") ?? "")
23print("body bytes:", request.httpBody?.count ?? 0)

For production code, consider centralizing encoder configuration in one factory method. That ensures all endpoints share key and date rules.

Add Error Handling and Simple Tests

Encoding can fail when models include unsupported values or invalid custom logic. Catch errors at the boundary and return actionable context.

swift
1import Foundation
2
3struct ValueBox: Codable {
4    let value: Double
5}
6
7func encodeValue(_ number: Double) {
8    do {
9        let data = try JSONEncoder().encode(ValueBox(value: number))
10        print("encoded bytes:", data.count)
11    } catch {
12        print("encoding failed:", error.localizedDescription)
13    }
14}
15
16encodeValue(12.5)

Unit tests should assert both success and formatting. For example, test that keys are converted to snake case when expected, and that date fields match the contract used by the backend.

Common Pitfalls

  • Building JSON by string concatenation. This is fragile for quoting, escaping, and optional values.
  • Forgetting consistent encoder configuration across endpoints. Create one shared encoder policy.
  • Ignoring date formatting requirements. Agree on one date strategy with backend teams.
  • Debugging from raw bytes only. Convert to UTF-8 string in logs for local inspection.
  • Skipping error paths in tests. Add at least one failure test for custom encoding logic.

Summary

  • Use Codable and JSONEncoder as the default Swift JSON workflow.
  • Configure key strategy and date strategy explicitly for API compatibility.
  • Attach encoded data directly to URLRequest for clean request construction.
  • Centralize encoding defaults to prevent contract drift.
  • Add tests for both happy path and failure behavior.

Course illustration
Course illustration

All Rights Reserved.