What?
Swift 2 brought with itself protocol extensions. Before this, a protocol was an abstract contract to which structs and classes could confirm. With Swift 2, a protocol can also have code in it. This brings a whole new level of abstraction to Swift and in this article I’m going to show you how we leverage it’s power at Aftonbladet.
Defining a news article
Let’s start to define a news article using protocols. We want an article to have the following properties:
- Title
- An optional image
- Text
- A list of tags (such as “politics” and “sport”)
- A URL (that points to the article on the web)
So let’s define these in separate protocols:
protocol WithTitle{ var title: String {get} } protocol PossiblyWithImage{ var image: UIImage? {get} } protocol WithText{ var text: String {get} } protocol WithTags{ var tags: [String] {get} } protocol WithUrl{ var url: NSURL {get} }
Then we are going to define an immutable article that conforms to these protocols:
struct Article : WithTitle, PossiblyWithImage, WithText, WithTags, WithUrl{ let title: String let image: UIImage? let text: String let tags: [String] let url: NSURL }
We can then of course initialize an article like so:
guard let url = NSURL(string: "http://goo.gl/qFvFlv") else {return} let article = Article(title: "Zlatan om mötet: Alla har förstått", image: nil, text: "Article text", tags: ["sport", "zlatan"], url: url)
Defining a Spotlight indexable item
The goal for us here is to allow an object of type Article to be able to be indexed in Spotlight on an iOS device, so that the user can just search for an article using its title or text, right on their “desktop” on their iOS device. Let’s define a protocol for a Spotlight indexable item:
protocol SpotlightIndexable{ var spotlightTitle: String {get} var spotlightDescription: String {get} var spotlightKeywords: [String] {get} var spotlightImageData: NSData? {get} var spotlightUrl: NSURL {get} func index() }
This protocol defines what a Spotlight indexable item requires. A title, description, keywords and possibly an image represented as data (NSData).
Now, how do we go about making our Article object indexable? We could just go and make Article conform to SpotlightIndexable but then we have to define the properties that SpotlightIndexable requires. If we had to make more objects conform to SpotlightIndexable, then we have to define the same properties over and over again. That won’t work very well and is a pain. So how do we solve this?
Making a protocol require other protocols
We know that for an article to be indexed in Spotlight, we have to make it conform to SpotlightIndexable protocol but we don’t want to define those properties that are in this protocol manually. Instead, what we want is for SpotlightIndexable to be able to read those properties from any object that conforms to it. This is a shift in our way of thinking. Now a protocol will require any object that conforms to it, to conform to a series of other protocols. Look at this new SpotlightIndexable protocol:
Sprotocol SpotlightIndexable : WithTitle, PossiblyWithImage, WithText, WithUrl{ var spotlightTitle: String {get} var spotlightDescription: String {get} var spotlightKeywords: [String] {get} var spotlightImageData: NSData? {get} var spotlightUrl: NSURL {get} func index() }
Now our SpotlightIndexable protocol requires any type that conforms to it, to also conform to WithTitle, PossiblyWithImage, WithText and WithUrl. Does this sound similar to any other types that we defined earlier? Yes, the Article type!
Extending our SpotlightIndexable protocol with code
What we need to do now is to translate the protocols that SpotlightIndexable requires (such as WithText), to the properties that it requires.
extension String{ var words: [String]{ return characters.split{$0 == " "} .map{String($0)}.map{$0 .stringByTrimmingCharactersInSet(NSCharacterSet .punctuationCharacterSet())} } } func + (lhs: [T], rhs: [T]) -> [T]{ var array = [T]() array.appendContentsOf(lhs) array.appendContentsOf(rhs) return array } extension SpotlightIndexable{ var spotlightTitle: String{ return title } var spotlightDescription: String{ return text } var spotlightKeywords: [String]{ return title.words + text.words } var spotlightImageData: NSData?{ guard let image = image else {return nil} return UIImagePNGRepresentation(image) } var spotlightUrl: NSURL{ return url } func index(){ //Code this } }
Putting it together and final words
Now that we have our SpotlightIndexable protocol and protocol extension, we can simply make our Article struct conform to it to make the article spotlight indexable:
struct Article : WithTitle, PossiblyWithImage, WithText, WithTags, WithUrl, SpotlightIndexable{ let title: String let image: UIImage? let text: String let tags: [String] let url: NSURL }
That was it really. The Article struct doesn’t have to implement anything related to SpotlightIndexable since the code resides in the protocol itself which on its own looks at Article to make sure it conforms to a series of protocol such as WithTitle and WithText.
I think protocols and protocol extensions are a welcome addition to Swift and they enable a level of abstraction unheard of.
/Vandad — glhf