在 UnitMass 上实施 RawRepresentable
Implement RawRepresentable on UnitMass
我正在尝试在 Measurement<UnitMass>
和 UnitMass
上实现 RawRepresentable
,以便用 @AppStorage
装饰器替换以下代码:
var unitOfMeasure: UnitMass {
get { AppSettings.defaults.string(forKey: "unitOfMeasure").flatMap { UnitMass.fromSymbol(rawValue: [=10=]) }! }
set { AppSettings.defaults.set(newValue.symbol, forKey: "unitOfMeasure") }
}
var weightOverwrite: Measurement<UnitMass> {
get { .init(value: AppSettings.defaults.double(forKey: "weightOverwrite"), unit: unitOfMeasure) }
set { AppSettings.defaults.set(newValue.value, forKey: "weightOverwrite") }
}
我该怎么做?我有点用 JSONEncoder/JSONDecoder:
实现了它
extension Measurement: RawRepresentable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Measurement.self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "{}"
}
return result
}
}
但是我没有为 UnitMass:
extension UnitMass: RawRepresentable {
public init?(rawValue: String) {
for unitMass in UnitMass.allCases where rawValue == unitLength.symbol {
self = unitLength
}
return nil
}
}
我得到 Designated initializer cannot be declared in an extension of 'UnitMass'
。我做错了什么?
这就是为什么你不能让 UnitMass
符合 RawRepresentable
。
RawRepresentable
要求符合 classes 必须有一个 init(rawValue:)
初始化器。
UnitMass
不是 final
,所以它可以有子class。
UnitMass
的子class 也符合 RawRepresentable
如果 UnitMass
符合 RawRepresentable
,那么它们也必须有 init(rawValue:)
.
子class中的init(rawValue:)
如何实现?请注意,它们不能只继承 UnitMass
中的实现,因为子 class 可能有自己的存储属性,需要在初始化程序中初始化。
所以您的扩展程序需要 UnitMass
的所有子 classes 来实现这个新的初始化程序。好吧,扩展不应该添加需求——它们应该添加功能!
即使扩展 可以 做到这一点,您去 UnitMass
的每个子 class 并添加 [= 的实现也是不切实际的=16=] :)
无论如何,这里有一些解决方法:
使用包装器 class:
class MyUnitMass: RawRepresentable {
let unitMass: UnitMass
var rawValue: String {
unitMass.symbol
}
required init?(rawValue: String) {
// assuming fromSymbol actually uses the correct converter
unitMass = UnitMass.fromSymbol(rawValue: rawValue)
}
}
或者,将UserDefaults
中的UnitMass
保存为Data
而不是String
,因为UnitMass
符合NSSecureCoding
。
let data = try NSKeyedArchiver.archivedData(withRootObject: UnitMass.grams, requiringSecureCoding: false)
// save "data" to UserDefaults instead
let unitMass = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! UnitMass
这样做的好处是还可以对 converter
进行编码,而无需在 fromSymbol
中对其进行硬编码(大概是您现在正在做的事情)。
另请注意,如果您仅将 unitOfMeasure
设置用作 weightOverwrite
的单位,则应将 weightOverwrite
保存在用户默认值中,并声明 unitOfMeasure
像这样:
@AppStorage("hello", store: UserDefaults.standard)
var weightOverwrite: Measurement<UnitMass> = Measurement(value: 1, unit: .grams)
var unitOfMeasure: UnitMass {
get { weightOverwrite.unit }
set { weightOverwrite.convert(to: newValue) }
}
您可以扩展 RawReprresentable
本身,以便 UnitMass
开始自动符合它。
extension RawRepresentable where Self: NSSecureCoding {
public var rawValue: String {
let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
return String(data: data!, encoding: .utf8)!
}
public init?(rawValue: String) {
if let unitMass = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(Data(rawValue.utf8))) as? Self {
self = unitMass
} else {
return nil
}
}
}
extension UnitMass: RawRepresentable { }
我正在尝试在 Measurement<UnitMass>
和 UnitMass
上实现 RawRepresentable
,以便用 @AppStorage
装饰器替换以下代码:
var unitOfMeasure: UnitMass {
get { AppSettings.defaults.string(forKey: "unitOfMeasure").flatMap { UnitMass.fromSymbol(rawValue: [=10=]) }! }
set { AppSettings.defaults.set(newValue.symbol, forKey: "unitOfMeasure") }
}
var weightOverwrite: Measurement<UnitMass> {
get { .init(value: AppSettings.defaults.double(forKey: "weightOverwrite"), unit: unitOfMeasure) }
set { AppSettings.defaults.set(newValue.value, forKey: "weightOverwrite") }
}
我该怎么做?我有点用 JSONEncoder/JSONDecoder:
实现了它extension Measurement: RawRepresentable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Measurement.self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "{}"
}
return result
}
}
但是我没有为 UnitMass:
extension UnitMass: RawRepresentable {
public init?(rawValue: String) {
for unitMass in UnitMass.allCases where rawValue == unitLength.symbol {
self = unitLength
}
return nil
}
}
我得到 Designated initializer cannot be declared in an extension of 'UnitMass'
。我做错了什么?
这就是为什么你不能让 UnitMass
符合 RawRepresentable
。
RawRepresentable
要求符合 classes 必须有一个 init(rawValue:)
初始化器。
UnitMass
不是 final
,所以它可以有子class。
UnitMass
的子class 也符合 RawRepresentable
如果 UnitMass
符合 RawRepresentable
,那么它们也必须有 init(rawValue:)
.
子class中的init(rawValue:)
如何实现?请注意,它们不能只继承 UnitMass
中的实现,因为子 class 可能有自己的存储属性,需要在初始化程序中初始化。
所以您的扩展程序需要 UnitMass
的所有子 classes 来实现这个新的初始化程序。好吧,扩展不应该添加需求——它们应该添加功能!
即使扩展 可以 做到这一点,您去 UnitMass
的每个子 class 并添加 [= 的实现也是不切实际的=16=] :)
无论如何,这里有一些解决方法:
使用包装器 class:
class MyUnitMass: RawRepresentable {
let unitMass: UnitMass
var rawValue: String {
unitMass.symbol
}
required init?(rawValue: String) {
// assuming fromSymbol actually uses the correct converter
unitMass = UnitMass.fromSymbol(rawValue: rawValue)
}
}
或者,将UserDefaults
中的UnitMass
保存为Data
而不是String
,因为UnitMass
符合NSSecureCoding
。
let data = try NSKeyedArchiver.archivedData(withRootObject: UnitMass.grams, requiringSecureCoding: false)
// save "data" to UserDefaults instead
let unitMass = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! UnitMass
这样做的好处是还可以对 converter
进行编码,而无需在 fromSymbol
中对其进行硬编码(大概是您现在正在做的事情)。
另请注意,如果您仅将 unitOfMeasure
设置用作 weightOverwrite
的单位,则应将 weightOverwrite
保存在用户默认值中,并声明 unitOfMeasure
像这样:
@AppStorage("hello", store: UserDefaults.standard)
var weightOverwrite: Measurement<UnitMass> = Measurement(value: 1, unit: .grams)
var unitOfMeasure: UnitMass {
get { weightOverwrite.unit }
set { weightOverwrite.convert(to: newValue) }
}
您可以扩展 RawReprresentable
本身,以便 UnitMass
开始自动符合它。
extension RawRepresentable where Self: NSSecureCoding {
public var rawValue: String {
let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
return String(data: data!, encoding: .utf8)!
}
public init?(rawValue: String) {
if let unitMass = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(Data(rawValue.utf8))) as? Self {
self = unitMass
} else {
return nil
}
}
}
extension UnitMass: RawRepresentable { }