Search code examples
swiftmacosenumsuserdefaults

Is it possible to extension existing enum to confirm Int?


In my app's Settings view, I'd like to let the user decide their favorite color scheme. But ColorScheme itself cannot be stored with UserDefaults, because it's not a basic data type. So I write a enum same as ColorScheme only for storage purpose. I hope to do something like extension ColorScheme: Int to make the ColorScheme itself storable to avoid the redundent enum. Can I do that, or smarter way to go?

enum Appearance: Int {
    case system
    case light
    case dark
    
    var colorScheme: ColorScheme? {
        switch self {
        case .light: .light
        case .dark: .dark
        default: nil
        }
    }
}

@AppStorage("appearance") var appearance: Appearance = .system

Picker("Appearance", selection: $appearance) {
    Text("System").tag(Appearance.system)
    Divider()
    Text("Light").tag(Appearance.light)
    Text("Dark").tag(Appearance.dark)
}

Solution

  • RawRepresentables with Int/String raw values can be stored with @AppStorage. You can retroactively conform ColorScheme to RawRepresentable.

    extension ColorScheme: RawRepresentable {
        public var rawValue: Int {
            switch self {
            case .light: 1
            case .dark: 2
            @unknown default: 0
            }
        }
        
        public init?(rawValue: Int) {
            switch rawValue {
            case 1: self = .light
            case 2: self = .dark
            default: return nil
            }
        }
    }
    
    @AppStorage("appearance") var appearance: ColorScheme?
    
    Picker("Appearance", selection: $appearance) {
        Text("System").tag(ColorScheme?.none)
        Divider()
        Text("Light").tag(ColorScheme?(.light))
        Text("Dark").tag(ColorScheme?(.dark))
    }
    

    That said, I'd still consider using your own Appearance enum, because there might be more cases added to the built-in ColorScheme in the future. If somehow a new color scheme is assigned to the @AppStorage property, you will get a nil when you try to retrieve it later.