Contents

kayllawen/autocodablepackage

一個強大的 Swift Macros 套件,為 JSON 解碼提供自動預設值支援,徹底解決了 Property Wrapper + Codable 的技術限制。

🎯 核心功能 / Core Features

✅ 完美的語法支援 / Perfect Syntax Support

@AutoCodable
struct Model: Codable {
    @DecodeDefault("") var name: String           // Use "" when decoding fails
    @DecodeDefault("Tom") var nickname: String    // Use "Tom" when decoding fails
    @DecodeDefault(100) var score: Int            // Use 100 when decoding fails
    @DecodeDefault(.daily) var type: TypeEnum     // Use .daily when decoding fails
}

✅ 解決所有技術問題 / Solve All Technical Issues

  • 無全域變數 / No Global Variables:沒有線程安全問題 / No thread safety issues
  • 無記憶體洩漏 / No Memory Leaks:編譯時生成,無靜態存儲 / Compile-time generation, no static storage
  • 無運行時開銷 / No Runtime Overhead:完全在編譯時處理 / Completely handled at compile time
  • 完全類型安全 / Completely Type Safe:編譯時檢查所有類型 / All types checked at compile time
  • 支援所有 Codable 類型 / Support All Codable Types:包括自定義 enum / Including custom enums

🚀 開始方式 / Usage

1. 基本使用 / Basic Usage

@AutoCodable
struct User: Codable {
    @DecodeDefault("Unknown") var name: String
    @DecodeDefault(0) var age: Int
    @DecodeDefault(true) var isActive: Bool
    
    // Required properties (must exist in JSON)
    var email: String
    var userId: String
}

2. JSON 解碼測試 / JSON Decoding Test

let json = """
{
    "email": "test@example.com",
    "userId": "12345"
    // Note: Missing name, age, isActive intentionally
}
"""

let user = try JSONDecoder().decode(User.self, from: json.data(using: .utf8)!)

print(user.name)     // "Unknown" (using default value)
print(user.age)      // 0 (using default value)
print(user.isActive) // true (using default value)
print(user.email)    // "test@example.com" (from JSON)

🏗️ 技術架構 / Technical Architecture

生成的代碼範例 / Generated Code Example

/* Your code */
@AutoCodable
struct Model: Codable {
    @DecodeDefault("Tom") var name: String
    @DecodeDefault(100) var score: Int
}

/* Expanded Source */
struct Model: Codable {
    var name: String
    var score: Int

    // Following auto-generated code by Macro
    enum CodingKeys: String, CodingKey {
        case name = "name"
        case score = "score"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = (try? container.decode(String.self, forKey: .name)) ?? "Tom"
        self.score = (try? container.decode(Int.self, forKey: .score)) ?? 100
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(score, forKey: .score)
    }
}

📝 詳細使用指南 / Detailed Usage Guide

### 支援的類型 / Supported Types

#### 基本類型 / Basic Types
```swift
@AutoCodable
struct BasicTypes: Codable {
    @DecodeDefault() var optional: String?
    @DecodeDefault("") var text: String
    @DecodeDefault(0) var number: Int
    @DecodeDefault(0.0) var decimal: Double
    @DecodeDefault(false) var flag: Bool
}
```

#### 集合類型 / Collection Types
```swift
@AutoCodable
struct Collections: Codable {
    @DecodeDefault([]) var items: [String]
    @DecodeDefault([:]) var metadata: [String: String]
    @DecodeDefault(Set<String>()) var tags: Set<String>
}
```

#### 自定義 Enum / Custom Enums
```swift
enum Status: String, Codable {
    case active, inactive, pending
}

@AutoCodable
struct WithEnum: Codable {
    @DecodeDefault(Status.pending) var status: Status
}
```

#### 嵌套結構 / Nested Structures
```swift
@AutoCodable
@GenerateEmptyInit
struct Address: Codable {
    @DecodeDefault("") var street: String
    @DecodeDefault("") var city: String
}

extension Address {
    static let `default` = Address()
}

@AutoCodable
struct User: Codable {
    @DecodeDefault("") var name: String
    @DecodeDefault(Address.default) var address: Address
}
```

#### 初始化器生成宏使用 / Initializer Generation Macros Usage

##### @GenerateEmptyInit - 空初始化器 / Empty Initializer
```swift
@AutoCodable
@GenerateEmptyInit
struct Settings: Codable {
    @DecodeDefault(true) var notifications: Bool
    @DecodeDefault("light") var theme: String
    @DecodeDefault(60) var timeout: Int
    @DecodeIgnore var sessionId: String? = nil
}

// Create instance with default values
let defaultSettings = Settings()
print(defaultSettings.notifications) // true
print(defaultSettings.theme)        // "light"
print(defaultSettings.timeout)      // 60
```

