Protocol extension vs class extension in Swift - ios

Assume there is a protocol Draggable, usually will be conformed by an UIView object
protocol Draggable {
drag()
}
We can either implement drag() in a protocol extension as option 1
// option 1
extension Draggable where Self: UIView {
func drag() {
// implementation
}
}
extension UIView: Draggable {} // added after #Rich Tolley's answer
Or we can implement drag() in a UIView extension as option 2
// option 2
extension UIView: Draggable {
func drag() {
// implementation
}
}
Here is my question:
Is there a difference between these two approaches (option 1 & option 2) ?
If yes, what's the difference and how to choose when we design a our project or library ?
And idea would be helpful.

Yes, there is a difference: (EDIT: or at least there was in the original version of this q, which didn't add extension UIView : Draggable {} to the end of option 1).
Option 1 creates a default implementation for instances of UIView that conform to Draggable. You still need to mark UIViews you wish to conform to Draggable as such in the declaration: class MyView : Draggable. Anything that conforms to Draggable but is not a UIView subclass will need to supply its own implementation.
Option 2 extends all UIViews to make them conform to Draggable. Nothing else can be a Draggable unless separate extensions are also written for those classes, or they are manually conformed to the protocol. There is no need to add Draggable in the class declaration.
The protocol extension is usually the better option. In this case this is obviously true since not all UIViews can be Draggable. Also, going down the protocol extension route means that you can create a Draggable object that is not a UIView subclass, if necessary (admittedly fairly unlikely, since most Cocoa controls are UIView subclasses - although not all -UIBarButtonItem isn't, strangely)
If you follow option 2, you will be adding unnecessary methods to UIView in a lot of cases, which is a violation of good object oriented design - specifically the Interface Segregation Principle (clients should not be forced to rely on methods they don't use
) - which is the 'I' in the SOLID principles

A protocol extension should be used when you want to implement functionality for more than just one class.
In this case you should use the extension UIView: Draggable as the Implementation is specific to the UIView class.
Assuming you have a protocol which provides location:
protocol Location {
var location: CGPoint { get set }
}
and you want every class which implements Location to conform to Draggable, then a protocol extension could be used:
extension Draggable where Self: Location {
func drag() {
}
}
For further reference, you should have a look at Protocol-Oriented Programming in Swift from the 2015 WWDC.

Related

Swift Protocol conform to two specific classes only

I want to make a protocol which can only be conformed by UIlabels and UIButton, is there a way ?
Example :
protocol MyProtocol {
func setTextValue()
}
I want MyProtocol to only be acessed by UILabel and UIButton and no other class
I want to make a protocol which can only be conformed by UIlabels and UIButton, is there a way ?
Which can only be? Not directly, no. You cannot impose upon another programmer a contract such that programmer cannot make some other class adopt your protocol (e.g. forcing the compiler to complain if that happens).
But perhaps you mean which is only “conformed” (i.e. adopted). In that case, certainly. Nothing prevents you from declaring a protocol and then declaring, yourself, that UILabel and UIButton do adopt it (and not declaring that any other classes adopt it). Just do it.
An extension may be a better suite for this, especially in swift, depending on what you want you want to do.
extension UILabel {
func greenBackground() {
self.backgroundColor = .green
}
}
Then you can use it anywhere in your project on any UILabel like this:
let label = UILabel()
label.greenBackground()

Swift Advanced - Setting extensions during run time

Is there a way to set extensions during run time in Swift?
I got a protocol named "CuteProtocol" and an extension to that protocol named "CuteExtension". When I want to add this extension to classes I just do as follows:
class CuteClass: UIViewController, CuteProtocol {
}
However I have many of these classes which should implement this protocol and I don't want to add them one by one, I don't want to make a base class either.
Is there a way to set extensions-protocols during run time as follows:
let cuteClass = CuteClass()
cuteClass. // ADD EXTENSION-PROTOCOL SOMEHOW HERE.
No, but you can extension for example UIViewController, or other base class
extension UIViewController: CuteProtocol {
// your code conforming to Cute Protocol goes here.
}
you can play with runtime modifiers in ObjC freely, but in Swift such kinda pattern is not really common.
NOTE: you can find more information about this in the ObjC Runtime Library, if you are interested.
You can not set extensions in runtime, BUT you don't have to set them in class definition either. you can extend classes like:
extension CuteClass: CuteProtocol {
// your code conforming to Cute Protocol goes here.
}
Update 1
Or if you want to have default implementations to CuteProtocol, you can extend CuteProtocol itself too:
protocol CuteProtocol {
func f1()
}
extension CuteProtocol {
func f1() {
// default implementation
}
}
This way each class can change f1 implementation if want to or use the default implementation.
You can even add conditional extensions like:
extension CuteProtocol where Self: CuteClass {
func f1() { }
func f2() { }
}
so if you write extension CuteClass: CuteProtocol {} all CuteClass instances and subclasses will have access to method f1 and f2.
But be-aware that functions added in extensions support dynamic-dispatch IFF they're defined in protocol.
In the sample I provided, f1 will be called with dynamic-dispatch but f2 will be called with static-dispatch. i.e: if CuteChildClass: CuteClass changes f2 implementation and you call f2 from a CuteProtocol variable, the code you provided in the extension will be called.

Subclass/Extend UIView so all other Views (UIImageView, UITextField ...) inherit upon creation

what is the suggested approach when I want to add a functionality to UIView so all views inside my app get those? As a matter of fact I need to add some stored properties too so an Extension is not possible. Since I need to deal with Textfields, ImageViews, Views (and who knows what else will come) I dont want to subclass every each of the too add that functionality, so the goal would be to make a subclass of UIView and all my controls (if its possible) get that functionality out of the box.
With an extension it would be easy, but as I said, I need to store some stuff too, so is this goal achievable with a subclass? Or what would be the right approach (maybe there is a third option)
Thanks
Why don't you define a protocol and provide default implementations in the protocol extension, then have UIView conform to that protocol? Here is an example:
protocol MyProto {
var someVar: Bool { get set }
func someFunc() -> Void
}
extension MyProto {
var someVar: Bool {
get {
// provide default implementation
return true
}
set {
}
}
func someFunc() -> Void {
// provide common implementation
}
}
extension UIView: MyProto {}
You can also use the where clause to constrain the default behaviour for a type.
extension MyProto where Self: UIControl {
var someVar: Bool {
get {
return isUserInteractionEnabled
}
set {
isUserInteractionEnabled = newValue
}
}
}
extension MyProto where Self: UITextField {
var someVar: Bool {
get {
return isFirstResponder
}
set {
newValue ? becomeFirstResponder() : resignFirstResponder()
}
}
}
TLDR; You can't do this and you will need to subclass each UI element that you want to introduce new properties to.
You can't do this (without access to the source code) as you would effectively be changing the class inheritance tree by injecting your own class between UIView and its subclasses.
Consider the implications if a language allowed this:
Class A defines a property, a
Class Binherits from Class A and defines a property b, which is fine because Class A does not have this property.
Class C inherits from Class B and has both a and b properties.
Now, what could happen if you could 'inject' Class A1 somehow 'below' Class A?
Class A1 could define a property, b, which is fine because Class A does not have this property
Class B now has a problem though, because its b clashes with the superclass b
Class C has a multiple-inheritance diamond-problem with property b
Of course, you only intend to add properties that don't clash (although you can't know this because you don't know of all possible subclass implementations) and don't need the subclasses to access your property, so the multiple inheritance
isn't an issue, but if such a feature were in a language, these potential issues would need to be addressed because you can't rely on everyone having the same intentions as you.

Swift property conforming with multiple protocols

I have custom UIView (CustomView) conforming to two different protocols
protocol ResizableDelegate: class {
func view(view:UIView, didChangeHeight difference:CGFloat)
}
protocol Resizable: class {
var delegate:ResizableDelegate? { set get }
}
protocol TappableDelegate: class {
func viewDidTap(view:UIView)
}
protocol Tappable {
var delegate:TappableDelegate? { set get }
}
And I need to have a property in my CustomView class named delegate and conforming to these two protocols at the same time. I read Types conforming to multiple protocols in swift but that is not solving my problem.
I created this protocol
protocol CustomViewDelegate: ResizableDelegate, TappableDelegate {}
And then make my CustomView
class CustomView : UIView, Resizable, Tappable {
var delegate:CustomViewDelegate?
}
But that is causing me to get a message
Type 'CustomView' does not conform to protocol 'Resizable'
I don't want to have:
class CustomView : UIView, Resizable, Tappable {
var resizableDelegate:ResizableDelegate?
var TappableDelegate:TappableDelegate?
}
Is there any way two have only one delegate property that conforms to these two protocols at the same time? Im using swift 2.0, Xcode 7.
I guess you don't really need to declare Resizable and Tappable protocols. All you need is to delegate from your custom view to some other object, which confirms to both ResizableDelegate and TappableDelegate, right? If so, this should work for you:
protocol ResizableDelegate: class {
func view(view:UIView, didChangeHeight difference:CGFloat)
}
protocol TappableDelegate: class {
func viewDidTap(view:UIView)
}
class CustomView : UIView {
var delegate: (ResizableDelegate, TappableDelegate)?
}
Although you can leave things as is, I would strongly recommend changing the required properties to "tappableDelegate" and "resizableDelegate" thus having two separate properties in your View subclass.
Your specific use case may require adherence to both, but having the same naming means you would not be able to have different delegates.

Swift - Same protocol for two different classes

I want to use the same protocol for two different classes. It is for two UIStoryboardSegue classes, the normal one and the unwind segue. In my first class GameSegue.swift, I've declared this protocol
#objc protocol ViewControllerWithBackgroundImage {
var backgroundImage: UIImageView { set get }
}
I use this protocol to have access to the ViewControllers property backgroundImage. In the first class GameSegue.swift, the normal segue, the backgroundImage animates 10 px up. So in the second class GameSegueUnwind.swift, I want to do the same thing backwards, move the background 10 pxdown. But to get access to the backgroundImage property I need this protocol. Therefore it would be useful, to not declare another protocol, but instead use the same.
Any idea how this is possible?
In the second class just declare a new delegate variable
class GameSegueUnwind {
var secondDelegate: ViewControllerWithBackgroundImage?
}
and you will be able to access the function in any other class that conforms to the protocol. Of course, in the conforming class remember to declare it has the delegate handler in the prepare for segue method
destinatonViewController.secondDelegate = self

Resources