Doghound
Doghound

Reputation: 139

Getting error with previously supported Optional type requirements on a function declaration (same-type, error in Swift 6)

This currently works, but in xcode-beta it is now showing a warning:

Same-type requirement makes generic parameter 'Value' non-generic; this is an error in Swift 6

func getUDValue<Value, R>(_ key: String, _ defaultValue: Value) -> Value
where Value == R?, R: RawRepresentable
{
    guard let rawValue = UserDefaults.standard.object(forKey: key) as? R.RawValue else {
        return defaultValue
    }
    return R(rawValue: rawValue) ?? defaultValue
}

I understand what it is saying, but trying to remove the Value == R?, R: RawRepresentable has proven difficult as the R is needed to access the RawValue and init(rawValue:) behind the Optional<RawRepresentable>

Interestingly enough, I got this structure from Apple's own AppStorage initializer for optional RawRepresentables: https://developer.apple.com/documentation/swiftui/appstorage/init(_:store:)-6aj8x

If this is soon to be an error, does anyone know how to accomplish this in the new world?

Update:

My current solution was to move these methods into a struct that provides the generic label. See here:

private struct UserDefaultsWrapper<Value> {
    static nonisolated func getValue(_ key: String, _ defaultValue: Value) -> Value
    where Value: RawRepresentable
    {
        guard let rawValue = UserDefaults.standard.object(forKey: key) as? Value.RawValue else {
            return defaultValue
        }
        return Value(rawValue: rawValue) ?? defaultValue
    }

    static nonisolated func getValue<R>(_ key: String, _ defaultValue: Value) -> Value
    where Value == R?, R: RawRepresentable
    {
        guard let rawValue = UserDefaults.standard.object(forKey: key) as? R.RawValue else {
            return defaultValue
        }
        return R(rawValue: rawValue) ?? defaultValue
    }

    static nonisolated func getValue(_ key: String, _ defaultValue: Value) -> Value
    where Value: UserDefaultsPropertyListValue
    {
        return UserDefaults.standard.object(forKey: key) as? Value ?? defaultValue
    }

    static nonisolated func getValue<R>(_ key: String, _ defaultValue: Value) -> Value
    where Value == R?, R: UserDefaultsPropertyListValue
    {
        return UserDefaults.standard.object(forKey: key) as? R ?? defaultValue
    }

    static nonisolated func setValue(_ key: String, _ newValue: Value)
    where Value: RawRepresentable
    {
        UserDefaults.standard.set(newValue.rawValue, forKey: key)
    }

    static nonisolated func setValue<R>(_ key: String, _ newValue: Value)
    where Value == R?, R: RawRepresentable
    {
        UserDefaults.standard.set(newValue?.rawValue, forKey: key)
    }

    static nonisolated func setValue(_ key: String, _ newValue: Value)
    where Value: UserDefaultsPropertyListValue
    {
        UserDefaults.standard.set(newValue, forKey: key)
    }

    static nonisolated func setValue<R>(_ key: String, _ newValue: Value)
    where Value == R?, R: UserDefaultsPropertyListValue
    {
        UserDefaults.standard.set(newValue, forKey: key)
    }
}

UserDefaultsPropertyListValue is a protocol used to extend supported non-RawRepresentable types, e.g. Int, Bool, String, etc.

Upvotes: 1

Views: 87

Answers (1)

lorem ipsum
lorem ipsum

Reputation: 29242

I think Value is redundant in this case.

func getUDValue<R>(_ key: String, _ defaultValue: R) -> R
where R: RawRepresentable
{
    guard let rawValue = UserDefaults.standard.object(forKey: key) as? R.RawValue else {
        return defaultValue
    }
    return R(rawValue: rawValue) ?? defaultValue
}

You can just remove it and have the type be declared by the resulting property.

let res:Test23? = getUDValue("test", .test)

or

let res:Test23 = getUDValue("test", .test)

Upvotes: 1

Related Questions