So, there has been a reason for my most recent choice of topics to discuss. This isn’t some grand culmination post, but the topic for today is related to several of the previous topics due to what it can do. Back in Objective-C, you could extend classes for which you did not have the source code with something called categories. If you are curious about Objective-C categories, I wrote about them back when this blog was still about Objective-C in my post Objective-C Categories. This post was much earlier in my blogging career, so it may not be as polished as my current posts, so, you have been warned.
Anyway, Objective-C Categories gave you the ability to add additional methods to another class for which you didn’t have the original source code. I used this capability in a later Objective-C post Introduction to UIColor, to create a category that made it easier to create a UIColor for my purposes. I didn’t need to customize alpha (transparency) each time, and I wanted to use color values between 0 and 255 instead of 0.0 and 1.0, so I made a category that translated my 0 to 255 values to their 0.0 to 1.0 equivalents, and hard-coded the alpha to 1.0 (fully opaque).
These are not the same as iOS 8 extensions, such as the Today View Widgets, UIActivityViewController sharing extensions, etc. We will cover those in a later post.
When you look at the capabilities of Swift Extensions, they are basically doing the same thing as Objective-C categories, adding methods to existing classes. With one exception, they are all different flavors of methods.
Swift Extensions
As mentioned above, Swift extensions are basically adding methods to existing type (classes, structures, and enumerations) just in a few different flavors. Below I will list off what extensions can add to existing types, as well as the accompanying post I wrote to discuss those aspects in detail:
- Computed Properties — Computed Properties in Swift
- Instance and type methods — Instance Methods and Type Methods in Swift
- Convenience Initializers — Designated Initializers and Convenience Initializers in Swift and Class Initializers
- Subscripts — Custom Subscripts in Swift
- Nested Types — Using a Nested Type in Swift
- Protocol Adoption — Protocols in Swift
Now you may look at that list and, if you’ve been following along, probably be able to easily see how computed properties, instance/type methods, initializers, and subscripts are basically just adding methods. If that isn’t apparent, I would recommend reading the associated sections from the links above. Each of them (besides instance/type methods themselves) are related a specialized form of a method. But protocol conformance and nested types? How are those methods? Protocol adoption itself has you define properties even! Well, stored properties can’t be boiled down to methods, but computed properties can be! The protocol doesn’t care whether the property is stored or computed, so you just would have to use them for the protocol. Nested types though, those surely aren’t methods. That much is true, but the only way to access the nested type of an extension is through a method or computed property. Apple’s Swift iBook has a great example of using a nested enumeration in an extension describing whether an Int was positive, zero, or negative. We will cover another example when we get to talking about that part of Swift extensions. You can create a nested type in your Swift extension, and then use it elsewhere in that extension, or whatever calls the it.
The syntax to create an extension is very similar to creating a type, and it looks a little something like this:
extension UIColor: ImportantProtocol, MoreImportantCustomProtocol { //Fill in the blank }
So you can see, instead of class, struct, or enum, we use the extension keyword. Afterwards, we the list of protocols it will adopt (if any). If you don’t need new protocols, just declare it without the colon and anything afterwards, so:
extension UIColor { //Fill in the blank }
Classes can override their superclass’s implementation of a method, however, extensions cannot do similar. Swift extensions can add new capabilities to a type, but they cannot override anything. You can add completely new functionality with a very similar name to a method, but it cannot be the same and must be unique, and cannot use the override keyword.
You can add this to the end of any Swift file (outside of the type’s curly braces) if you wish, or you can create a separate file and have it in your target. I don’t know what the best practice is yet, but I will probably do it as a separate file until I find a reason not to. You do not name extensions like you did in Objective-C, and you do not need to import them in your files (since they will be shared in the same module by default (unless marked as private, of course)).
Convenience Initializers in Swift Extensions
I said that on purpose there. You can only add convenience initializers in Swift extensions, not designated initializers. These follow the same rules as convenience initializers in a type, that they can call other convenience initializers, but must eventually call a designated initializer of the class it is extending. You of course must also make sure that all properties are given valid values before initialization is complete.
We will harken back to my Objective-C Introduction to UIColor post for this one, and remake a simplified version of my UIColor initializer:
extension UIColor { convenience init(red: Int, green: Int, blue: Int) { let newRed = CGFloat(red)/255 let newGreen = CGFloat(green)/255 let newBlue = CGFloat(blue)/255 self.init(red: newRed, green: newGreen, blue: newBlue, alpha: 1.0) } }
It calls the designated initializer for UIColor via self.init at the end, with the Integer components converted into the CGFloats necessary for that initializer. Similar to my original post, if you want to use something like this, you should put some bounds checking code in the initializer to make sure your newRed, newGreen, and newBlue are between 0 and 1.0 before you call the designated initializer, just to make sure they are valid values.
Computed Properties in Swift Extensions
There is something important to know before we go any further. Swift extensions cannot add stored properties to a type. You can make computed properties, but you cannot store them unless they store via a stored property that is already in the type you are extending (like if you added an extension to a Bool to take the Strings “Oui” or “Non” and just mapped them back to true and false and stored those as the Bool’s value). You also cannot add property observers to properties that already exist in the type you are extending.
Let’s say you were making an app that had stored hashtags without the # symbol. We want to read them back out with the # symbol prepended, so let’s add that as a read-only computed property:
extension String { var hashtag: String { get { return "#\(self)" } set { let stringWithoutHashtag = newValue.replacingOccurrences(of: "#", with: String(), options: .literal) self = stringWithoutHashtag } } } //Usage let stringOne = "swift" let stringTwo = "swiftlang".hashtag //Can use on literals too print(stringOne.hashtag) //Outputs: "#swift" print(stringTwo) //Outputs: "#swiftlang" var justTag = String() justTag.hashtag = "#iosdev" print(justTag) //Outputs: "iosdev"
Using the setter for this one is a bit clunky (I couldn’t set directly from the declaration of justTag), but you get the point there. You read from or assign to self for the getter and the setter respectively to use computed properties in a Swift extension.
Conclusion
This post is getting a bit long, so we shall cover the other capabilities of Swift extensions in a later post.
Swift extensions are particularly useful for making the built-in Swift or Cocoa types easier to use in the context of your app. When I started adding some UIColor code to one of my apps, it just got unwieldy to do it all by hand. I had RGB values in 0 to 255, and I never wanted to change the alpha. To use the built in type, I had to have (colorComponent/255.0) for each component (or calculate them externally (in my case with a spreadsheet earlier on)) and constantly setting alpha to 1.0. It was ridiculous and unscalable. When I added the Objective-C category that the above Swift extension was based on, my code got a lot clearer, showing just what I needed. It also got safer (in case of a typo of setting a color value above 255) because I also added bounds checking to the category (not depicted above). Swift extensions are extremely useful and you can bet I’ll be using them in my code.
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!