---
# System prepended metadata

title: Swift PropertyWrapper
tags: [iOS, Swift]

---

# Swift PropertyWrapper
###### tags: `iOS` `Swift`
## 目的：

在解JSON資料時，若出現格式不符合，或原本必填的參數沒給，會自動帶入預設值而不會噴錯。

## 範例：

JSON資料如下

``` JSON
{
    "name": "orange",  //食物名稱
    "calorie": 54,  //熱量
    "state": "good"  // 食物保存狀態 1.good 2.normal 3.bad
    "isExpired": false //是否過期
}
```
但若今天傳入
``` JSON
{
    "name": "orange",  
    "calorie": 54,  
    "state": "excellent"  
}
```
也要能夠正確解析
## 程式碼完成如下

``` swift 

import Foundation

protocol DefaultValue {
    associatedtype Value: Decodable
    static var defaultValue: Value { get }
}

extension Bool {
    enum False: DefaultValue {
          static let defaultValue = false
      }
    enum True: DefaultValue {
          static let defaultValue = true
      }
}

@propertyWrapper
struct Default<T: DefaultValue> {
    var wrappedValue: T.Value
}
extension Default: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = (try? container.decode(T.Value.self)) ?? T.defaultValue
    }
}
extension KeyedDecodingContainer {
    func decode<T>(
         type: Default<T>.Type,
        forKey key: Key
    ) throws -> Default<T> where T: DefaultValue {
        try decodeIfPresent(type, forKey: key) ?? Default(wrappedValue: T.defaultValue)
    }
}


struct Food: Decodable {
    
    struct FoodState: RawRepresentable, Decodable {
        static let good = FoodState(rawValue: "good")
        static let bad = FoodState(rawValue: "bad")
        static let normal = FoodState(rawValue: "normal")
        
        let rawValue: String
    }
    
    var name: String
    var calorie: Int
    @Default<Bool.False> var isExpired: Bool
    @Default<FoodState.Normal> var state: FoodState
}
extension Food.FoodState {
    enum Normal: DefaultValue {
        static let defaultValue = Food.FoodState.normal
    }
    enum Good: DefaultValue {
        static let defaultValue = Food.FoodState.good
    }
    enum Bad: DefaultValue {
        static let defaultValue = Food.FoodState.bad
    }
  
}
```
## 測試

```swift
let jsonString = """
        {
           "name": "Apple",
           "calorie": 54,
           "state": "excellent"
        }
    """
let jsonData = jsonString.data(using: .utf8)
let food = try JSONDecoder().decode(Food.self, from: jsonData!)
print("calorie: \(food.calorie)") //calorie: 54
print("isExpired: \(food.isExpired)") //isExpired: false
print("name: \(food.name)") //name: Apple
print("state: \(food.state.rawValue)") //state: excellent





```