Can I cast a metaclass object to a protocol type in Swift?
As of Xcode 7 beta 2 and Swift 2 it has been fixed. You can now write:
for type in types {
if let fooType = type as? Foo.Type {
// in Swift 2 you have to explicitly call the initializer of metatypes
let obj = fooType.init(foo: "special snowflake string")
}
}
Or if you only want type
as type Foo.Type
you can use for case
for case let type as Foo.Type in types {
let obj = type.init(foo: "special snowflake string")
}
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.
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
}()
Instantiate class from protocol type
I usually do that by defining a generic method. Try this:
class func createClass<T: CreatableClass>(classType: T.Type) -> CreatableClass {
return classType()
}
Update
A possible workaround is to pass a closure creating a class instance, rather than passing its type:
class ClassCreator {
class func createClass(instantiator: () -> CreatableClass) -> (CreatableClass, CreatableClass.Type) {
let instance = instantiator()
let classType = instance.dynamicType
return (instance, classType)
}
}
let ret = ClassCreator.createClass { ExampleClass() }
The advantage in this case is that you can store the closure in a dictionary for example, and create more instances on demand by just knowing the key (which is something in 1:1 relationship with the class name).
I used that method in a tiny dependency injection framework I developed months ago, which I realized it works only for @objc-compatible classes only though, making it not usable for my needs...
Type cast an instance of a class
Type cast operator (as?
or as!
) is compile time operator. Type of any variable in Swift can't be dynamic as you intend to achieve with your cast.
You can't easily avoid if let
, guard
etc. In your your case you can do an explicit cast (as!
), if you know that view controller at particular index never changes. But it is not recommended approach, usually you should tend to avoid using explicit casting.
Related Topics
Getting an Issue with Upgrade to Xcode 10.2
Deleting a Camera Roll Asset Using Photos Framework
Using Vapor-Fluent to Upsert Models
Assemble a List of Users with Geofire/Firebase
Correct Way to Call "Realloc" in Swift with a Float Array
Increment Integer in Nsuserdefaults
Deleting with One-To-Many Relationship
How Do We Implement Wait/Notify in Swift
New Fuitableviewdatasource - How to Use? Swift 3
How to Prevent a Spacer to Make a VStack Greedly Grow Beyond Necessary
How to Get the List of Open Windows on MACos in Swift
Compiler Cannot Infer Return Type