Forced to cast, even if protocol requires given type
Adopting the protocol Fooable
tells the compiler that this particular UIViewController
responds to foo()
, no less no more.
In reverse conclusion Fooable
does not become UIViewController
necessarily.
The constraint Self: UIViewController
is just another information for the compiler to complain at compile time if the affected class is not UIViewController
In your case when annotating SampleViewController
to Fooable
the compiler knows only that SampleViewController
responds to foo()
. It does not know that the type is actually a subclass of UIViewController
.
So do not annotate a concrete class to a protocol if you want to access properties of the concrete class.
However you could add the show
method and other common properties / methods to the protocol
protocol Fooable: class where Self: UIViewController {
func foo()
func show(_ vc: Fooable, sender: Any?)
}
then you can use Fooable
because the compiler knows that the type adopting the protocol responds to the method.
A suitable practice to annotate a type to a protocol is for example when you are going to create a heterogenous but restricted collection type
let array : [CustomStringConvertible] = ["Foo", 1, false]
array.forEach{ print("\($0)")}
The code prints the three items using the description
property which all items respond to. The compiler recognizes the three items as types which have a description
property, not as String
, Int
and Bool
.
Update:
In Swift 5 support of superclass constrained protocols is implemented.
Forced to cast, even if protocol requires given type
Adopting the protocol Fooable
tells the compiler that this particular UIViewController
responds to foo()
, no less no more.
In reverse conclusion Fooable
does not become UIViewController
necessarily.
The constraint Self: UIViewController
is just another information for the compiler to complain at compile time if the affected class is not UIViewController
In your case when annotating SampleViewController
to Fooable
the compiler knows only that SampleViewController
responds to foo()
. It does not know that the type is actually a subclass of UIViewController
.
So do not annotate a concrete class to a protocol if you want to access properties of the concrete class.
However you could add the show
method and other common properties / methods to the protocol
protocol Fooable: class where Self: UIViewController {
func foo()
func show(_ vc: Fooable, sender: Any?)
}
then you can use Fooable
because the compiler knows that the type adopting the protocol responds to the method.
A suitable practice to annotate a type to a protocol is for example when you are going to create a heterogenous but restricted collection type
let array : [CustomStringConvertible] = ["Foo", 1, false]
array.forEach{ print("\($0)")}
The code prints the three items using the description
property which all items respond to. The compiler recognizes the three items as types which have a description
property, not as String
, Int
and Bool
.
Update:
In Swift 5 support of superclass constrained protocols is implemented.
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.
Protocol with where causes crash
Make your type a composite of UIViewController
and Displayable
.
For example:
private var displayable: (UIViewController & Displayable)!
Here is a link to some docs that contain relevant info on composite types:
https://docs.swift.org/swift-book/ReferenceManual/Types.html
Swift protocol with where Self clause
The ability to put superclass constraints on protocols declarations (that is, being able to define protocol P where Self : C
where C
is the type of a class) was a premature consequence of
SE-0156, and the syntax should have been rejected in Swift 4.x until the feature was implemented. Attempting to use this feature in Swift 4.x can cause miscompilation and crashes, so I would avoid using it until Swift 5.
In Swift 5 (Xcode 10.2) the feature has now been implemented. From the release notes:
Protocols can now constrain their conforming types to those that
subclass a given class. Two equivalent forms are supported:protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ }
Swift 4.2 accepted the second form, but it wasn’t fully implemented
and could sometimes crash at compile time or runtime. (SR-5581)
(38077232)
This syntax places a superclass constraint on MyView
which restricts conforming types to those inheriting from (or being) UIView
. In addition, the usage of MyView
is semantically equivalent to a class existential (e.g UIView & MyView
) in that you can access both members of the class and requirements of the protocol on the value.
For example, expanding upon the release notes' example:
protocol MyView : UIView {
var foo: Int { get }
}
class C : MyView {} // error: 'P' requires that 'C' inherit from 'UIView'
class CustomView : UIView, MyView {
var foo: Int = 0
}
// ...
let myView: MyView = CustomView(frame: .zero)
// We can access both `UIView` members on a `MyView` value
print(myView.backgroundColor as Any)
// ... and `MyView` members as usual.
print(myView.foo)
Clang: promoting warning for type-casting like idprotocol to classProtocol?
This is a natural consequence of the weak type system of Objective-C, where id
can be virtually downcasted to any kind of object. Specializing id
with a given protocol doesn't help much in this direction, this is why you can easily run into unexpected situations, like the current one.
If you want to force the casting, one solution would be to change the signature of getObject
to return NSObject
instead of id
:
static NSObject<CommonProtocol> *getObject(void) {
return [Bar new];
}
With this setup in place the naive Foo *casting = getObject();
will generate a clear warning:
Incompatible pointer types initializing 'Foo *' with an expression of type 'NSObject *'
, and the person writing the code will have to explicitly cast:
Foo *casting = (Foo *)getObject();
Related Topics
Swift 3 Incorrect String Interpolation With Implicitly Unwrapped Optionals
Alternative to Performselector in Swift
Difference Between a Weak Reference and an Unowned Reference
How to Use Special Character in Nsurl
Why Can't I Instantiate an Empty Array of a Nested Class
Binary Operator * Cannot Be Applied to Operands of Type Int and Double
Decrement Index in a Loop After Swift C-Style Loops Deprecated
How to Get Monday'S Date of the Current Week in Swift
How to Atomically Increment a Variable in Swift
How to Convert Data to Hex String in Swift
Nsobject Subclass in Swift: Hash VS Hashvalue, Isequal VS ==
String Value to Unsafepointer≪Uint8≫ Function Parameter Behavior
How to Convert Unix Epoch Time to Date and Time in iOS Swift
Changing the Status Bar Color For Specific Viewcontrollers Using Swift in Ios8