This is pretty interesting. It appears that once you constrain the type of an associatedtype
in a given protocol, you need to provide a concrete type in the implementation of that protocol (instead of another protocol type) – which is why your second example worked.
If you remove the A
constraint on the associated type, your first example will work (minus the error about not being able to use weak
on a non-class type, but that doesn’t seem to be related).
That all being said, I can't seem to find any documentation in order to corroborate this. If anyone can find something to back this up with (or completely dispute it), I’d love to know!
To get your current code working, you can use generics. This will actually kill two birds with one stone, as both your code will now compile and you'll benefit from the increased type safety that generics bring (by inferring the type you pass to them).
For example:
protocol A : class {}
protocol B : A {}
protocol AnotherProtocol{
associatedtype Type : A
weak var type : Type? {get set}
}
class SomeClass<T:B> : AnotherProtocol {
typealias Type = T
weak var type : Type?
}
Edit: It appears the above solution won't work in your particular case, as you want to avoid using concrete types. I'll leave it here in case it's useful for anyone else.
In your specific case, you may be able to use a type erasure in order to create a pseudo concrete type for your B
protocol. Rob Napier has a great article about type erasures.
It's a bit of a weird solution in this case (as type erasures are normally used to wrap protocols with associatedtypes
), and it's also definitely less preferred than the above solution, as you have to re-implement a 'proxy' method for each method in your A
& B
protocols – but it should work for you.
For example:
protocol A:class {
func doSomethingInA() -> String
}
protocol B : A {
func doSomethingInB(foo:Int)
func doSomethingElseInB(foo:Int)->Int
}
// a pseudo concrete type to wrap a class that conforms to B,
// by storing the methods that it implements.
class AnyB:B {
// proxy method storage
private let _doSomethingInA:(Void)->String
private let _doSomethingInB:(Int)->Void
private let _doSomethingElseInB:(Int)->Int
// initialise proxy methods
init<Base:B>(_ base:Base) {
_doSomethingInA = base.doSomethingInA
_doSomethingInB = base.doSomethingInB
_doSomethingElseInB = base.doSomethingElseInB
}
// implement the proxy methods
func doSomethingInA() -> String {return _doSomethingInA()}
func doSomethingInB(foo: Int) {_doSomethingInB(foo)}
func doSomethingElseInB(foo: Int) -> Int {return _doSomethingElseInB(foo)}
}
protocol AnotherProtocol{
associatedtype Type:A
weak var type : Type? {get set}
}
class SomeClass : AnotherProtocol {
typealias Type = AnyB
weak var type : Type?
}
class AType:B {
// implement the methods here..
}
class AnotherType:B {
// implement the methods here..
}
// your SomeClass instance
let c = SomeClass()
// set it to an AType instance
c.type = AnyB(AType())
// set it to an AnotherType instance
c.type = AnyB(AnotherType())
// call your methods like normal
c.type?.doSomethingInA()
c.type?.doSomethingInB(5)
c.type?.doSomethingElseInB(4)
You can now use the AnyB
type in place of using the B
protocol type, without making it any more type restrictive.