Failing cast in Swift from Any? to protocol
This is pretty weird bug – it looks like it happens when an instance has been bridged to Obj-C by being boxed in a _SwiftValue
and is statically typed as Any(?)
. That instance then cannot be cast to a given protocol that it conforms to.
According to Joe Groff in the comments of the bug report you filed:
This is an instance of the general "runtime dynamic casting doesn't bridge if necessary to bridge to a protocol" bug. Since sender is seen as
_SwiftValue
object type, and we're trying to get to a protocol it doesn't conform to, we give up without also trying the bridged type.
A more minimal example would be:
protocol P {}
struct S : P {}
let s = S()
let val : Any = s as AnyObject // bridge to Obj-C as a _SwiftValue.
print(val as? P) // nil
Weirdly enough, first casting to AnyObject
and then casting to the protocol appears to work:
print(val as AnyObject as! P) // S()
So it appears that statically typing it as AnyObject
makes Swift also check the bridged type for protocol conformance, allowing the cast to succeed. The reasoning for this, as explained in another comment by Joe Groff, is:
The runtime has had a number of bugs where it only attempts certain conversions to one level of depth, but not after performing other conversions (so AnyObject -> bridge -> Protocol might work, but Any -> AnyObject -> bridge -> Protocol doesn't). It ought to work, at any rate.
Could not cast value of type - initialising with protocol (Swift)
Make variable presenter lazy or initialize it after viewcontroller initialization. This error might be because its trying to type cast viewcontroller object before getting initalized.
private lazy var presenter: WirelessSpeakersPresenter = {
let p = WirelessSpeakersPresenter(view: self as! WirelessSpeakersView)
return p
}()
How can you get the shared instance from AnyClass using a protocol in Swift?
So, I assume you have something like this
protocol SharedProvider {
static var shared: AnyObject { get }
}
class MySharedProvider: SharedProvider {
static var shared: AnyObject = MySharedProvider()
}
If you want to use AnyObject/AnyClass
func sharedInstanceForClass(_ aClass: AnyClass) -> AnyObject? {
return (aClass as? SharedProvider.Type)?.shared
}
Better approach
func sharedInstanceForClass<T: SharedProvider>(_ aClass: T.Type) -> AnyObject {
return T.shared
}
How can I best log a failed cast in Swift?
As you said, extending Any
isn't possible. But optionals hold whatever, and if you rely on them, you can get your logging and have very similar code to what you're already using.
extension Optional {
struct UnwrapError: Error { }
static func unwrap(_ any: Any) throws -> Wrapped {
guard let wrapped = any as? Wrapped else {
print("\(any) cannot be cast to \(Wrapped.self)")
throw UnwrapError()
}
return wrapped
}
}
try Double?.unwrap(actuallyADouble) // 3
let double: Double = try Optional.unwrap(actuallyADouble) // 3
try? String?.unwrap(actuallyADouble) // nil. "3.0 cannot be cast to String"
…or even just
extension Optional {
init(_ any: Any) {
self = any as? Wrapped
if self == nil {
print("\(any) cannot be cast to \(Wrapped.self)")
}
}
}
Double?(actuallyADouble) // 3
String?(actuallyADouble) // nil. "3.0 cannot be cast to String"
Downcast from Any to a protocol
Update: This has been fixed in Swift 1.2+ (Xcode 6.3+). The Xcode 6.3 beta release notes say:
Dynamic casts (“as!", “as?" and “is”) now work with Swift protocol
types, so long as they have no associated types.
You can only check for protocol conformance (which includes is
, as
, and as?
) with an @objc
protocol. Animal
is not @objc
.
See the Checking for Protocol Conformance section of the Swift book.
NOTE
You can check for protocol conformance only if your protocol is marked
with the @objc attribute
Related Topics
Segue and Button Programmatically Swift
Append Text or Data to Text File in Swift
How to Generate a Random Number in Swift Without Repeating the Previous Random Number
How to Get the Count of a Swift Enum
Dyld: Library Not Loaded: @Rpath/Libswift_Stdlib_Core.Dylib
Swift Protocol With "Where Self" Clause
How to Get a Reference to the App Delegate in Swift
How to Save and Read Array of Array in Nsuserdefaults in Swift
Swift Protocol Extension Method Is Called Instead of Method Implemented in Subclass
Biginteger Equivalent in Swift
What Are the New "For", "At", "In" Keywords in Swift3 Function Declarations
Swift Variable Decorations With "" (Question Mark) and "!" (Exclamation Mark)