Search code examples
swiftappstoragerawrepresentable

Storing ClosedRange<Float> via @AppStorage


Following this tutorial I would like to store ClosedRange<Float> using @AppStorage. I've extended ClosedRange as follows:

extension ClosedRange where Bound == Float {
    init?(rawValue: String) {
        let components = rawValue.split(separator: ",").compactMap { Float($0) }
        guard components.count == 2 else { return nil }
        self = components[0]...components[1]
    }

    var rawValue: String {
        return "\(lowerBound),\(upperBound)"
    }
}

In my view I would like to use @AppStorage in the following manner:

@AppStorage("range_slider_position", store: .group) var sliderPosition: ClosedRange<Float> = 20...80

Presently the code returns the error:

<view_definition_file...>.swift:16:3 No exact matches in call to initializer 

Candidate requires that the types 'ClosedRange<Float>' and 'Bool' be equivalent (requirement specified as 'Value' == 'Bool') (SwiftUI.AppStorage)
Candidate requires that the types 'ClosedRange<Float>' and 'Int' be equivalent (requirement specified as 'Value' == 'Int') (SwiftUI.AppStorage)
Candidate requires that the types 'ClosedRange<Float>' and 'Double' be equivalent (requirement specified as 'Value' == 'Double') (SwiftUI.AppStorage)
Candidate requires that the types 'ClosedRange<Float>' and 'String' be equivalent (requirement specified as 'Value' == 'String') (SwiftUI.AppStorage)
Candidate requires that the types 'ClosedRange<Float>' and 'URL' be equivalent (requirement specified as 'Value' == 'URL') (SwiftUI.AppStorage)
Candidate requires that the types 'ClosedRange<Float>' and 'Data' be equivalent (requirement specified as 'Value' == 'Data') (SwiftUI.AppStorage)

Solution

  • You forgot one of your tags, the extension must adopt RawRepresentable.
    And the protocol methods must be declared public

    extension ClosedRange : RawRepresentable where Bound == Float {
       public init?(rawValue: String) {
            let components = rawValue.split(separator: ",").compactMap { Float($0) }
            guard components.count == 2 else { return nil }
            self = components[0]...components[1]
        }
    
        public var rawValue: String {
            return "\(lowerBound),\(upperBound)"
        }
    }