In Swift, we can init a non-optional variable not immediately but later on in a if else block, for example:
let result: Bool
if something {
result = computeSomething()
} else {
result = computeSomethingElse()
}
But what if my variable is of protocol type? (in my example, I'd like to do this with a GraphQLMutation which is a protocol):
let mutation: GraphQLMutation
if something {
mutation = StartMutation()
} else {
mutation = StopMutation()
}
self.graphQLDataSource.set(mutation: mutation)
Swift compiler error says:
Protocol 'GraphQLMutation' can only be used as a generic constraint because it has Self or associated type requirements
Any idea to be able to do this and avoid code repetition?
It does work with protocols:
protocol Foo {}
struct A: Foo {}
class B: Foo {}
let x: Foo
if Bool.random() {
x = A()
} else {
x = B()
}
It just doesn't work with protocols that have an associated type. You can only use it in a generic function. Here's some code showcasing it:
protocol Foo {
associatedtype T
}
struct A: Foo {
typealias T = Int
}
class B: Foo {
typealias T = String
}
func x<Foo>(_ test: Bool) -> Foo? {
let x: Foo?
if test {
x = A() as? Foo
} else {
x = B() as? Foo
}
return x
}
let a1: A? = x(true) // => A
let a2: A? = x(false) // => nil
let b1: B? = x(true) // => nil
let b2: B? = x(false) // => B
For a1
we get an instance of A
as the cast A() as? Foo
worked because it has a type of Foo
with an associated type Int
required by the let a1: A?
.
For a2
we get nil as the cast B() as? Foo
fails because it cannot be cast into Foo
with an associated type Int
required by the let a2: A?
.
For b1
we get nil as the cast A() as? Foo
fails because it cannot be cast into Foo
with an associated type String
required by the let b1: B?
.
For b2
we get an instance of B
as the cast B() as? Foo
worked because it has a type of Foo
with an associated type String
required by the let b2: B?
.