GitPedia

SmartCodable

SmartCodable is a data parsing library built on Swift’s Codable, designed for simple usage and strong real-world compatibility. It gracefully handles missing fields, default values, and evolving JSON structures. SmartCodable 是基于 Swift Codable 的数据解析库,主打简单易用与真实业务场景下的强兼容性,能够优雅应对不断变化的 JSON 数据。

From iAmMccc·Updated June 25, 2026·View on GitHub·

**SmartCodable** is a SmartCodable is a data parsing library built on Swift’s Codable, designed for simple usage and strong real-world compatibility. It gracefully handles missing fields, default values, and evolving JSON structures. SmartCodable 是基于 Swift Codable 的数据解析库,主打简单易用与真实业务场景下的强兼容性,能够优雅应对不断变化的 JSON 数据。 The project is written primarily in Swift, distributed under the MIT License license, first published in 2023. Key topics include: codable, data-model, decodable, decoding, handyjson.

Latest release: 6.0.1V6.0.1
January 22, 2026View Changelog →
<p align="center"> <img src="https://github.com/intsig171/SmartCodable/assets/87351449/89de27ac-1760-42ee-a680-4811a043c8b1" alt="SmartCodable" title="SmartCodable" width="500"/> </p> <h1 align="center">SmartCodable - Resilient & Flexible Codable for Swift</h1> <p align="center"> <a href="https://github.com/iAmMccc/SmartCodable/releases"> <img src="https://img.shields.io/github/v/release/iAmMccc/SmartCodable?color=blue&label=version" alt="Latest Release"> </a> <a href="https://swift.org/"> <img src="https://img.shields.io/badge/Swift-5.0%2B-orange.svg" alt="Swift 5.0+"> </a> <a href="https://github.com/iAmMccc/SmartCodable/wiki"> <img src="https://img.shields.io/badge/Documentation-available-brightgreen.svg" alt="Documentation"> </a> <a href="https://swift.org/package-manager/"> <img src="https://img.shields.io/badge/SPM-supported-DE5C43.svg?style=flat" alt="SPM Supported"> </a> <a href="https://github.com/iAmMccc/SmartCodable/blob/main/LICENSE"> <img src="https://img.shields.io/badge/license-MIT-black.svg" alt="MIT License"> </a> <a href="https://deepwiki.com/intsig171/SmartCodable"> <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"> </a> </p>

English | 中文

SmartCodable enhances Apple's native Codable with production-ready resilience. When standard Codable fails on a single missing field or type mismatch, your entire model is lost. SmartCodable gracefully recovers — falling back to defaults, converting types automatically, and never interrupting the parse.

Why SmartCodable?

ScenarioStandard CodableSmartCodable
Missing key❌ Throws keyNotFound, entire model fails✅ Uses property initializer as default
Type mismatch (e.g., "123" for Int)❌ Throws typeMismatch, entire model fails✅ Auto-converts, returns 123
Null value for non-optional❌ Throws valueNotFound, entire model fails✅ Falls back to default value
Extra unknown keys✅ Ignored✅ Ignored

vs HandyJSON: SmartCodable builds on Apple's Codable protocol — no unsafe runtime reflection, no ABI stability risks. HandyJSON relies on Swift metadata reflection that may break across Swift versions.

vs Manual init(from:): SmartCodable eliminates the boilerplate of writing decodeIfPresent + ?? for every property. Same safety, zero ceremony.

Quick Start

swift
import SmartCodable struct User: SmartCodableX { var name: String = "" var age: Int = 0 } // ✅ Normal case let user = User.deserialize(from: ["name": "John", "age": 30]) // User(name: "John", age: 30) // ✅ Missing field — falls back to default let user2 = User.deserialize(from: ["name": "John"]) // User(name: "John", age: 0) // ✅ Type mismatch — auto-converts let user3 = User.deserialize(from: ["name": "John", "age": "30"]) // User(name: "John", age: 30)

To conform to SmartCodable, a class needs to implement an empty initializer:

swift
class BasicTypes: SmartCodableX { var int: Int = 2 var doubleOptional: Double? required init() {} } let model = BasicTypes.deserialize(from: json)

For struct, the compiler provides a default empty initializer:

swift
struct BasicTypes: SmartCodableX { var int: Int = 2 var doubleOptional: Double? } let model = BasicTypes.deserialize(from: json)

Installation

Swift Package Manager

swift
dependencies: [ .package(url: "https://github.com/iAmMccc/SmartCodable.git", branch: "main") ]
  • SmartCodable provides the core parsing capabilities with no external dependencies.
  • For class inheritance via @SmartSubclass, see the companion package SmartCodableMacro.

CocoaPods (legacy)

Starting from 7.0, SmartCodable no longer ships via CocoaPods. If you must stay on CocoaPods, please use the 6.x series, which is preserved on the 6.1.0 branch:

