Swift Codable logo

Swift Codable

CommunityPopular
dpearson2699
swift-codable

Implement Swift Codable models for JSON and property-list encoding and decoding with JSONDecoder, JSONEncoder, CodingKeys, and custom init(from:) or encode(to:). Use when parsing API responses, remapping keys, flattening nested JSON, handling date or data decoding strategies, decoding heterogeneous arrays, or integrating Codable with URLSession, SwiftData, or UserDefaults.

Overview

Publisherdpearson2699
Repositoryswift-ios-skills
Skill nameswift-codable
Stars
1.2K
Forks
61
Bundled files
2
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 2 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by dpearson2699 on GitHub. Read the source before you install it.

Installation

Install the Swift Codable AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/swift-codable
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/dpearson2699/swift-ios-skills.git /tmp/swift-ios-skills
mkdir -p .claude/skills
cp -r /tmp/swift-ios-skills/skills/swift-codable .claude/skills/swift-codable
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Swift Codable in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Swift Codable on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Swift Codable is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Swift Codable

Encode and decode Swift types using Codable (Encodable & Decodable) with JSONEncoder, JSONDecoder, and related APIs. Targets Swift 6.3 / iOS 26+.

Contents

Decode and Verify Workflow

  1. Decode representative success, missing, null, malformed, acronym-key, and date fixtures.
  2. On failure, inspect DecodingError, its codingPath, and the raw payload.
  3. Correct only the mismatched model, key, container, or strategy; do not hide contract failures with lossy decoding.
  4. Rerun fixtures and encode/decode round trips where both directions are part of the contract.

Basic Conformance

When all stored properties are themselves Codable, the compiler synthesizes conformance automatically:

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

let user = try JSONDecoder().decode(User.self, from: jsonData)
let encoded = try JSONEncoder().encode(user)

Prefer Decodable for read-only API responses and Encodable for write-only. Use Codable only when both directions are required.

Custom CodingKeys

Rename JSON keys without writing a custom decoder by declaring a CodingKeys enum:

swift
struct Product: Codable {
    let id: Int
    let displayName: String
    let imageURL: URL
    let priceInCents: Int

    enum CodingKeys: String, CodingKey {
        case id
        case displayName = "display_name"
        case imageURL = "image_url"
        case priceInCents = "price_in_cents"
    }
}

Every stored property must appear in the enum. Omitting a property from CodingKeys excludes it from encoding/decoding -- provide a default value or compute it separately.

Custom Decoding and Encoding

Override init(from:) and encode(to:) for transformations the synthesized conformance cannot handle:

swift
struct Event: Codable {
    let name: String
    let timestamp: Date
    let tags: [String]

    enum CodingKeys: String, CodingKey {
        case name, timestamp, tags
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        // Decode Unix timestamp as Double, convert to Date
        let epoch = try container.decode(Double.self, forKey: .timestamp)
        timestamp = Date(timeIntervalSince1970: epoch)
        // Default to empty array when key is missing
        tags = try container.decodeIfPresent([String].self, forKey: .tags) ?? []
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(timestamp.timeIntervalSince1970, forKey: .timestamp)
        try container.encode(tags, forKey: .tags)
    }
}

Nested and Flattened Containers

Use nestedContainer(keyedBy:forKey:) to navigate and flatten nested JSON:

swift
// JSON: { "id": 1, "location": { "lat": 37.7749, "lng": -122.4194 } }
struct Place: Decodable {
    let id: Int
    let latitude: Double
    let longitude: Double

    enum CodingKeys: String, CodingKey { case id, location }
    enum LocationKeys: String, CodingKey { case lat, lng }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        let location = try container.nestedContainer(
            keyedBy: LocationKeys.self, forKey: .location)
        latitude = try location.decode(Double.self, forKey: .lat)
        longitude = try location.decode(Double.self, forKey: .lng)
    }
}

Chain multiple nestedContainer calls to flatten deeply nested structures. Also use nestedUnkeyedContainer(forKey:) for nested arrays.

Heterogeneous Arrays

Load Advanced Codable Patterns for discriminator-based mixed arrays.

Date Decoding Strategies

Configure JSONDecoder.dateDecodingStrategy to match your API:

swift
let decoder = JSONDecoder()

// ISO 8601 (e.g., "2024-03-15T10:30:00Z")
decoder.dateDecodingStrategy = .iso8601

// Unix timestamp in seconds (e.g., 1710499800)
decoder.dateDecodingStrategy = .secondsSince1970

// Custom DateFormatter
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
decoder.dateDecodingStrategy = .formatted(formatter)

// Custom closure for multiple formats
decoder.dateDecodingStrategy = .custom { decoder in
    let container = try decoder.singleValueContainer()
    let string = try container.decode(String.self)
    if let date = ISO8601DateFormatter().date(from: string) { return date }
    throw DecodingError.dataCorruptedError(
        in: container, debugDescription: "Cannot decode date: \(string)")
}

Set the matching strategy on JSONEncoder: encoder.dateEncodingStrategy = .iso8601

Data and Key Strategies

swift
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = .base64           // Base64-encoded Data fields
decoder.keyDecodingStrategy = .convertFromSnakeCase  // simple keys only; not URL/ID spelling
// {"user_name": "Alice"} maps to `var userName: String` -- no CodingKeys needed

let encoder = JSONEncoder()
encoder.dataEncodingStrategy = .base64
encoder.keyEncodingStrategy = .convertToSnakeCase

