Home > Net >  Swift: Set protocol's associated type in argument / member variable
Swift: Set protocol's associated type in argument / member variable

Time:09-26

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
  • Related