ios - PropertyWrapper 中不存在的 Codable 属性的默认值

标签 ios swift xcode

我创建了一个像这样的propertyWrapper:

@propertyWrapper
public struct DefaultTodayDate: Codable {
    public var wrappedValue: Date

    private let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "y-MM-dd'T'HH:mm:ss"

        return formatter
    }()

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        var stringDate = ""
        do {
            stringDate = try container.decode(String.self)
            self.wrappedValue = self.dateFormatter.date(from: stringDate) ?? Date()
        } catch {
            self.wrappedValue = Date()
        }
    }

    public func encode(to encoder: Encoder) throws {
        try wrappedValue.encode(to: encoder)
    }
}

和这样的模型:

struct MyModel: Codable {
    @DefaultTodayDate var date: Date
}

所以,如果我想解析这个json文件,一切都可以:

let json = #"{ "date": "2022-10-10T09:09:09" }"#.data(using: .utf8)!
let result = try! JSONDecoder().decode(MyModel.self, from: json)

print(result) // result.date is: 2022-10-10 09:09:09 +0000
-----

let json = #"{ "date": "" }"#.data(using: .utf8)!
let result = try! JSONDecoder().decode(MyModel.self, from: json)

print(result) // result.date is: Date()
-----

let json = #"{ "date": null }"#.data(using: .utf8)!
let result = try! JSONDecoder().decode(MyModel.self, from: json)

print(result) // result.date is: Date()

但我也想解析没有 date 属性的 json。但我明白了。 fatal error :

let json = #"{ "book": "test" }"#.data(using: .utf8)!
let result = try! JSONDecoder().decode(MyModel.self, from: json)

// Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "date", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"date\", intValue: nil) (\"date\").", underlyingError: nil))

print(result) // i want to result.date be Date() 

最佳答案

您可以通过向 KeyedDecodingContainerProtocol (或 KeyedDecodingContainer)添加新的 decode(_:forKey:) 方法来实现此目的,该方法将自动默认情况下使用 Decodable 一致性。

此方法必须将属性包装器的 .Type 作为其第一个参数,并返回属性包装器的实例。

在您的情况下,这样的扩展将如下所示:

extension KeyedDecodingContainerProtocol { // KeyedDecodingContainer works too
    public func decode(_ type: DefaultTodayDate.Type, forKey key: Key) throws -> DefaultTodayDate {
        return try decodeIfPresent(type, forKey: key) ?? DefaultTodayDate(wrappedValue: Date())
    }
}

然后只需将此初始值设定项添加到您的 DefaultTodayDate 类型中即可:

public init(wrappedValue: Date) {
    self.wrappedValue = wrappedValue
}

您失败的示例现在可以正常工作:

let json = #"{ "book": "test" }"#.data(using: .utf8)!
let result = try! JSONDecoder().decode(MyModel.self, from: json)
print(result.date) // 2022-09-08 08:16:33 +0000
print(Date())      // 2022-09-08 08:16:33 +0000

关于ios - PropertyWrapper 中不存在的 Codable 属性的默认值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73644388/

相关文章:

iphone - ios 的 Cordova 3.0.0 ActionSheet 插件无法正常工作

iphone - 在Three20 TTLauncherView中更改页面控制点的颜色

ios - 如何以编程方式将多个自定义 XIB 添加到 ScrollView

ios - 带有 Realm 支持的数据库和在线同步的 UITableView

ios - UINavigationBar 返回按钮标题无法使用 iOS 13 的导航栏外观删除

html - Perfect app 中 webroot 的位置(用于 css 文件)

android - 运行xcode时出现词法错误

ios - 如何在 Swift 中获取 'expected' 类型的 CoreData 属性?

ios - 通过变量使 UIAlertView 触发

ios - 模态转场,但我需要一个模态礼物