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
- 右鍵點擊
@AutoCodable/ Right-click on@AutoCodable - 選擇 "Expand Macro" / Select "Expand Macro"
- 查看生成的完整代碼 / 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@DecodeDefaultattributes - 🏗️ 代碼生成 / Code Generation:自動生成完整的
Codable實現 / Automatically generates completeCodableimplementation - 🔒 類型安全 / 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:
- Swift 版本是否 >= 5.9 / Swift version >= 5.9
- 是否正確添加了 Package 依賴 / Package dependency correctly added
- 是否在正確的類型上使用了
@AutoCodable/@AutoCodableused 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