swiftdartflutterjsoniosmobile

JSON Parsing in Swift and Dart (Flutter): Codable Structs and fromJson Classes

·7 min read

JSON Parsing in Mobile Apps

Mobile apps are API-heavy. Every screen loads data from a backend and every form submission sends data back. In Swift (iOS/macOS) and Dart (Flutter/Android), this means writing model classes that map JSON responses to native objects.

Swift — Codable Protocol

Swift uses the Codable protocol (combining Encodable and Decodable) for JSON serialization. The standard library's JSONDecoder handles all the conversion.

Given this API response:

json
{
  "user_id": 42,
  "display_name": "Ravi Mehta",
  "is_premium": true,
  "created_at": "2024-01-15T10:30:00Z"
}

Write this Swift struct:

swift
import Foundation

struct UserProfile: Codable {
    let userId: Int
    let displayName: String
    let isPremium: Bool
    let createdAt: String

    enum CodingKeys: String, CodingKey {
        case userId = "user_id"
        case displayName = "display_name"
        case isPremium = "is_premium"
        case createdAt = "created_at"
    }
}

CodingKeys maps JSON snake_case keys to Swift camelCase properties. The enum only needs cases where the names differ.

Decode from URLSession:

swift
let (data, _) = try await URLSession.shared.data(from: url)
let profile = try JSONDecoder().decode(UserProfile.self, from: data)
print(profile.displayName) // Ravi Mehta

Or use keyDecodingStrategy to skip the CodingKeys enum entirely:

swift
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let profile = try decoder.decode(UserProfile.self, from: data)

When to use each approach: - CodingKeys: precise control, rename individual fields, skip optional fields - convertFromSnakeCase: simple bulk rename, less code, but all or nothing

Swift Optional Fields

If an API field might be absent, make it optional:

swift
struct UserProfile: Codable {
    let userId: Int
    let displayName: String
    let avatarUrl: String?  // Optional — won't throw if missing
}

Dart — fromJson / toJson

Dart does not have a built-in serialization protocol. Instead, you write factory constructors and methods manually (or use code generation with json_serializable).

dart
class UserProfile {
  final int userId;
  final String displayName;
  final bool isPremium;

  UserProfile({
    required this.userId,
    required this.displayName,
    required this.isPremium,
  });

  factory UserProfile.fromJson(Map<String, dynamic> json) => UserProfile(
    userId: json['user_id'] as int,
    displayName: json['display_name'] as String,
    isPremium: json['is_premium'] as bool,
  );

  Map<String, dynamic> toJson() => {
    'user_id': userId,
    'display_name': displayName,
    'is_premium': isPremium,
  };
}

Parse an HTTP response in Flutter:

dart
import 'dart:convert';
import 'package:http/http.dart' as http;

final response = await http.get(Uri.parse('https://api.example.com/profile'));
final profile = UserProfile.fromJson(jsonDecode(response.body));
print(profile.displayName);

Dart Nested Objects

For nested objects, call fromJson recursively:

dart
factory UserProfile.fromJson(Map<String, dynamic> json) => UserProfile(
  userId: json['user_id'],
  displayName: json['display_name'],
  address: Address.fromJson(json['address']),
);

Null Safety in Dart

For optional fields, use nullable types:

dart
final String? avatarUrl;  // nullable
avatarUrl: json['avatar_url'] as String?,

Generate Boilerplate Automatically

Use JSONKit's tools to generate these classes from any JSON response: - Swift Codable structs: /json-to-swift - Dart fromJson/toJson classes: /json-to-dart

Paste your API response and get the complete class hierarchy — including all nested types and CodingKeys — in seconds.

Try JSON to Swift / Dart

Generate Swift Codable structs or Dart fromJson classes from any JSON response.