0xDEADBEEF

Home Projects AboutRSS

šŸ”ˆ Swift Talk: Limiting the use of Protocols

  • swift
  • talk

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 UIViews 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! šŸ¤¦ā€ā™‚ļø