##### @GenerateMemberwiseInit - 成員初始化器 / Memberwise Initializer
```swift
@AutoCodable
@GenerateMemberwiseInit
struct Product: Codable {
    @DecodeDefault("") var name: String
    @DecodeDefault(0.0) var price: Double
    @DecodeDefault(true) var isAvailable: Bool
    @DecodeIgnore var createdAt: Date = Date()
}

// Create instance with custom values
let product = Product(
    name: "MacBook Pro",
    price: 2999.99,
    isAvailable: false
)
print(product.name)        // "MacBook Pro"
print(product.price)       // 2999.99
print(product.isAvailable) // false
```

##### 組合使用 / Combined Usage
```swift
@AutoCodable
@GenerateEmptyInit          // Company()
@GenerateMemberwiseInit     // Company(name:industry:location:)
struct Company: Codable {
    @DecodeDefault("") var name: String
    @DecodeDefault("") var industry: String
    @DecodeDefault("TW") var location: String
}

// Both initialization methods available
let defaultCompany = Company()                    // Use default
let customCompany = Company(                      // Use custom values
    name: "Apple",
    industry: "Technology", 
    location: "US"
)

// Usage in extensions
extension Company {
    static let `default` = Company()              // Use default init
    static let tech = Company(                    // Use All parameters init
        name: "Tech Corp",
        industry: "Technology",
        location: "TW"
    )
}
```

##### 複雜嵌套範例 / Complex Nested Example
```swift
@AutoCodable
@GenerateEmptyInit
@GenerateMemberwiseInit
struct DatabaseConfig: Codable {
    @DecodeDefault("localhost") var host: String
    @DecodeDefault(5432) var port: Int
    @DecodeDefault("myapp") var database: String
}

extension DatabaseConfig {
    static let `default` = DatabaseConfig()
}

@AutoCodable
@GenerateEmptyInit
@GenerateMemberwiseInit
struct AppConfig: Codable {
    @DecodeDefault("MyApp") var appName: String
    @DecodeDefault("1.0.0") var version: String
    @DecodeDefault(DatabaseConfig.default) var database: DatabaseConfig
    @DecodeIgnore var buildTimestamp: Date = Date()
}

// Flexible creation methods
let config1 = AppConfig()                                      // Default
let config2 = AppConfig(                                       // Custom top-level properties
    appName: "MyCustomApp",
    version: "2.0.0", 
    database: DatabaseConfig()
)
let config3 = AppConfig(                                       // Fully custom
    appName: "ProductionApp",
    version: "1.5.0",
    database: DatabaseConfig(
        host: "prod-db.company.com",
        port: 5432,
        database: "production"
    )
)
```

#### 屬性驗證規則 / Property Validation Rules
使用 `@GenerateEmptyInit` 或 `@GenerateMemberwiseInit` 時,必須遵循以下規則:

When using `@GenerateEmptyInit` or `@GenerateMemberwiseInit`, you must follow these rules:

```swift
@AutoCodable
@GenerateEmptyInit
@GenerateMemberwiseInit
struct ValidModel: Codable {
    @DecodeDefault("") var name: String           // ✅ Marked @DecodeDefault
    @DecodeDefault(0) var age: Int               // ✅ Marked @DecodeDefault
    @DecodeIgnore var cache: String? = nil // ✅ Marked @DecodeIgnore
}

@AutoCodable
@GenerateMemberwiseInit
struct InvalidModel: Codable {
    @DecodeDefault("") var name: String
    var unmarkedProperty: String  // ❌ Unmarked property: unmarkedProperty
}
```

#### 類型轉換 / Type Conversion
```swift
@AutoCodable
struct TypeConversion: Codable {
    // String to Int conversion
    @DecodeDefault(0, from: String.self) var stringToInt: Int
    
    // String to Bool conversion  
    @DecodeDefault(false, from: String.self) var stringToBool: Bool
    
    // Handle multiple source types
    @DecodeDefault(0, from: [Int.self, String.self]) var flexibleInt: Int
}
```

#### 自定義 JSON Key / Custom JSON Keys
```swift
@AutoCodable
struct CustomKeys: Codable {
    @DecodeDefault("", keyName: "user_name") var userName: String
    @DecodeDefault(0, keyName: "user_id") var userId: Int
    @DecodeDefault(false, keyName: "is_verified") var isVerified: Bool
}
```

