我在 Swift 上有一个相当老的项目,仍然使用
NSCalendar.Unit
,而且它非常方便,因为它的 rawValue
是一个可以存储在我的 Core Data 数据库中的 UInt
。我想转向更新的 Swift API,所以我必须使用 Set<Calendar.Component>
,但看起来 Calendar.Component
只是一个没有 rawValue
的 Swift 枚举。我发现它有一个hashValue
,但Apple不建议保存它,而且Calendar.Component
不符合Encodable
。那么有什么方法可以将 Set<Calendar.Component>
存储在核心数据中,还是我只能使用旧的 NSCalendar.Unit
?
您可以使
Calendar.Component
符合 RawRepresentable
,从而拥有 rawValue
,然后将其存储为 RawValue
。或者你也可以使其符合Codable
,然后将其存储为Data
。
extension Calendar.Component: RawRepresentable {
public var rawValue: Int {
switch self {
case .calendar:
return 0
case .day:
return 1
case .era:
return 2
case .hour:
return 3
case .minute:
return 4
case .month:
return 5
case .nanosecond:
return 6
case .quarter:
return 7
case .second:
return 8
case .timeZone:
return 9
case .weekday:
return 10
case .weekdayOrdinal:
return 11
case .weekOfMonth:
return 12
case .weekOfYear:
return 13
case .year:
return 14
case .yearForWeekOfYear:
return 15
}
}
public init?(rawValue: Int) {
switch rawValue {
case 0:
self = .calendar
case 1:
self = .day
case 2:
self = .era
case 3:
self = .hour
case 4:
self = .minute
case 5:
self = .month
case 6:
self = .nanosecond
case 7:
self = .quarter
case 8:
self = .second
case 9:
self = .timeZone
case 10:
self = .weekday
case 11:
self = .weekdayOrdinal
case 12:
self = .weekOfMonth
case 13:
self = .weekOfYear
case 14:
self = .year
case 15:
self = .yearForWeekOfYear
default:
return nil
}
}
}
Codable
一致性不是必需的,RawRepresentable
应该足够了,但留在这里作为参考。
extension Calendar.Component: Codable {
enum DecodingError: Error {
case unknownRawValue
}
/// Throwable initialiser that throws when an unknown `RawValue` is passed to it
/// Necessary for `init(from decoder:)` to be able to delegate to a non-failable init
init(value: Int) throws {
guard let component = Calendar.Component(rawValue: value) else {
throw DecodingError.unknownRawValue
}
self = component
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(Int.self)
try self.init(value: rawValue)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.rawValue)
}
}
您可以使Calendar.Component符合OptionSet协议。这将允许您使用单个值保存一组 Calendar.Components:
extension Calendar.Component: OptionSet, CaseIterable, Codable {
public init(rawValue: RawValue) {
switch rawValue {
case 1 << 0: self = .era
case 1 << 1: self = .year
case 1 << 2: self = .month
case 1 << 3: self = .day
case 1 << 4: self = .hour
case 1 << 5: self = .minute
case 1 << 6: self = .second
case 1 << 7: self = .weekday
case 1 << 8: self = .weekdayOrdinal
case 1 << 9: self = .quarter
case 1 << 10: self = .weekOfMonth
case 1 << 11: self = .weekOfYear
case 1 << 12: self = .yearForWeekOfYear
case 1 << 13: self = .nanosecond
case 1 << 14: self = .calendar
case 1 << 15: self = .timeZone
default: self = []
}
}
public var rawValue: Int {
switch self {
case .era: return 1 << 0
case .year: return 1 << 1
case .month: return 1 << 2
case .day: return 1 << 3
case .hour: return 1 << 4
case .minute: return 1 << 5
case .second: return 1 << 6
case .weekday: return 1 << 7
case .weekdayOrdinal: return 1 << 8
case .quarter: return 1 << 9
case .weekOfMonth: return 1 << 10
case .weekOfYear: return 1 << 11
case .yearForWeekOfYear: return 1 << 12
case .nanosecond: return 1 << 13
case .calendar: return 1 << 14
case .timeZone: return 1 << 15
}
}
public init() { self = [] }
public static let allCases: [Calendar.Component] = [
.era, .year, .month, .day, .hour, .minute, .second, .weekday, .weekdayOrdinal, .quarter, .weekOfMonth, .weekOfYear, .yearForWeekOfYear, .nanosecond, .calendar, .timeZone]
}
并实现 Set RawValue 初始值设定项和 rawValue 属性:
extension Set where Element: OptionSet & CaseIterable, Element.RawValue: FixedWidthInteger {
var rawValue: Element.RawValue {
var rawValue: Element.RawValue = .zero
for (index, element) in Element.allCases.enumerated() where contains(element) {
rawValue |= (1 << index)
}
return rawValue
}
init(rawValue: Element.RawValue) {
self.init()
for (index, element) in Element.allCases.enumerated()
where (rawValue & (1 << index)) != 0 {
insert(element)
}
}
}
用途:
let componentSet: Set<Calendar.Component> = [.year, .month, .day]
let rawValue = componentSet.rawValue // 14
let loadedSet: Set<Calendar.Component> = .init(rawValue: rawValue) // [month, day, year]