ruby
pod 'SmartCodable', '~> 6.1'

The 6.x line will receive critical fixes only. All new features will land in 7.x and beyond — we strongly recommend migrating to Swift Package Manager.

Features

1. Deserialization

Only types conforming to SmartCodable (or [SmartCodable] for arrays) can use these methods:

swift
public static func deserialize(from dict: [String: Any]?, designatedPath: String? = nil, options: Set<SmartDecodingOption>? = nil) -> Self? public static func deserialize(from json: String?, designatedPath: String? = nil, options: Set<SmartDecodingOption>? = nil) -> Self? public static func deserialize(from data: Data?, designatedPath: String? = nil, options: Set<SmartDecodingOption>? = nil) -> Self? public static func deserializePlist(from data: Data?, designatedPath: String? = nil, options: Set<SmartDecodingOption>? = nil) -> Self?

Multi-Format Input Support:

Input TypeExample UsageInternal Conversion
DictionaryModel.deserialize(from: dict)Directly processes native collections
Array[Model].deserialize(from: arr)Directly processes native collections
JSON StringModel.deserialize(from: jsonString)Converts to Data via UTF-8
DataModel.deserialize(from: data)Processes directly

Deep Path Navigation — Extract nested data directly:

swift
// JSON: {"data": {"user": {"info": { ... }}}} let model = Model.deserialize(from: json, designatedPath: "data.user.info")

Decoding Strategies:

swift
let options: Set<SmartDecodingOption> = [ .key(.convertFromSnakeCase), .date(.iso8601), .data(.base64) ] let model = Model.deserialize(from: json, options: options)
Strategy TypeAvailable OptionsDescription
Key Decoding.fromSnakeCasesnake_case → camelCase
.firstLetterLower"FirstName" → "firstName"
.firstLetterUpper"firstName" → "FirstName"
Date Decoding.iso8601, .secondsSince1970, etc.Full Codable date strategies
Data Decoding.base64Binary data processing
Float Decoding.convertToString, .throwNaN/∞ handling

⚠️ Important: Only one strategy per type is allowed (last one wins if duplicates exist)

2. Key Mapping

Map JSON keys to Swift property names. First non-null match wins:

