I'm trying to create a generic protocol that can be reused in various parts throughout my application. Previously I've hardcoded KeyValueStore
to only use String
and Int
for Key
and Value
respectively. I ran into an issue where I want a different consumer, class ConsumerB
, to use KeyValueStore
with Key
/Value
being Int
/Int
respectively.
protocol KeyValueStore {
associatedtype Key
associatedtype Value
func get(key: Key) -> Value
func set(key: Key, value: Value)
}
For the original implementation, I had two classes the conform to the protocol. UserDefaultsStore
was used to store data during runtime and MemoryKeyValueStore
was used in unit testing.
class UserDefaultsStore: KeyValueStore {
func get(key: String) -> Int { return 0
// Implementation
}
func set(key: String, value: Int) {
// Implementation
}
}
class MemoryKeyValueStore: KeyValueStore {
var values: [String: Int] = [:]
func get(key: String) -> Int { return values[key]! }
func set(key: String, value: Int) { values[key] = value}
}
Since they both used KeyValueStore
with hardcoded types I could provide a store implementation at runtime.
class ConsumerA {
var keyValueStore: KeyValueStore
init(store: KeyValueStore) {
keyValueStore = store
}
func example() {
keyValueStore.set(key: "Hello", value: 5)
}
}
After adding the associated types, I get the error below. It makes sense that while compiling ConsumerA
, there is no way to know what type Key
and Value
take on in the KeyValueStore
argument and member variable. However after poking around, I could not seem to find a clear answer on how to properly declare what types Key
and Value
should be in the ConsumerA
class.
In the example
function, it is clear it would use String
and Int
, but how do I set the argument/member variable to only allow classes that conform to KeyValueStore
and have Key
/Value
set to String
/Int
?
Protocol 'KeyValueStore' can only be used as a generic constraint because it has Self or associated type requirements
CodePudding user response:
ConsumerA
needs to be generic. Replace ModuleName
with your actual module name.
class ConsumerA<KeyValueStore: ModuleName.KeyValueStore>
where KeyValueStore.Key == String, KeyValueStore.Value == Int {
Also, your method pair should be a subscript instead.
subscript(key: Key) -> Value { get set }
keyValueStore["Hello"] = 5