š Swift Talk: Limiting the use of Protocols
TL;DR Practice like itās real. So when itās real, itās just like practice
Product first
Swift니까 무조건 프로토콜을 써야 할까요? 🤔 프로토콜을 제대로 사용하는 방법을 알려드립니다!
Realm Koreaģ ģķ“ ź²ģ ėØ 2017ė 7ģ 24ģ¼ ģģģ¼
Credits to these awesome photos go to @gbmksquare
The Talk
The topic of the talk wasā¦
Using protocols everywhere in Swift is not a good thing.
Protocols?
To see where I am trying to go with this, we first need to know what a protocol is. There are tons of tutorials online about protocols so I will keep it simple.
Object Oriented | ā Protocol Oriented |
---|---|
Basically, protocols allow you to go from the left to the right and thatās considered a good thing.
Flatter architecture made possible with the concept of composition is easier to deal with than a complex class hiearchy.
Whatās so bad then?
Letās look at some examples.
protocol URLStringConvertible {
var urlString: String { get }
}
// ...
func sendRequest(urlString: URLStringConvertible, method: () -> ()) {
let string = urlString.urlString
}
This URLStringConvertible
is not accomplishing anything here and can simply be replaced by a value.
But somehow, I think a lot people feel as if using protocols for everything is the right thing to do in Swift. Maybe this is because of the large number of āProtocol Oriented Xā tutorials on the web or maybe because Apple has been trying to sell Swift as a āProtocol Oriented Languageā. But whatever the reason,
Using protocols without thinking about the consequences is NOT ideal
More Examples
Suppose we are making a library that is good at creating UIView
elements with data inserted in them.
Iām going to completely ignore what I just said above and make a protocol first because thatās the ācool thing to doā.
Protocol Oriented Approach
protocol HeaderViewProtocol {
associatedtype Content
func setHeader(data: Content)
}
That looks like a cool protocol that will make us look like we know what we are doing. Letās now apply this protocol to various UIView subclassess to build up our library.
class MyLabel: UILabel, HeaderViewProtocol {
func setHeader(data: String) {
self.text = data
}
}
class MyButton: UIButton, HeaderViewProtocol {
func setHeader(data: String) {
self.titleLabel?.text = data
}
}
Simple enough. I just successfully abstracted the idea of a class that can be used as a HeaderView
with a single protocol.
Now, I want to make an array of HeaderViewProtocol
elements so that I can later insert them into a UICollectionView
.
let elements = [MyLabel(), MyButton(), UIStackView()]
Wait.. UIStackView
isnāt a HeaderViewProtocol
but why does the compiler not raise any errors?
If you look at elements
type, the Swift compiler tells us that itās just a [UIView]
and not an array of UIView
s that conform to HeaderViewProtocol
.
You could go ahead and try things like
let elements : [HeaderViewProtocol] = [MyLabel(), MyButton(), UIStackView()]
let elements : [UIView<HeaderViewProtocol>] = [MyLabel(), MyButton(), UIStackView()]
but nothing works because there is not a way to express a class that conforms to a protocol type in Swift 3.
We could have done UIView<HeaderViewProtocol>
in Objective-C but thatās a whole different story.
Note: turns out you can do this in Swift 4 with class subtype existentials but this doesnāt really change anything
let elements: [HeaderViewProtocol & UIView]
So in Swift 3, we need to go ahead and create a type eraser like this.
struct AnyHeaderView<Content>: HeaderViewProtocol {
var _setHeader: (Content) -> ()
init<T: HeaderViewProtocol>(_ view: T) where Content == T.Content {
_setHeader = view.setHeader
}
func setHeader(data: Content) {
return _setHeader(data)
}
}
Ok, thatās a lot of code to keep the compiler happy. Anyway, we can now go ahead make the array with type safety we want.
let elements = [AnyHeaderView(MyLabel()), AnyHeaderView(MyButton())]
Value Oriented Approach
But instead of passing in a type that promises things, couldnāt we just pass the things we promised?
Read that again and think about that for a second.
And to do just that, letās make a type that can wrap all the things we promised into a struct.
struct HeaderView<T>{
let view: UIView
let setHeader: (T) -> ()
}
Now, we can do thisā¦
let label = UILabel()
let labelHeader = HeaderView<String>(view: label) { str in
label.text = str
}
let imageView = UIImageView()
let imageHeader = HeaderView<UIImage>(view: imageView ) { img in
imageView.image = img
}
Using a struct instead of a protocol halved the code size. Once implemented, the solution seems almost too simple to be true that we wonder, āWhy couldnāt we think of this the first time?ā The reason maybe because we came up with a solution to a problem we didnāt even try tounderstand.
Pick the right tool for the job, not the other way around.
Conclusion
If using protocols in your code is making you write unncessary code to keep the compiler quiet and satisfied, maybe you should consider using struct / function values instead.
Hereās a general rule of thumb.
Situation | Solution |
---|---|
1 function in protocol? | Function |
1 > functions? | Protocol |
Used only once? (completion handlers, callbacks) | Function |
Used a lot? (data source, delegate) | Protocol |
About the talk
This was my first ever programming talk. My content was very techinal and the talk had some live coding in the middle. So naturally, I was nervous.
The talk was held in Korea, at one of Kakaoās buildings. I had just served 2 years in the Army and also had just come back from the ISC that was held in Germany for a week.
My flight landed the day before the conference so I was sleep deprived and so tired that I couldnāt really say all the things I had in my head. I was not prepared for this and maybe I was almost arrogant to think that I would be able to pull this off.
Oh well, at least I learned something from this. At least I wonāt make the same mistake for my next talk! š¤¦āāļø
« Globalizing Software II
Transducers in Swift »