I'm having trouble with some Swift Optional Binding with a cast into a protocol. I have the following code in a playground that works fine.
protocol CodeCollection {
var name: String { get }
var codes: [String] { get }
}
struct VirtualDoors: CodeCollection {
var name = "Virtual Doors"
var codes: [String] = ["doorNumba1", "doorNumba2"]
}
// Instance of VirtualDoors
let doors = VirtualDoors()
// cast into Any? like what awake(withContext context: Any?) receives
var context = doors as Any?
print(context)
if let newDoors = context as? CodeCollection {
// Works as expected
print(newDoors)
}
I'm using the exact same protocol and struct in watchKit as a piece of info passed in awake(withContext context: Any?) and the optional binding with cast is failing there.
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Just checking to make sure the expected item is in fact being passed in
print(context)
// "Optional(VirtualDoors(name: "Virtual Doors", codes: ["doorNumba1", "doorNumba2"]))\n"
if let newDoors = context as? CodeCollection {
self.collection = newDoors
print("Context Casting Success")
} else {
// Casting always fails
print("Context Casting Fail")
}
}
I'd be really appreciative if someone could tell me why this is working in the playground but not in the watchKit class method.
I feel like I am missing something really obvious.
I suspect you doors to context as an implicit Any??, which only unwraps to another Optional instead of CodeCollection.
If you use let context = context as AnyObject inside the awake function, then should be able to unwrap it correctly.
Think of it like an force-unwraped optional that you don't get to see.
The last two comments of this playground should give others an example to play with where the optionals are kept, but the optional type is erased and wrapped.
import Foundation
protocol Target {
var name: String { get }
}
func takesAnyOptional(context: Any?) -> String? {
return (context as? Target)?.name
}
struct Source: Target {
let name = "Source"
}
let solid = Source()
print((solid as Target).name)
takesAnyOptional(context: solid)
let solid_any = solid as Any
print((solid_any as? Target)?.name)
takesAnyOptional(context: solid_any)
takesAnyOptional(context: solid_any as Any?)
let solid_anyOpt = solid as Any?
print((solid_anyOpt as? Target)?.name)
takesAnyOptional(context: solid_anyOpt)
takesAnyOptional(context: solid_anyOpt as Any) // -> double optional -> nil
let solid_anyOpt_any = solid_anyOpt as Any
takesAnyOptional(context: solid_anyOpt_any) // -> double optional -> nil
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With