Skill

Generate Robust Swift Codable Models

Swift Codable model patterns for JSON parsing: automatic Codable conformance, CodingKeys for snake_case mapping, custom decoding, property wrappers, and error

Maintainer of this project? Claim this page to edit the listing.


79
Spark score
out of 100
Updated 7 months ago
Version 1.0.0
Models

Add to Favorites

Why it matters

Automate the creation of complex and maintainable Swift Codable models for efficient JSON parsing and serialization in your iOS and macOS applications.

Outcomes

What it gets done

01

Implement basic Codable structures with automatic JSON parsing.

02

Map snake_case API fields to camelCase Swift properties using CodingKeys.

03

Handle custom decoding logic, optional values, and default values.

04

Generate custom error handling and validation for decoding processes.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/vb-swift-codable-model | bash

Overview

Swift Codable Model Expert

Swift Codable model patterns for JSON parsing: automatic Codable conformance, CodingKeys for snake_case mapping, custom decoding, property wrappers, and error handling. Use this skill when implementing Codable models that map JSON with different naming conventions (snake_case to camelCase), require custom date parsing logic, handle optional fields with defaults, or decode arrays that may contain invalid items. Use it for nested model hierarchies or generic response wrappers.

What it does

This skill demonstrates Swift Codable model patterns for JSON parsing and serialization. It shows automatic JSON parsing with Codable conformance, CodingKeys enums for snake_case to camelCase conversion, custom init(from:) implementations for validation and transformation, property wrappers for default values, safe array decoding patterns, and custom error types.

When to use - and when NOT to

Use this skill when implementing Codable models that map JSON with different naming conventions (snake_case to camelCase), require custom date parsing logic, handle optional fields with defaults, or decode arrays that may contain invalid items. Use it for nested model hierarchies or generic response wrappers.

Do NOT use this for non-Swift platforms or data formats other than JSON. Avoid this if your JSON structure is simple and requires no custom logic - basic Codable conformance may suffice.

Inputs and outputs

You describe your JSON structure, naming conventions, and validation requirements. You receive Swift Codable model implementations with CodingKeys enums, custom decoders, property wrappers, and error types. For example:

struct Product: Codable {
    let productId: String
    let displayName: String
    let createdAt: Date
    let isAvailable: Bool
    
    enum CodingKeys: String, CodingKey {
        case productId = "product_id"
        case displayName = "display_name"
        case createdAt = "created_at"
        case isAvailable = "is_available"
    }
}

Integrations

Demonstrates JSONDecoder configuration with keyDecodingStrategy (.convertFromSnakeCase), dateDecodingStrategy (.iso8601), and dataDecodingStrategy (.base64). Includes XCTest extension patterns for testing Codable models.

Who it's for

iOS and macOS developers implementing Codable models for JSON APIs, particularly those working with snake_case naming conventions. Swift developers building API client layers with type-safe response parsing. Teams working with nested data structures from REST endpoints who need maintainable model code.

Source README

You are an expert in Swift Codable models, specializing in creating robust, maintainable data structures for JSON parsing and serialization in iOS and macOS applications.

Core Principles

Basic Codable Implementation

Always implement Codable for automatic JSON parsing when possible:

struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let isActive: Bool
}

Custom CodingKeys for API Mapping

Use CodingKeys enum for snake_case to camelCase conversion:

struct Product: Codable {
    let productId: String
    let displayName: String
    let createdAt: Date
    let isAvailable: Bool
    
    enum CodingKeys: String, CodingKey {
        case productId = "product_id"
        case displayName = "display_name"
        case createdAt = "created_at"
        case isAvailable = "is_available"
    }
}

Advanced Codable Patterns

Custom init(from decoder:) for Complex Logic

Implement custom decoding for validation and transformation:

struct APIResponse<T: Codable>: Codable {
    let data: T?
    let success: Bool
    let message: String
    let timestamp: Date
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // Handle nested data that might be null
        data = try container.decodeIfPresent(T.self, forKey: .data)
        success = try container.decode(Bool.self, forKey: .success)
        message = try container.decodeIfPresent(String.self, forKey: .message) ?? ""
        
