I have a protocol SomeObjectFactory
whose method createSomeObjectWithConfiguration(_ config: SomeObjectConfiguration<T>)
is used inside the class Builder. When I tried to compile this code with Swift 5.7 I ran into an error
Member 'configWithExperience' cannot be used on value of type 'any configurationFactory'; consider using a generic constraint instead
Here is the implementation below
import Combine
import Foundation
final class SomeObject<T: Combine.Scheduler> {}
struct Experience {
let id: String
}
struct SomeObjectConfiguration<T: Combine.Scheduler> {
let scheduler: T
}
protocol SomeObjectFactory {
associatedtype T: Combine.Scheduler
func createSomeObjectWithConfiguration(_ config: SomeObjectConfiguration<T>) -> SomeObject<T>
}
protocol ConfigurationFactory {
associatedtype T: Combine.Scheduler
func configWithExperience(_ experience: Experience) -> SomeObjectConfiguration<T>
}
final class Builder<T: Combine.Scheduler> {
private let configurationFactory: any ConfigurationFactory
init(configurationFactory: any ConfigurationFactory) {
self.configurationFactory = configurationFactory
}
func createSomeObject(_ experience: Experience) {
let someObjectConfiguration: SomeObjectConfiguration<T> = configurationFactory.configWithExperience(experience)
}
}
I was hoping to create a someObjectConfiguration from the configurationFactory instance of the Builder.
CodePudding user response:
any ConfigurationFactory
really does mean, any ConfigurationFactory
. There's no guarantee the ConfigurationFactory.T
of that factory will be the same as the Builder.T
that contains it. You need to add a constraint that those two should match.
One way to do that is with a primary associated type, like so:
// Make `T` a primary associated type
// https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md
protocol ConfigurationFactory<T> {
-protocol ConfigurationFactory {
associatedtype T: Combine.Scheduler
func configWithExperience(_ experience: Experience) -> SomeObjectConfiguration<T>
}
final class Builder<T: Combine.Scheduler> {
// Constant the `T` of the factory to be the `T` of this builder
private let configurationFactory: any ConfigurationFactory<T>
- private let configurationFactory: any ConfigurationFactory
// Likewise, update the initializer to match
init(configurationFactory: any ConfigurationFactory<T>) {
- init(configurationFactory: any ConfigurationFactory) {
self.configurationFactory = configurationFactory
}
func createSomeObject(_ experience: Experience) {
let someObjectConfiguration: SomeObjectConfiguration<T> = configurationFactory.configWithExperience(experience)
}
}