I’d barely used extensions in my Swift code. When we started using Swift at SoundCloud I noticed a common parttern that most of people follow. They created extensions to organize the interface methods in different “namespaces”. As shown in the example below:
Extensions were used for pure style reasons, keeping the interface well organized. The interface could even be separated in different files follogin the
Objective-C name style,
With the transition into frameworks we found out a couple of use cases for extensions that might help you if you plan to transition your monolithic app into frameworks. I’ll go through them and show you some exaples:
Implicit dependency injection
When a framework provides a feature, it takes all the dependencies (aka Services) from the app. Typically a constructor of a feature that is defined in a framework looks like this:
Every time we instantiate the feature from the app, we’ll end up writing the same initialization, passing the dependencies managed by the app:
The more dependencies our feature has, the more code we’ll duplicate, since by default, all the instances will take the same dependencies (in rare cases we’ll inject a different dependency into a feature). Here is where extensions came very handy. Since we want to prevent our developers from write the same initialization logic all the time, we can extend the class from the app, adding up a convenience initializer:
Another very useful use case for extensions in a frameworks setup is the conformance of application protocols from a framework model. In our transition into frameworks, we wanted to be able to reuse components from the app until we had time to migrate them into their own framework. To reuse these components (e.g. a TableViewCell presenter), the component (in the app) and the model (in the framework) had to speak the same language, in other words, they had to know about a shared interface. Since these interfaces are something application specific, that the framework shouldn’t be aware of, it didn’t make sense to pull them out to the framework. It’ll be clearer with an example:
- The model
SearchEntityis extracted into its own
- From the app, the user can select a search result and open the player. The player requires the entity to conform a
PlayQueueEntityprotocol that is defined in the app.
- Since the
Search.frameworkcould be used in a different app/target where the results are not opened in a player, conforming the
PlayQueueEntityprotocol from the
Search.frameworkwouldn’t make sense. Otherwise, the framework would know where it’s going to be used.
Thanks to extensions we can solve this issue. By just conforming a protocol from the app, we provide our framework models with a behaviour that they didn’t have originally:
By doing that the
Search.framework can be very generic, and depending on where they are used, we extend the interfaces of its models.