Есть ли способ хранить Сет ? - PullRequest
0 голосов
/ 04 августа 2020

У меня есть довольно старый проект на Swift, который все еще использует NSCalendar.Unit, и это довольно удобно, потому что его rawValue - это UInt, который можно сохранить в моей базе данных Core Data. Я хочу перейти на более новые API Swift, поэтому мне нужно использовать Set<Calendar.Component>, но похоже, что Calendar.Component - это просто перечисление Swift без rawValue. Я обнаружил, что он имеет hashValue, но Apple не рекомендует его сохранять, а также Calendar.Component не соответствует Encodable. Так есть ли способ сохранить Set<Calendar.Component> в Core Data, или я застрял со старым NSCalendar.Unit?

введите описание изображения здесь

Ответы [ 2 ]

2 голосов
/ 04 августа 2020

Вы можете сделать 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)
    }
}
0 голосов
/ 04 августа 2020

Вы можете сделать 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]
...