#### 忽略屬性 / Ignore Properties
```swift
@AutoCodable
struct WithIgnoredProperties: Codable {
    @DecodeDefault("") var name: String
    @DecodeDefault(0) var age: Int
    @DecodeIgnore var computedValue: String = "calculated"
    @DecodeIgnore var timestamp: Date = Date()
}
```

#### 可選類型預設值 / Optional Type Defaults
```swift
@AutoCodable
struct OptionalDefaults: Codable {
    @DecodeDefault() var optionalName: String?        // Defaults to nil
    @DecodeDefault() var optionalAge: Int?            // Defaults to nil  
    @DecodeDefault("Guest") var nameWithDefault: String  // Defaults to "Guest"
}
```

### 混合使用範例 / Mixed Usage Example

```swift
@AutoCodable
struct Product: Codable {
    // Properties with default values
    @DecodeDefault("") var name: String
    @DecodeDefault(0.0) var price: Double
    @DecodeDefault(true) var isAvailable: Bool
    @DecodeDefault([]) var tags: [String]
    
    // Required properties (must exist in JSON)
    var productId: String
    var createdAt: Date
    
    // Optional properties
    var description: String?
    var imageUrl: String?
}
```

### 邊緣情況處理 / Edge Case Handling

```swift
@AutoCodable
struct EdgeCases: Codable {
    // Handle whitespace in string-to-number conversion
    @DecodeDefault(0, from: String.self) var numberFromString: Int
    
    // Handle NaN for floating point
    @DecodeDefault(0.0, from: String.self) var doubleFromString: Double
    
    // Handle mixed character strings (will use default)
    @DecodeDefault(0, from: String.self) var intFromMixedString: Int
}

// JSON Test Data
let json = """
{
    "numberFromString": "  123  ",      // -> 123 (trimmed)
    "doubleFromString": "NaN",          // -> Double.nan
    "intFromMixedString": "123有中文"    // -> 0 (default, conversion fails)
}
"""
```

🧪 測試和驗證 / Testing and Validation

測試覆蓋 / Test Coverage

  • ✅ 基本 Property Wrapper 功能 / Basic Property Wrapper functionality
  • ✅ Macro 展開正確性 / Macro expansion correctness
  • ✅ 錯誤情況處理 / Error condition handling
  • ✅ 複雜類型支援 / Complex type support
  • ✅ 實際 JSON 解碼場景 / Real JSON decoding scenarios
  • ✅ 類型轉換測試 / Type conversion testing
  • ✅ 邊緣情況測試 / Edge case testing

在 Xcode 中查看 Macro 展開 / View Macro Expansion in Xcode

  1. 右鍵點擊 @AutoCodable / Right-click on @AutoCodable
  2. 選擇 "Expand Macro" / Select "Expand Macro"
  3. 查看生成的完整代碼 / View the complete generated code

🔍 技術細節 / Technical Details

為什麼選擇 Swift Macros? / Why Choose Swift Macros?

問題分析 / Problem Analysis

傳統的 Property Wrapper + Codable 組合有根本性的技術問題:

Traditional Property Wrapper + Codable combination has fundamental technical issues:

@propertyWrapper
struct DecodeDefault<T: Codable>: Codable {
    init(_ defaultValue: T) { /* User-specified default value */ }
    
    init(from decoder: Decoder) throws {
        // ⚠️ Problem: Cannot access the default value passed by user during initialization!
    }
}
protocol DefaultValueProvider {
    associatedtype Value: Codable
    static var defaultValue: Value { get }
}

@propertyWrapper
struct Default<Provider: DefaultValueProvider>: Codable {
    var wrappedValue: Provider.Value = Provider.defaultValue
    // ... decoder implement ...
}

// 
struct One: DefaultValueProvider { static var defaultValue = 1 }
struct Zero: DefaultValueProvider { static var defaultValue = 0 }
struct True: DefaultValueProvider { static var defaultValue = true }

// ⚠️ If you need a different default value (e.g., 100), you would have to define a new struct for each value.
struct OneHundred: DefaultValueProvider { static var defaultValue = 100 }
Swift Macros 解決方案 / Swift Macros Solution

Swift Macros 在編譯時運行,能夠:

Swift Macros run at compile time and can:

  • 📖 編譯時分析 / Compile-time Analysis:Macro 讀取你的源碼並分析 @DecodeDefault 屬性 / Macro reads your source code and analyzes @DecodeDefault attributes
  • 🏗️ 代碼生成 / Code Generation:自動生成完整的 Codable 實現 / Automatically generates complete Codable implementation
  • 🔒 類型安全 / Type Safety:所有類型檢查在編譯時完成 / All type checking completed at compile time

