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
Downcasting on protocols in Swift
Protocol can be adopted by a struct or enum so it don't do class polymorphism, because they can not be inherited. This is also one of the reasons that why associatedtype
exists.
Back to your question, you defined a variable with the same name as the parent protocol in the sub-protocol, but the type is different, this is allowed because the protocol is not a concrete type. But this is not a type overload, because the protocol works differently from the class.
In this case, you'd better use generics, which is associatedtype
. But if you have to use these three protocols, you only need to declare the sub-protocol.
...
protocol ComponentA: Component {}
protocol ComponentB: Component {}
struct SomeComponentOfTypeA: ComponentA {
var step: Step = StepA.one
}
struct SomeComponentOfTypeB: ComponentB {
var step: Step = StepB.two
}
let a = SomeComponentOfTypeA()
print(a.step)
print(type(of: a))
let b = SomeComponentOfTypeB()
print(b.step)
print(type(of: b))
Output:
one
SomeComponentOfTypeA
two
SomeComponentOfTypeB
Another way is to meet your needs. Limit SomeComponentOfTypeA
only allows StepA
type.
protocol StepAProtocol { /* body */ }
protocol StepBProtocol { /* body */ }
enum StepA: Int, CaseIterable, StepAProtocol {
case one
case two
case three
}
enum StepB: Int, CaseIterable, StepBProtocol {
case one
case two
case three
}
protocol Component {
associatedtype T
var step: T { get }
}
protocol ComponentA: Component where T: StepAProtocol {}
protocol ComponentB: Component where T: StepBProtocol {}
struct SomeComponentOfTypeA: ComponentA {
var step: StepA = .one
}
struct SomeComponentOfTypeB: ComponentB {
var step: StepB = .two
}
struct SomeComponentOfTypeC: ComponentB {
var step: StepA = .two // error: Type 'StepA' does not conform to protocol 'StepBProtocol'
}
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.
Swift AnyObject - Down casting an array of protocols to [AnyObject]
It's less than perfect, but it works if the protocol and class are both @objc
(and the class subclasses NSObject
):
@objc protocol Nameable: class {
var name: String { get }
}
@objc class Person: NSObject, Nameable {
var name: String
init(name: String) {
self.name = name
}
}
...
var array: [Nameable] = [personOne, personTwo]
let array2 = array as [AnyObject] // ✓
Downcast Generic AnyObject to Protocol Associated Type Self.Model
What you are trying to do is not possible. This is because of the strong type safety system in Swift.
It looks like you want to restrict the configuration
to the associated type Model
, but you also want to provide a default configuration. The issue here is that your default configuration has no guarantee that its generic type will be the same as the type of Model
.
The Swift type system won't let you pass <AnyObject>
as type <Model>
, as it has no way to ensure that the object actually IS of the Model
type.
When you make something conform to Configurable
and declare the type of the Model
, you are saying, "My configuration must use the type I've given." The defaultConfiguration
can't guarantee that, so it cannot be used in this way.
From looking at your code, you are using this to determine the type of the responseSerializer
to use. But, if you are using different Configurable
objects, they will all need different responseSerializer
s, so they can't all use the default configuration.
I've spent a lot of time thinking about this problem, and I don't see any way around it. You are going to have to change your design in some way.
If you move the responseSerializer
on to the Configurable
protocol, then you could make the Configuration
non-generic. In that case, you would be able to create the responseSerializer
in a protocol extension on Configurable
. If you need to use the responseSerializer
with a configuration independently of a Configurable
object, then you would have to create a responseSerializer<AnyObject, NSError>
wherever it is that you are using it. I'm not familiar with the entire design and intent of your library, so I'm not sure if this will work for what you are trying to achieve.
The one thing I can tell you with certainty is that your design has to change in order to use the defaultConfiguration
.
Cannot downcast from ... to a more optional type
You had mistake in if let
syntax. Instead of force cast you want to have optional cast. And since you already have this array you don't want get optional array of pokemons
if let pokemonsList = objects as? [Pokemon] {
self.pokemons = pokemonsList
}
Related Topics
Disable Warning Dialog If Bluetooth Is Powered Off iOS
Making Phone Calls on the Iphone
Gcd - Main VS Background Thread for Updating a Uiimageview
Uilabel, Uifont and Utf-8 Triangle
How to Convert Ciimage to Uiimage in Swift 3.0
iOS Ibeacon: How to Get All of Proximityuuid Programmatically
Custom Uitableviewcell with Progress Bar Download Update
Duet - Merge 2 Videos Side by Side
Remove Underline on Uibutton in iOS 7
Insert String at Cursor Position of Uitextfield
Taking Screenshots in the Background (Ios) - Improving Performance
How to Emulate Nfc Cards in iOS 13
Error Using Swift - Instance Member Cannot Be Used on Type 'Viewcontroller'
Capture Location in All States App
Fbsdkaccesstoken Currentaccesstoken Is Not Being Updated After Log In
iOS - Using Uisearchdisplaycontroller with Uisearchbar That Is Uibarbuttonitem in Uitoolbar