我創建了一個這樣的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。但我明白了。致命錯誤:
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()
uj5u.com熱心網友回復:
您可以通過向(或)添加一個新decode(_:forKey:)方法來實作這一點,該方法將在默認一致性下自動使用。KeyedDecodingContainerProtocolKeyedDecodingContainerDecodable
此方法必須將.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
uj5u.com熱心網友回復:
根據屬性包裝器的提議,使用屬性包裝器注釋的屬性被轉換為計算屬性和存盤屬性。存盤屬性存盤屬性包裝器的實體,計算屬性獲取(或設定)包裝后的值。
例如,這是其中一種變體:
@Lazy var foo = 17
// ... implemented as
private var _foo: Lazy = Lazy(wrappedValue: 17)
var foo: Int {
get { _foo.wrappedValue }
set { _foo.wrappedValue = newValue }
}
請注意,在所有變體中,存盤屬性始終是非可選的。這意味著在生成Codable一致性時,屬性包裝器將始終生成為decode,而不是decodeIfPresent,并且如果密鑰不存在,則會引發錯誤。
所以@DefaultTodayDate var date: Date是不可能的,但我們仍然可以將DefaultTodayDate其用作普通型別,假設您想要使用包裝器來決議它。
首先,將無引數初始化器添加到DefaultTodayDate:
public init() {
wrappedValue = Date()
}
然后做:
struct MyModel: Codable {
enum CodingKeys: String, CodingKey {
case dateWrapper = "date"
}
private var dateWrapper: DefaultTodayDate?
mutating func setDateToTodayIfNeeded() {
dateWrapper = dateWrapper ?? .init() // Note that
}
var date: Date {
dateWrapper.wrappedValue
}
}
如果Date改為重命名屬性,則可以避免撰寫所有編碼鍵,并將其命名DefaultTodayDate為date.
要解碼,您只需要setDateToTodayIfNeeded另外呼叫:
var result = try! JSONDecoder().decode(MyModel.self, from: json)
result.setDateToTodayIfNeeded() // if date key is missing, the date when this is called will be used
print(result.date)
setDateToTodayIfNeeded如果你不介意使用mutating geton ,你可以避免這樣做date,我覺得這很“惡心”:
var date: Date {
mutating get {
let wrapper = dateWrapper ?? DefaultTodayDate()
dateWrapper = wrapper
return wrapper.wrappedValue
}
}
var result = try! JSONDecoder().decode(MyModel.self, from: json)
print(result.date) // if date key is missing, the date when you first get date will be used
決議具有各種日期格式的 JSON 的其他選項是DateDecodingStrategy.custom,這也值得探索。您只需瀏覽所有預期的格式并一一嘗試。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/506892.html
