Sitemap

Stop using Codable everywhere.

3 min readMay 13, 2026

--

The short answer: you’re probably doing it right. Here’s how to know for sure.
Swift doesn’t provide a single “JSON protocol.” It gives you three, each with a specific responsibility

Press enter or click to view image in full size
Press enter or click to view image in full size

Most networking code only needs Decodable

When your app fetches data and displays it, data flows one way — from the server into your models. The Decodable conformance is a signal to anyone reading this code: this model is read-only by design. You don’t need Codable here, just in case.

struct Article: Decodable {
let id: Int
let title: String
let publishedAt: Date
let author: Author
}

Use Codable when data moves in both directions

Two situations make Codable the right call:

1. You’re sending data back to a server

A request body going out as JSON, a form payload, a PUT with updated fields — anything where your Swift type needs to become JSON.

struct UpdateProfileRequest: Encodable {
let displayName: String
let bio: String
let avatarURL: URL?
}

var request = URLRequest(url: endpoint)
request.httpBody = try JSONEncoder().encode(
UpdateProfileRequest(displayName: "MG", bio: "iOS dev", avatarURL: nil)
)

Notice Encodable here, not Codable — because this request model will never be decoded from JSON. Same principle, different direction.

Get Mok5ha G’s stories in your inbox

Join Medium for free to get updates from this writer.

2. You’re persisting models locally

Saving user preferences, caching a response, storing settings in UserDefaults — any time you write a model to disk and read it back:

struct UserSettings: Codable {
var theme: String
var notificationsEnabled: Bool
var lastSyncDate: Date
}

// Save
let data = try JSONEncoder().encode(settings)
UserDefaults.standard.set(data, forKey: "userSettings")

// Load later
if let stored = UserDefaults.standard.data(forKey: "userSettings") {
let settings = try JSONDecoder().decode(UserSettings.self, from: stored)
}

Here Codable earns its place — you encode to save, decode to load.

Press enter or click to view image in full size

Don’t add Codable “just in case.” The compiler won’t complain, but your codebase quietly becomes harder to read — because now nothing tells you which models are read-only and which ones aren’t.

A pattern worth keeping

In larger apps, separating request models from response models entirely pays off fast:

// Comes from the server — Decodable only
struct ProductResponse: Decodable {
let id: Int
let name: String
let price: Double
let inStock: Bool
}

// Goes to the server — Encodable only
struct AddToCartRequest: Encodable {
let productId: Int
let quantity: Int
}

// Lives locally, needs both
struct CartItem: Codable {
let productId: Int
var quantity: Int
let addedAt: Date
}

The takeaway

Decodable isn’t the “basic” version. It’s the precise version — for the most common case in iOS networking. Using it deliberately is a sign of experience, not a gap in knowledge.

Use the protocol that matches the data's actual behaviour. Nothing more.

Have a follow-up question or a use case I didn’t cover? Drop it in the comments — it might become the next article.

--

--