sindresorhus / Defaults

💾 Swifty and modern UserDefaults
https://swiftpackageindex.com/sindresorhus/Defaults/documentation/defaults
MIT License
1.97k stars 117 forks source link

Defaults crashes on custom bridge with array of serializable values #101

Closed sindresorhus closed 1 year ago

sindresorhus commented 2 years ago
struct PlainHourMinuteTimeRange: Hashable, Codable {
    var start: PlainHourMinuteTime
    var end: PlainHourMinuteTime
}

extension PlainHourMinuteTimeRange: Defaults.Serializable {
    struct Bridge: Defaults.Bridge {
        typealias Value = PlainHourMinuteTimeRange
        typealias Serializable = [PlainHourMinuteTime]

        public func serialize(_ value: Value?) -> Serializable? {
            guard let value = value else {
                return nil
            }

            return [value.start, value.end]
        }

        public func deserialize(_ object: Serializable?) -> Value? {
            guard
                let array = object,
                let start = array[safe: 0],
                let end = array[safe: 1]
            else {
                return nil
            }

            return .init(start: start, end: end)
        }
    }

    static let bridge = Bridge()
}

struct PlainHourMinuteTime: Hashable, Codable, Defaults.Serializable {
    var hour: Int
    var minute: Int
}

extension Collection {
    subscript(safe index: Index) -> Element? {
        indices.contains(index) ? self[index] : nil
    }
}

Error:

022-05-06 15:55:56.549366+0700 Dato[75451:1229122] [User Defaults] Attempt to set a non-property-list object (
    "Dato.PlainHourMinuteTime(hour: 9, minute: 0)",
    "Dato.PlainHourMinuteTime(hour: 22, minute: 0)"
) as an NSUserDefaults/CFPreferences value for key hourlyChimeTimeRange

@hank121314 I think we may need to check whether the value is serializable in the array bridge and if so, use its serializer. I'm not sure though. I might be doing something wrong too. However, we should never let it hard crash.

hank121314 commented 2 years ago

By using the custom bridge, Defaults will only use this bridge to serialize. Type Serializable in custom bridge should be UserDefaults native supported type. But I think this might be a bit awkward.

I think we may need to check whether the value is serializable in the array bridge and if so, use its serializer.

This is a great way to improve this kind of situation. I will try to implement it 😃 !