I’ve gradually been changing my code style favouring (Obj-C) categories and (Swift) extensions a lot more. I’ve been inspired by my other lover (Ruby). Why? Readability and code organisation.
Let me explain my source of inspiration. I’ve been coding with Ruby from time to time this whole year, and one of the features I really like is opening classes.
As a matter of fact, classes in Ruby are never closed. Any file can add to a class declaration whatever it sees fit. You can add methods, nested classes, and properties. This is often used to either add functionality, or to add more expressivity to the code.
A classic example:
1 2 3 4 5 6 7 8 9
So you can express:
1 2 3 4 5
The rules for this in Ruby are simple, you can add to existing classes and modify methods and properties. Opening a class and modifying it can create serious problems, but this is the nature of the language. In Ruby you can change even essential parts of the language and the standard libraries.
From now on I will be giving examples using Swift extensions. You can apply the examples to Objective-C categories, but bear in mind that they are not exactly same, and they don’t offer the same functionality - More on that just a bit later.
Please don’t confuse Swift extensions with Objective-C class extensions. There is no swift counterpart to Objective-C class extensions.
So how do I like to (ab-)use extensions?
Factory methods for 3rd party code
Let’s say you are using a 3rd party library. The library offers an awesome control and it is very customisable:
1 2 3 4 5 6 7 8
But in your application you configure that control in a very specific way, from many different parts of your UI code. Instead of creating a configurator object with static methods, or specific configuration methods on your types, you can create an extension on the 3rd party type to offer more API and specific configuration for your application. This keeps your code DRY and separates the configuration of 3rd party types from your code.
1 2 3 4 5 6 7 8 9
Semantic separation of APIs
This is possibly the best known use of this feature. The only drawback is that you can’t add stored properties, so API extensions only containing methods are the best candidates.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
You can interface two disparate types, in different files, by adopting a protocol in an extension, rather than in the type declaration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Instead of relying on a gigantic stylesheet-type file, where all appearance setup is made, you can use extensions specific to your application to style UI components. Here I use the previous pattern of adopting a protocol in an extension:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Additionally, you can create extensions to reflect your designer’s stylesheet.
1 2 3 4 5 6 7 8 9 10 11 12 13
Extracting specific logic for readability
Sometimes you may want to split a slightly complex method into different private methods.
If your logic requires manipulating a 3rd party or framework type, then you can extract that part of logic into an extension of the 3rd party type instead of extracting it into another method in your type.
The language details
Objective-C categories offer similar functionality as Ruby’s open classes. You can override existing methods and, using the runtime, you can ‘inject’ your code between the previous implementation and yours. They are very flexible, but unfortunately very dangerous.
In Swift, the programmer can extend not only classes, but enums and structs. Extensions in Swift are safer because they are namespaced to the module they belong to, and you can’t override existing behaviour.
They are less flexible because you can’t override existing behaviour, and you don’t have access to the runtime to ‘plug into’ existing code.
Shooting everybody on the foot
Why did I write about alternatives to do the same thing on 3 different languages?
There is very good reasons to take categories and opening classes as a bad practice. It’s very easy to misuse and abuse, creating problems for any users of your code, and breaking functionality with newer releases of OS and libraries. I’ve already mentioned before safety problems of categories.
Please don’t do this in Objective-C:
1 2 3 4 5 6 7 8 9
If you are using Objective-C you should prefix the category names and the method names with your project prefix. This will ensure that no other code clashes with yours at run-time. This is standard practice for any competent Obj-C developer.
What is not so well understood is that you can start changing method implementations from categories in any class, meaning you can do exactly what Ruby does with open classes. If you start swizzling implementations and many libraries do the same, there’s high risk that there will be undefined behaviour. Tshe code starts to be hard to understand because of too much runtime magic.
Even though you are on the safe side by adhering to best practices and not abusing the runtime. The same best practices sometimes pollute the beauty of categories. If you need to prefix every method name, the code does not look so clean and elegant.
Swift offers the best balance with extensions. Thus I think it is perfect for a very aggressive use of extensions, as the code will fail to compile when methods and properties have the same name across classes and extensions. The extensions are implicitly namespaced in different modules, so no clash will happen between libraries and between libraries and application code.
I think extensions is one of the best features of Swift, and a vast improvement over Objective-C categories. I have shown you examples of how you can use extensions for your advantage.
The best feature of Swift extensions, in my opinion, is safety. All types and extensions are implicitly namespaced under a module, per-target in Xcode. This means all complex clashes of similar additions between different modules (libraries, application) are gone.
Many developers would say that Swift extensions are not so flexible as Objective-C categories. Not having access to the runtime, you can’t exchange method implementations to add behaviour or to capture method executions for your own (evil) needs. While this is true, many times this flexibility is limited to small hacks, debugging or proof of concept prototypes. You can still Objective-C for that if you wish.
If you have more ideas on how to use extensions for fun and profit, please tell me!