A demo project that illustrates the content of this article is available on my GitHub.

Frameworks are, by definition, meant to be used in many diverse contexts, that’s why developers pay special attention when they design them, in order to let their integration be as seamless as possible.

When it comes to API design, we all have a pretty good sense on how to achieve this goal. But an iOS framework is not only made of code: it also contains resources. And in many cases, being able to override such resources might be a mandatory prerequisite for an app that integrates the framework.

For the purpose of this article, we will assume that we are working on a series of white-label apps. Those apps deal in the same business logic, but each one does so using its own branding. In such a situation, a framework that provides the common business logic and allows the app to override its default resources is a great tool to streamline the development workflow.

How to achieve it

An iOS framework is basically a container that stores some code, along with a bundle in which resources are stored.

Our first step toward our goal will be to make this bundle easily accessible from inside the framework:

Then, we create a global function that will allow us to instantiate an overridable image. The strategy behind this function will be to attempt to load an hypothetic overridden image provided by bundle of the app, and if none is found it will then default to the one stored in the framework:

This strategy can also be applied to storyboards (and to basically any binary resource stored in the bundle):

Lastly, to handle the case of localized strings, we will attempt to localize the key using a table stored in the bundle of the app and — by convention — named Override.strings. If the key was not found in this table, we then fall back to the one contained in the framework:

Conclusion

The aim of this article is to describe a general strategy that can be applied to tackle the issue of designing a framework whose resources are overridable. The API used to achieve it was voluntarily presented in a very raw fashion, using global functions.

In practice, a developer might want to add some additional syntactic sugar. For instance, using an extension as follows:

It might be tempting to fully integrate the overriding API into UIKit through the use of swizzling. I, personally, would strongly advise against it, because modifying the expected behavior of SDK-level API will revel misleading and hard to maintain.


Originally published at gist.github.com.