Learn/Language Integrations

JSON in Mobile Apps — React Native, Swift & Kotlin

Every mobile app talks to APIs that return JSON. This guide covers parsing, serialization, and offline caching across the three major mobile platforms — with type-safe patterns that prevent crashes from unexpected API responses.

JSON Parsing Across Platforms

Mobile JSON Pipeline
FeatureSwift (iOS)Kotlin (Android)React Native
ParserJSONDecoder (Codable)kotlinx.serializationJSON.parse / Zod
Type safetyCompile-time (Codable)Compile-time (@Serializable)Runtime (Zod/io-ts)
Key mappingCodingKeys enum@SerialName annotationManual or transform
Date handling.iso8601 strategykotlinx-datetimenew Date(string)
Null safetyOptional (String?)Nullable (String?)Optional chaining
Offline cacheCore Data / FileManagerRoom / DataStoreAsyncStorage / MMKV

1. Swift — Codable (iOS / macOS)

Define a Codable modelswift
1struct User: Codable {
2 let id: Int
3 let name: String
4 let email: String
5 let bio: String? // nullable field
6 let createdAt: Date
7 let settings: UserSettings
8
9 struct UserSettings: Codable {
10 let theme: String
11 let notifications: Bool
12 }
13
14 // Map JSON keys to Swift properties
15 enum CodingKeys: String, CodingKey {
16 case id, name, email, bio, settings
17 case createdAt = "created_at" // snake_case → camelCase
18 }
19}
Fetch and parse JSONswift
1func fetchUsers() async throws -> [User] {
2 let url = URL(string: "https://api.example.com/users")!
3 let (data, response) = try await URLSession.shared.data(from: url)
4
5 guard let httpResponse = response as? HTTPURLResponse,
6 httpResponse.statusCode == 200 else {
7 throw APIError.invalidResponse
8 }
9
10 let decoder = JSONDecoder()
11 decoder.dateDecodingStrategy = .iso8601
12 decoder.keyDecodingStrategy = .convertFromSnakeCase
13
14 return try decoder.decode([User].self, from: data)
15}
16
17// Usage in SwiftUI
18struct UserListView: View {
19 @State private var users: [User] = []
20
21 var body: some View {
22 List(users, id: \.id) { user in
23 VStack(alignment: .leading) {
24 Text(user.name).font(.headline)
25 Text(user.email).font(.caption)
26 }
27 }
28 .task {
29 users = (try? await fetchUsers()) ?? []
30 }
31 }
32}

Tip

Use keyDecodingStrategy: .convertFromSnakeCase to automatically convert snake_case JSON keys to camelCase Swift properties, eliminating the need for manual CodingKeys in most cases.

2. Kotlin — kotlinx.serialization (Android)

Define a serializable modelkotlin
1import kotlinx.serialization.Serializable
2import kotlinx.serialization.SerialName
3import kotlinx.serialization.json.Json
4
5@Serializable
6data class User(
7 val id: Int,
8 val name: String,
9 val email: String,
10 val bio: String? = null, // nullable with default
11 @SerialName("created_at")
12 val createdAt: String, // ISO string
13 val settings: UserSettings = UserSettings()
14)
15
16@Serializable
17data class UserSettings(
18 val theme: String = "light",
19 val notifications: Boolean = true
20)
21
22// Configure parser
23val json = Json {
24 ignoreUnknownKeys = true // Don't crash on extra fields
25 isLenient = true // Accept unquoted values
26 coerceInputValues = true // Coerce nulls to defaults
27 encodeDefaults = false // Skip default values in output
28}
Parse JSON with Ktor clientkotlin
1import io.ktor.client.*
2import io.ktor.client.call.*
3import io.ktor.client.request.*
4import kotlinx.serialization.json.Json
5
6suspend fun fetchUsers(): List<User> {
7 val client = HttpClient()
8 val response: String = client.get("https://api.example.com/users").body()
9 return json.decodeFromString<List<User>>(response)
10}
11
12// Serialize to JSON
13val user = User(id = 1, name = "Alice", email = "[email protected]", createdAt = "2026-04-02T14:30:00Z")
14val jsonString = json.encodeToString(User.serializer(), user)

3. React Native — fetch + Zod

Type-safe API fetch in React Nativetypescript
1import { z } from 'zod';
2
3const UserSchema = z.object({
4 id: z.number(),
5 name: z.string(),
6 email: z.string().email(),
7 bio: z.string().nullable(),
8 created_at: z.string().datetime(),
9 settings: z.object({
10 theme: z.enum(['light', 'dark']),
11 notifications: z.boolean(),
12 }),
13});
14
15type User = z.infer<typeof UserSchema>;
16
17async function fetchUsers(): Promise<User[]> {
18 const response = await fetch('https://api.example.com/users');
19 if (!response.ok) throw new Error(`HTTP ${response.status}`);
20
21 const data = await response.json();
22 return z.array(UserSchema).parse(data); // Validates at runtime
23}

Common Mobile JSON Pitfalls

1. Crashing on Unexpected null

1// API sometimes returns null for "name"
2// WRONG — will crash if null
3struct User: Codable {
4 let name: String // Fatal error if JSON has null
5}
6
7// CORRECT — use Optional
8struct User: Codable {
9 let name: String? // Gracefully handles null
10}

2. Ignoring Unknown Keys

1// API adds a new field "avatar_url" — your app crashes
2// WRONG — strict parsing
3val json = Json { }
4
5// CORRECT — ignore unknown keys
6val json = Json { ignoreUnknownKeys = true }

3. Not Handling Empty Responses

1// React Native: 204 No Content returns empty body
2const response = await fetch('/api/resource', { method: 'DELETE' });
3// WRONG — crashes on empty body
4const data = await response.json();
5
6// CORRECT — check status first
7if (response.status === 204) {
8 return null;
9}
10const data = await response.json();

Try It — Validate a Mobile API Response

Try It Yourself

A typical user JSON response consumed by mobile apps

Frequently Asked Questions

What is Swift Codable?
Codable is a Swift protocol that combines Encodable and Decodable. When your struct conforms to Codable, Swift can automatically convert between JSON and your type using JSONDecoder and JSONEncoder. It handles key mapping, nested objects, and optional fields with minimal boilerplate.
Should I use Gson, Moshi, or kotlinx.serialization in Android?
kotlinx.serialization is the modern choice — it is maintained by JetBrains, supports multiplatform, and works at compile-time (no reflection). Moshi is a solid alternative with Kotlin-first design. Gson is legacy and less Kotlin-friendly. For new projects, use kotlinx.serialization.
How do I handle date formats in mobile JSON?
Use ISO 8601 strings ("2026-04-02T14:30:00Z") in your JSON API. In Swift, configure JSONDecoder with dateDecodingStrategy: .iso8601. In Kotlin, parse with kotlinx-datetime or java.time.Instant. Avoid Unix timestamps unless the API requires them.
How do I cache JSON for offline use?
In Swift, save to UserDefaults (small), FileManager (medium), or Core Data (complex). In Kotlin/Android, use DataStore (key-value), Room (relational), or file storage. In React Native, use AsyncStorage or MMKV. Always serialize to JSON strings before storing.
How do I handle null vs missing fields in mobile JSON?
In Swift, use Optional properties (let bio: String?). Missing or null JSON fields map to nil. In Kotlin, use nullable types (val bio: String?). In React Native/TypeScript, use optional types and default values. Always design your models to handle both null and absent fields gracefully.