Use key strategies only for mechanical snake_case-to-camelCase mappings. convertFromSnakeCase maps by spelling, not Swift acronym/initialism policy: image_url, base_uri, and user_id match imageUrl, baseUri, and userId only. If the Swift model uses imageURL, baseURI, or userID, declare explicit CodingKeys; the strategy will not synthesize those names.

Lossy Array Decoding

Use lossy arrays only when partial success is part of the product contract; load Lossy Arrays.

Single Value Containers

Use singleValueContainer() for type-safe primitive wrappers; see Single-Value Wrappers.

Default Values for Missing Keys

Stored defaults do not make synthesized decoding tolerate missing nonoptional keys. Load Missing-Key Defaults when the contract assigns explicit fallback behavior to missing or null values.

Encoder and Decoder Configuration

Keep matching strategies at the transport/file-format boundary. Load Encoder Configuration for nonconforming floats and property-list guidance.

Codable with URLSession

swift
func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, response) = try await URLSession.shared.data(from: url)
    guard let http = response as? HTTPURLResponse,
          (200...299).contains(http.statusCode) else {
        throw APIError.invalidResponse
    }
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    decoder.dateDecodingStrategy = .iso8601
    return try decoder.decode(User.self, from: data)
}

// Generic API envelope. Configure a decoder inside this helper because
// fetchUser's decoder is out of scope.
struct APIResponse<T: Decodable>: Decodable {
    let data: T
    let meta: Meta?
    struct Meta: Decodable { let page: Int; let totalPages: Int }
}

func decodeUsersEnvelope(from data: Data) throws -> [User] {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    decoder.dateDecodingStrategy = .iso8601
    return try decoder.decode(APIResponse<[User]>.self, from: data).data
}

Codable with SwiftData

Keep schema values typed and route persistence design to swiftdata; see Persistence Boundaries.

Codable with UserDefaults

Use primitives for small preferences. Load Persistence Boundaries for a small Codable RawRepresentable/@AppStorage handoff; use a real persistence layer for larger or durable data.

Common Mistakes

1. Not handling missing defaulted fields:

swift
// DON'T -- crashes if key is absent
let value = try container.decode(String.self, forKey: .bio)
// DO -- falls back when the key is absent or null
let value = try container.decodeIfPresent(String.self, forKey: .bio) ?? ""

2. Failing entire array when one element is invalid:

swift
// DON'T -- one bad element kills the whole decode
let items = try container.decode([Item].self, forKey: .items)
// DO -- decode elements individually only when partial success is allowed

3. Date strategy mismatch:

swift
// DON'T -- default strategy expects Double, but API sends ISO string
let decoder = JSONDecoder()  // dateDecodingStrategy defaults to .deferredToDate
// DO -- set strategy to match your API format
decoder.dateDecodingStrategy = .iso8601

4. Force-unwrapping decoded optionals:

swift
// DON'T
let user = try? decoder.decode(User.self, from: data)
print(user!.name)
// DO
guard let user = try? decoder.decode(User.self, from: data) else { return }

5. Using Codable when only Decodable is needed:

swift
// DON'T -- unnecessarily constrains the type to also be Encodable
struct APIResponse: Codable { let id: Int; let message: String }
// DO -- use Decodable for read-only API responses
struct APIResponse: Decodable { let id: Int; let message: String }

6. Manual CodingKeys for simple snake_case APIs:

swift
// DON'T -- verbose boilerplate for every model
enum CodingKeys: String, CodingKey {
    case userName = "user_name"
    case avatarUrl = "avatar_url"
}
// DO -- configure once on the decoder for simple cases
decoder.keyDecodingStrategy = .convertFromSnakeCase
// Keep CodingKeys for `imageURL`, `baseURI`, `userID`, and similar names.

Review Checklist

  • Types conform to Decodable only when encoding is not needed
  • decodeIfPresent used with defaults for optional or missing keys
  • keyDecodingStrategy = .convertFromSnakeCase used for simple snake_case APIs, with CodingKeys retained for acronym spellings
  • dateDecodingStrategy matches the API date format
  • Arrays of unreliable data use lossy decoding to skip invalid elements
  • Custom init(from:) validates and transforms data instead of post-decode fixups
  • JSONEncoder.outputFormatting includes .sortedKeys for deterministic test output
  • Wrapper types (UserID, etc.) use singleValueContainer for clean JSON
  • Generic APIResponse<T> wrapper used for consistent API envelope handling
  • No force-unwrapping of decoded values
  • Persistence boundary is explicit: SwiftData only for compatible noncomputed model properties, @AppStorage/UserDefaults only for small primitive or RawRepresentable preferences

References

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Swift Codable AI skill do?

Implement Swift Codable models for JSON and property-list encoding and decoding with JSONDecoder, JSONEncoder, CodingKeys, and custom init(from:) or encode(to:). Use when parsing API responses, remapping keys, flattening nested JSON, handling date or data decoding strategies, decoding heterogeneous arrays, or integrating Codable with URLSession, SwiftData, or UserDefaults.

Why use Swift Codable on TypingMind?

Because you install it once and use it with any model. Swift Codable is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Swift Codable in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/swift-codable. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Swift Codable?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Swift Codable?

As many as you like. As long as a model supports skills, you can use Swift Codable with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Swift Codable AI skill free?

It is published on GitHub by dpearson2699. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