Welcome to part 2 of the Swift Extensions posts. If you haven’t already, check out the previous post Swift Extensions Part 1 — Computed Properties and Initializers, to learn about those aspects and get an introduction to what Swift extensions are, and why you might want to use them. The short reason is they let you extend a type for which you may not have the source code for, and you may want to do so to make working with that type easier in your own code.
Are the initializers in the main type long with a bunch of options that are unnecessary for your code? Add your own convenience initializer that only takes the parameters you need to change, and assigns defaults to the parameters you don’t care about in the real type’s initializers. I was making several different UIColor objects in one of my apps, and they never needed alpha changed, so I just hard-coded that to be 1.0 in an Objective-C category (the precursor to Swift extensions). Of course, this is only really helpful if you call the longer code several times, which was the case for my app.
Methods in Swift Extensions
So, I’ve talked about how Objective-C categories and Swift extensions basically add methods to existing types, but I’ve not actually shown how to add methods themselves yet. Well, it’s pretty anticlimactic. Really, you just combine my previous post and my post about Instance Methods and Type Methods in Swift, and that’s about it, but for completeness, here we go:
extension String { func printContents() { print(self) } } //Called "Swift is awesome!".printContents() //Output: "Swift is awesome!"
This is based on, but even simpler than the example listed in the Apple iBook. In the iBook, they overload Int with a function that takes a closure, and performs that closure as many times as the Int. This is cool, but this is significantly simpler than anything that uses closures. You should notice that we call the self keyword, to refer to the instance of that which we are extending, in this case the String itself.
As mentioned in my post about Instance Methods and Type Methods in Swift, there is something to keep in mind for value types (structures and enumerations). If you have to change a property or assign to self in a value type, you must preface the function with mutating. That is no different for extensions. If you will recall from the post Swift Strings, Swift Strings are actually structures. As such, they must follow this rule as well. Let’s say we wanted to make every string twice as long, maybe for testing auto-layout issues relating to buttons or labels. We could do something like this:
extension String { mutating func doubleString() { self = self + self } } var testString = "Swift is awesome!" testString.doubleString() testString.printContents() //Outputs: "Swift is awesome!Swift is awesome!"
See what I did there? I was about to just do a normal print, and then I remembered that I just wrote something to do that. Don’t worry, I’m not planning to use this in my future posts. Anyway, we used the mutating keyword on the function, and then assigned the String concatenated with itself, back to itself.
Nested Types and Subscripts in Swift Extensions
The easiest way I thought of showing how to add subscripts with extensions was getting the components out of a Date (the day, month, and year). I also figured the easiest way to delineate between the different components you would want would require a nested type, so let’s look at both in the same example.
Like the rest of our discussion of extensions, these are pretty simple. You basically just declare the subscript code and the nested type within an extension block. The major new thing is the use of self to refer to that which is being extended. Without further ado, let’s see an example of nested types and subscripts to extract the year, month, and day from an Date:
extension Date { enum DateComponent { case year,month,day } subscript(requestedComponent: DateComponent) -> Int { switch requestedComponent { case .year: return Calendar.current.component(.year, from: self) case .month: return Calendar.current.component(.month, from: self) case .day: return Calendar.current.component(.day, from: self) } } }
Yes, you can see when I wrote and compiled that. We create a simple enumeration at the top just signifying year, month, and day called “DateComponent”. We had the subscript accept a value the DateComponent type, and return an Int for whatever the value of that component was. We then run the requestedComponent through a switch statement to check which component was being requested, and then call a function to get it, and return that result. We first requested the current calendar from the type property “current” of the Calendar type.
Adopting and Conforming to protocols with an Extension
Finally here we are. With everything else we’ve talked about, now this one is pretty easy. Let’s have a protocol that requires a read-only computed property named “tweet”. This property should return an optional String, containing an actual Swift String if it is 280 characters or fewer, or nil otherwise. The developer could test for this and disable the “send” button if the String is too long, or request the user to write a shorter message. The protocol would look like this:
protocol Tweetable { var tweet: String? { get } }
Now, the String type would make sense to adopt this protocol, since tweets are Strings anyway. Here is how we would adopt and then conform to the Tweetable protocol:
extension String: Tweetable { var tweet: String? { if self.characters.count <= 280 { return self } else { return nil } } }
As you can see, it just counts the characters in the String (self, in this case) with the countElements function. If it is less than or equal to a length of 140 characters, it returns self. If it is greater than 140 characters, it will return nil. Pretty simple.
Now, we can just use it via dot syntax on any Swift String variable or constant:
let shortString = "Swift is awesome!" let longString = "This is a long run-on sentence that keeps going well in excess of 280 characters just to test this tweet property, while imparting no actually relevant or important meaning. Really, I'm just trying to write a really long String here. Okay, maybe it is not too far in excess, but a String of 347 characters is good enough for this example, right?" let shortTweet = shortString.tweet //Stores: "Swift is awesome!" let longTweet = longString.tweet //Stores: nil
Conclusion
There you have it, all of the original capabilities of Swift extensions in two posts. Swift 2 added the ability to extend protocols themselves as well, which you can read about in its own post Protocol Extensions in Swift. When you are using something in your app for which you do not have the code, be it a built in Cocoa type, or even a third-party framework, extensions are a great tool to customize them to your needs. Even if you do have the code, you probably don’t want to go changing the code for a framework that is updated frequently if the addition is really custom to your project. In that case, making an extension helps you get what you need, and lets you not have to remember to copy it back into the file if you update the framework from GitHub or whatnot later.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!