性能影響 / Performance Impact

✅ 編譯時間 / Compile Time
  • 初次編譯可能增加 2-5 秒 / Initial compilation may increase by 2-5 seconds
  • 增量編譯影響極小 / Minimal impact on incremental compilation
✅ App Size
  • 生成的代碼量與手寫代碼相當 / Generated code amount equivalent to hand-written code
  • 影響 < 0.1%,可忽略 / Impact < 0.1%, negligible
✅ 運行時性能 / Runtime Performance
  • 零運行時開銷 / Zero runtime overhead
  • 與手寫 Codable 代碼性能完全相同 / Exactly the same performance as hand-written Codable code

🎉 優勢總結 / Advantages Summary

開發體驗 / Development Experience

  • 🎯 完美語法 / Perfect Syntax:語法簡潔自然,直接在屬性上聲明預設值,可讀性極高。/ Clean and natural syntax, allowing you to declare default values directly on properties for high readability.
  • 🚀 減少代碼 / Reduce Code:自動生成複雜的 Codable 實現 / Automatically generate complex Codable implementation
  • 🔍 易於調試 / Easy to Debug:可以查看生成的代碼 / Can view generated code
  • 📝 類型提示 / Type Hints:完整的 IDE 支援 / Complete IDE support

技術優勢 / Technical Advantages

  • 零運行時開銷 / Zero Runtime Overhead:編譯時處理 / Compile-time processing
  • 🔒 完全類型安全 / Completely Type Safe:編譯時檢查 / Compile-time checking
  • 🧵 無線程問題 / No Threading Issues:無全域狀態 / No global state
  • 💾 無記憶體洩漏 / No Memory Leaks:無靜態存儲 / No static storage

可維護性 / Maintainability

  • 📚 豐富文檔 / Rich Documentation:完整的使用指南 / Complete usage guide
  • 🧪 完整測試 / Complete Testing:涵蓋各種使用場景 / Cover various usage scenarios
  • 🔄 易於擴展 / Easy to Extend:可添加新功能 / Can add new features
  • 🛠️ 工具支援 / Tool Support:測試和調試工具 / Testing and debugging tools

📋 TODO 和未來計劃 / TODO and Future Plans

已完成 ✅ / Completed ✅

  • [x] 核心 DecodeDefault Property Wrapper / Core DecodeDefault Property Wrapper
  • [x] AutoCodable Macro 實現 / AutoCodable Macro implementation
  • [x] @GenerateEmptyInit 宏實現 / @GenerateEmptyInit Macro implementation
  • [x] @GenerateMemberwiseInit 宏實現 / @GenerateMemberwiseInit Macro implementation
  • [x] 自動空初始化器生成 / Automatic empty initializer generation
  • [x] 自動成員初始化器生成 / Automatic memberwise initializer generation
  • [x] 自訂類型預設值支援 / Custom type default value support
  • [x] 完整測試套件 / Complete test suite
  • [x] 使用文檔和範例 / Usage documentation and examples
  • [x] 類型轉換支援 / Type conversion support
  • [x] 邊緣情況處理 / Edge case handling
  • [x] 結構性測試驗證 / Structural test validation

未來增強 🔮 / Future Enhancements 🔮

  • [ ] 支援更多預設值類型(Date, URL 等)/ Support more default value types (Date, URL, etc.)
  • [ ] 添加自定義錯誤處理 / Add custom error handling
  • [ ] 支援嵌套 Macro 展開 / Support nested Macro expansion
  • [ ] 性能優化和更好的錯誤訊息 / Performance optimization and better error messages
  • [ ] SwiftUI 整合範例 / SwiftUI integration examples

📞 支援和貢獻 / Support and Contribution

問題回報 / Issue Reporting

如果遇到任何問題,請檢查 / If you encounter any issues, please check:

  1. Swift 版本是否 >= 5.9 / Swift version >= 5.9
  2. 是否正確添加了 Package 依賴 / Package dependency correctly added
  3. 是否在正確的類型上使用了 @AutoCodable / @AutoCodable used on correct types

📋 查看完整變更記錄 / Full Changelog: CHANGELOG.md

AutoCodable Package - Make JSON decoding simpler, safer, and more elegant! 🎉

Package Metadata

Repository: kayllawen/autocodablepackage

Default branch: main

README: README.md