【问题标题】:Is there a way to store Set<Calendar.Component>?有没有办法存储 Set<Calendar.Component>?
【发布时间】:2020-11-24 08:11:28
【问题描述】:

我在 Swift 上有一个相当古老的项目,它仍然使用 NSCalendar.Unit,它非常方便,因为它的 rawValue 是一个 UInt,可以存储在我的 Core Data 数据库中。我想迁移到更新的 Swift API,所以我必须使用 Set&lt;Calendar.Component&gt;,但似乎 Calendar.Component 只是一个没有 rawValue 的 Swift 枚举。我发现它有一个hashValue,但是Apple不建议保存它,而且Calendar.Component不符合Encodable。那么有什么方法可以将 Set&lt;Calendar.Component&gt; 存储在 Core Data 中,还是我坚持使用旧的 NSCalendar.Unit

【问题讨论】:

    标签: swift core-data calendar storage foundation


    【解决方案1】:

    您可以使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)
        }
    }
    

    【讨论】:

      【解决方案2】:

      您可以使 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]
      

      【讨论】:

        猜你喜欢
        • 2017-03-21
        • 2015-04-22
        • 2019-09-08
        • 2019-12-08
        • 2014-02-27
        • 2017-12-14
        • 1970-01-01
        • 2011-06-22
        • 1970-01-01
        相关资源
        最近更新 更多