0

I have a type called Setting that takes a generic type parameter as such:

Setting<T>

Every setting contains a value that can be an Int32, String, Bool, or a custom object type, etc. Here is some of the full implementation of Setting:

class Setting<T> {
    var key:String?
    var defaultValue:T?
    //...
}

This all works with various type params as expected, however, now there is a requirement for a collection that contains multiple Setting objects that could have various type parameters. When I declare an array variable of type [Setting], obviously the compiler expects a type which is unknown at runtime.

I've tried using a protocol and an extension on the types that could be used for the generic type parameter such as this:

protocol SettingProtocol {
    func getType() -> Self.Type
}

extension Int32:SettingProtocol {
    func getType() -> Int32.Type {
        return Int32.self
    }
}

extension String:SettingProtocol {
    func getType() -> String.Type {
        return String.self
    }
}
//...

and declaring my array as

var settings = [Setting<SettingProtocol>]()

but this does not work when I try to append a Setting instance to the array as follows:

var newSetting = Setting<String>()
newSetting.setDefaultValue(value: "SomeString")
settings?.append(newSetting) // compile error here

and results in the following compiler error:

Cannot convert value of type 'Setting<String>' to expected argument type 'Setting<SettingProtocol>'

Also, using the protocol/extension route might require an extension on every type that might be encountered when building these objects which seems really clunky.

I feel like there should be a way to accomplish this. Also hoping that when I pull these items out of the array that I can avoid a lot of type checking.

Can anyone offer any advice?

2 Answers 2

1

Change

class Setting<T> 

to

class Setting<T:SettingProtocol>

and try compiling.

Sign up to request clarification or add additional context in comments.

3 Comments

Won't resolve it. "Using 'SettingProtocol' as a concrete type conforming to protocol 'SettingProtocol' is not supported".
@AhmadF You're correct. I had to make my Setting class conform to a protocol, and now it works
@Pheepster What?! How is a valid answer? Would you mention what have you done to make it work?!
1

Actually, you can't define:

var settings = [Setting<SettingProtocol>]()

because the generic type of Setting must be one of the concrete types but not the protocol itself. For example, you could declare it as:

var settings = [Setting<String>]() // since you already implemented extension String:SettingProtocol { ...

Therefore you could append objects of type Setting<String>, however that's not what are you looking for, you need settings to be a heterogeneous container.

So what you could do is:

class Setting {
    var key:String?
    var defaultValue:SettingProtocol?
}

protocol SettingProtocol { }
extension Int32:SettingProtocol {}
extension String: SettingProtocol {}

At this point, you declared defaultValue to be of type SettingProtocol, without the need of dealing with a generic.

Therefore:

var newStringSetting = Setting()
newStringSetting.defaultValue = "My String"
settings.append(newStringSetting)

var newInt32Setting = Setting()
newInt32Setting.defaultValue = Int32(100)
settings.append(newInt32Setting)

for setting in settings {
    print(setting.defaultValue)
    // Optional("My String")
    // Optional(100)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.