swift
static func mappingForKey() -> [SmartKeyTransformer]? { [ CodingKeys.id <--- ["user_id", "userId", "id"], CodingKeys.name <--- "nested.path.to.name" // nested path supported ] }

3. Value Transformation

Convert between JSON values and custom types:

swift
static func mappingForValue() -> [SmartValueTransformer]? { [ CodingKeys.url <--- SmartURLTransformer(prefix: "https://"), CodingKeys.date <--- SmartDateFormatTransformer(DateFormatter()), CodingKeys.status <--- FastTransformer<Status, String>( fromJSON: { Status(rawValue: $0 ?? "") }, toJSON: { $0?.rawValue } ), ] }

Built-in Transformers:

TransformerJSON → Object
SmartDateTransformerDouble/String → Date
SmartDateFormatTransformerString (custom format) → Date
SmartDataTransformerBase64 String → Data
SmartURLTransformerString → URL (with optional prefix & encoding)
SmartHexColorTransformerHex String → UIColor/NSColor

Need custom logic? Implement ValueTransformable:

swift
public protocol ValueTransformable { associatedtype Object associatedtype JSON func transformFromJSON(_ value: Any?) -> Object? func transformToJSON(_ value: Object?) -> JSON? }

4. Property Wrappers

WrapperPurposeExample
@SmartAnyAny, [Any], [String: Any] support@SmartAny var dict: [String: Any] = [:]
@SmartIgnoredSkip property during decoding@SmartIgnored var cache: String = ""
@SmartFlatFlatten nested object into parent@SmartFlat var profile: Profile?
@SmartPublishedCombine ObservableObject support@SmartPublished var name: String?
@SmartHexColorHex string → UIColor/NSColor@SmartHexColor var color: UIColor?
@SmartDateMulti-format date parsing@SmartDate var date: Date?
@SmartCompact.ArraySkip invalid array elements@SmartCompact.Array var ids: [Int]
@SmartCompact.DictionarySkip invalid dict entries@SmartCompact.Dictionary var info: [String: String]

@SmartAny example — support Any types that Codable can't handle natively:

swift
struct Model: SmartCodableX { @SmartAny var dict: [String: Any] = [:] @SmartAny var arr: [Any] = [] @SmartAny var any: Any? } let dict: [String: Any] = [ "dict": ["name": "Lisa"], "arr": [1, 2, 3], "any": "Mccc" ] let model = Model.deserialize(from: dict) // Model(dict: ["name": "Lisa"], arr: [1, 2, 3], any: "Mccc")

@SmartIgnored example — skip property during decoding:

swift
struct Model: SmartCodableX { @SmartIgnored var name: String = "" } let model = Model.deserialize(from: ["name": "Mccc"]) // Model(name: "") — "name" was ignored, keeps default

@SmartFlat example — flatten nested fields into parent:

swift
struct Model: SmartCodableX { var name: String = "" @SmartFlat var profile: Profile? } struct Profile: SmartCodableX { var name: String = "" var age: Int = 0 } // JSON: {"name": "Mccc", "age": 18} // profile gets name="Mccc", age=18 from the SAME level

@SmartCompact.Array example — tolerant array parsing:

swift
struct Model: Decodable { @SmartCompact.Array var ages: [Int] } // JSON: {"ages": ["Tom", 1, {}, 2, 3, "4"]} // Result: ages = [1, 2, 3, 4] (invalid elements skipped, "4" auto-converted)

5. Inheritance

Class inheritance support has been moved to a separate package — SmartCodableMacro. It depends on swift-syntax, so we ship it independently to keep this core library lightweight and dependency-free.

Add it alongside SmartCodable when you need @SmartSubclass:

swift
dependencies: [ .package(url: "https://github.com/iAmMccc/SmartCodableMacro.git", branch: "main") ]

For inheritance usage on Swift versions prior to 5.9, see Inheritance in Lower Versions.

6. Enum Support

Simple enums — conform to SmartCaseDefaultable:

swift
enum Sex: String, SmartCaseDefaultable { case man case woman }

Enums with associated values — conform to SmartAssociatedEnumerable and provide a transformer via mappingForValue():

swift
struct Model: SmartCodableX { var sex: Sex = .man static func mappingForValue() -> [SmartValueTransformer]? { [ CodingKeys.sex <--- SexTransformer() ] } } enum Sex: SmartAssociatedEnumerable { case man, woman, other(String) } struct SexTransformer: ValueTransformable { typealias Object = Sex typealias JSON = String func transformFromJSON(_ value: Any?) -> Sex? { guard let str = value as? String else { return nil } switch str { case "man": return .man case "woman": return .woman default: return .other(str) } } func transformToJSON(_ value: Sex?) -> String? { nil } }

7. Post-Processing & Update

didFinishMapping() — runs after decoding completes:

swift
struct Model: SmartCodableX { var name: String = "" mutating func didFinishMapping() { name = "I am \(name)" } }

SmartUpdater — update an existing model with new data:

swift
var model = Model.deserialize(from: initialData)! SmartUpdater.update(&model, from: newData)

8. Compatibility

SmartCodable handles parsing failures gracefully, ensuring the entire model never fails:

swift
let dict = ["number1": "123", "number2": "Mccc", "number3": "Mccc"] struct Model: SmartCodableX { var number1: Int? var number2: Int? var number3: Int = 1 } // Result: Model(number1: 123, number2: nil, number3: 1)
  • Type conversion: "123" (String) → 123 (Int) automatically
  • Default fill: When conversion fails, uses the property's initializer value (number3 = 1)
  • Optional handling: When conversion fails for optionals, returns nil (number2 = nil)

Performance tip for large data: When parsing very large datasets, avoid unnecessary compatibility overhead — use CodingKeys to exclude unused properties instead of @SmartIgnored, as it's more efficient.

9. Stringified JSON

SmartCodable auto-detects and parses string-encoded JSON:

swift
struct Model: SmartCodableX { var hobby: Hobby? } // JSON: {"hobby": "{\"name\":\"sleep\"}"} // hobby is parsed as Hobby(name: "sleep"), not a raw string

10. Debugging

swift
SmartSentinel.debugMode = .verbose // .none | .verbose | .alert SmartSentinel.onLogGenerated { log in print(log) }
================================  [Smart Sentinel]  ================================
UserModel 👈🏻 👀
╆━ UserModel
┆┄ age    : Expected Int, got String — auto-converted
┆┄ email  : Key not found — using default ""
====================================================================================

Explore & Contribute

🔧 Migrate from HandyJSONStep-by-step migration guide
🛠 SmartModelerJSON → SmartCodable model generator
👀 SmartSentinelReal-time parsing log viewer
💖 ContributingSupport SmartCodable development
🏆 ContributorsKey contributors

FAQ

GitHub Stars

<p style="margin:0"> <img src="https://starchart.cc/iAmMccc/SmartCodable.svg" alt="Stars" width="750"> </p>

Join Community 🚀

SmartCodable is an open-source project dedicated to making Swift data parsing more robust, flexible and efficient. We welcome all developers to join our community!

<p> <img src="https://github.com/user-attachments/assets/7b1f8108-968e-4a38-91dd-b99abdd3e500" alt="JoinUs" width="700"> </p>

Contributors

Showing top 9 contributors by commit count.

View all contributors on GitHub →

This article is auto-generated from iAmMccc/SmartCodable via the GitHub API.Last fetched: 6/28/2026