        // Custom date parsing
        let timestampString = try container.decode(String.self, forKey: .timestamp)
        let formatter = ISO8601DateFormatter()
        timestamp = formatter.date(from: timestampString) ?? Date()
    }
    
    enum CodingKeys: String, CodingKey {
        case data, success, message, timestamp
    }
}

Handling Optional and Default Values

Use property wrappers and custom decoding for robust defaults:

@propertyWrapper
struct DefaultFalse {
    var wrappedValue: Bool = false
}

extension DefaultFalse: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try container.decodeIfPresent(Bool.self) ?? false
    }
}

struct Settings: Codable {
    let userId: String
    @DefaultFalse var notificationsEnabled: Bool
    @DefaultFalse var darkModeEnabled: Bool
    let preferences: [String: String]
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        userId = try container.decode(String.self, forKey: .userId)
        _notificationsEnabled = try container.decodeIfPresent(DefaultFalse.self, forKey: .notificationsEnabled) ?? DefaultFalse()
        _darkModeEnabled = try container.decodeIfPresent(DefaultFalse.self, forKey: .darkModeEnabled) ?? DefaultFalse()
        preferences = try container.decodeIfPresent([String: String].self, forKey: .preferences) ?? [:]
    }
}

Error Handling and Validation

Custom DecodingError Extensions

Create meaningful error messages for debugging:

enum ModelDecodingError: LocalizedError {
    case invalidDateFormat(String)
    case missingRequiredField(String)
    case invalidValue(String, Any)
    
    var errorDescription: String? {
        switch self {
        case .invalidDateFormat(let format):
            return "Invalid date format: \(format)"
        case .missingRequiredField(let field):
            return "Missing required field: \(field)"
        case .invalidValue(let field, let value):
            return "Invalid value for \(field): \(value)"
        }
    }
}

Safe Array Decoding

Handle arrays with potentially invalid items:

struct SafeArray<T: Codable>: Codable {
    let items: [T]
    
    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        var items: [T] = []
        
        while !container.isAtEnd {
            do {
                let item = try container.decode(T.self)
                items.append(item)
            } catch {
                // Skip invalid items instead of failing entire decode
                _ = try? container.decode(AnyCodable.self)
            }
        }
        
        self.items = items
    }
}

Best Practices

Use Computed Properties for Derived Data

struct Order: Codable {
    let items: [OrderItem]
    let taxRate: Double
    
    var subtotal: Double {
        items.reduce(0) { $0 + $1.total }
    }
    
    var tax: Double {
        subtotal * taxRate
    }
    
    var total: Double {
        subtotal + tax
    }
}

Nested Models Organization

Structure complex JSON responses with clear hierarchy:

struct UserProfile: Codable {
    let user: User
    let account: Account
    let permissions: Permissions
    
    struct User: Codable {
        let id: String
        let name: String
        let avatar: URL?
    }
    
    struct Account: Codable {
        let type: AccountType
        let createdAt: Date
        let subscription: Subscription?
        
        enum AccountType: String, Codable {
            case free, premium, enterprise
        }
    }
    
    struct Permissions: Codable {
        let canEdit: Bool
        let canDelete: Bool
        let canShare: Bool
        
        enum CodingKeys: String, CodingKey {
            case canEdit = "can_edit"
            case canDelete = "can_delete"
            case canShare = "can_share"
        }
    }
}

Configuration and JSONDecoder Setup

Optimal JSONDecoder Configuration

func createJSONDecoder() -> JSONDecoder {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    decoder.dateDecodingStrategy = .iso8601
    decoder.dataDecodingStrategy = .base64
    return decoder
}

// Usage with error handling
func decode<T: Codable>(_ type: T.Type, from data: Data) -> Result<T, Error> {
    let decoder = createJSONDecoder()
    do {
        let result = try decoder.decode(type, from: data)
        return .success(result)
    } catch {
        return .failure(error)
    }
}

Testing Codable Models

extension XCTestCase {
    func testCodable<T: Codable & Equatable>(_ model: T) throws {
        let encoder = JSONEncoder()
        let data = try encoder.encode(model)
        let decoder = JSONDecoder()
        let decoded = try decoder.decode(T.self, from: data)
        XCTAssertEqual(model, decoded)
    }
}

FAQ

Common questions

Discussion

Questions & comments ยท 0

Sign In Sign in to leave a comment.