In Swift I can explicitly set the type of a variable by declaring it as follows:
var object: TYPE_NAME
If we want to take it a step further and declare a variable that conforms to multiple protocols we can use the protocol
declarative:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
What if I would like to declare an object that conforms to one or more protocols and is also of a specific base class type? The Objective-C equivalent would look like this:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
In Swift I would expect it to look like this:
var object: TYPE_NAME,ProtocolOne//etc
This gives us the flexibility of being able to deal with the implementation of the base type as well as the added interface defined in the protocol.
Is there another more obvious way that I might be missing?
As an example, say I have a UITableViewCell
factory that is responsible for returning cells conforming to a protocol. We can easily setup a generic function that returns cells conforming to a protocol:
class CellFactory { class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T { //etc } }
later on I want to dequeue these cells whilst leveraging both the type and the protocol
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
This returns an error because a table view cell does not conform to the protocol...
I would like to be able to specify that cell is a UITableViewCell
and conforms to the MyProtocol
in the variable declaration?
If you are familiar with the Factory Pattern this would make sense in the context of being able to return objects of a particular class that implement a certain interface.
Just like in my example, sometimes we like to define interfaces that make sense when applied to a particular object. My example of the table view cell is one such justification.
Whilst the supplied type does not exactly conform to the mentioned interface, the object the factory returns does and so I would like the flexibility in interacting with both the base class type and the declared protocol interface
Declaring VariablesThe var keyword is the only way to declare a variable in Swift. The most common and concise use of the var keyword is to declare a variable and assign a value to it. Remember that we don't end this line of code with a semicolon.
You can declare multiple constants or multiple variables on a single line, separated by commas: var x = 0.0, y = 0.0, z = 0.0.
In swift, we use the let keyword to declare a constant variable, a constant is a variable that once declared, the value can not be changed.
In Swift 4 it is now possible to declare a variable that is a subclass of a type and implements one or more protocols at the same time.
var myVariable: MyClass & MyProtocol & MySecondProtocol
To do an optional variable:
var myVariable: (MyClass & MyProtocol & MySecondProtocol)?
or as the parameter of a method:
func shakeEm(controls: [UIControl & Shakeable]) {}
Apple announced this at WWDC 2017 in Session 402: Whats new in Swift
Second, I want to talk about composing classes and protocols. So, here I've introduced this shakable protocol for a UI element that can give a little shake effect to draw attention to itself. And I've gone ahead and extended some of the UIKit classes to actually provide this shake functionality. And now I want to write something that seems simple. I just want to write a function that takes a bunch of controls that are shakable and shakes the ones that are enabled to draw attention to them. What type can I write here in this array? It's actually frustrating and tricky. So, I could try to use a UI control. But not all UI controls are shakable in this game. I could try shakable, but not all shakables are UI controls. And there's actually no good way to represent this in Swift 3. Swift 4 introduces the notion of composing a class with any number of protocols.
You cannot declare variable like
var object:Base,protocol<ProtocolOne,ProtocolTwo> = ...
nor declare function return type like
func someFunc() -> Base,protocol<MyProtocol,Protocol2> { ... }
You can declare as a function parameter like this, but it's basically up-casting.
func someFunc<T:Base where T:protocol<MyProtocol1,MyProtocol2>>(val:T) { // here, `val` is guaranteed to be `Base` and conforms `MyProtocol` and `MyProtocol2` } class SubClass:BaseClass, MyProtocol1, MyProtocol2 { //... } let val = SubClass() someFunc(val)
As of now, all you can do is like:
class CellFactory { class func createCellForItem(item: SpecialItem) -> UITableViewCell { return ... // any UITableViewCell subclass } } let cell = CellFactory.createCellForItem(special) if let asProtocol = cell as? protocol<MyProtocol1,MyProtocol2> { asProtocol.protocolMethod() cell.cellMethod() }
With this, technically cell
is identical to asProtocol
.
But, as for compiler, cell
has interface of UITableViewCell
only, while asProtocol
has only protocols interface. So, when you want to call UITableViewCell
's methods, you have to use cell
variable. When you want to call protocols method, use asProtocol
variable.
If you are sure that cell conforms to protocols you don't have to use if let ... as? ... {}
. like:
let cell = CellFactory.createCellForItem(special) let asProtocol = cell as protocol<MyProtocol1,MyProtocol2>
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