The Common Reuse Principle on iOS

Bear this in mind before creating or adding a 3rd Party Library to your project

César Vargas Casaseca
Axel Springer Tech
5 min readJun 21, 2020

--

So many things we do not need!

We as developers are usually advised to search in the Open Source Community before adding new functionality. The odds are that most likely some other engineer faced the same problem and, in a remarkable act of charity, published their solution so we can also reuse it. Or perhaps we are that caring developer, who content with their work, decided that more people should benefit from it.

In any case, we should make sure that the component will adhere to the Common Reuse Principle (CRP): Don’t force users of a component to depend on things they don’t need. Oh ok, could you elaborate on that? Yeah, sure.

The CRP in Depth

Photo by Jack B on Unsplash

Whenever we have a use case, some set of classes collaborate amongst themselves to achieve their goal. A very common case is a Factory class and the types it generates, they are tightly coupled. Being so dependant on each other, the CRP states that they belong together in the same component.

Classes and modules that tend to be reused together belong in the same component.

Until here everything is fine, if this is the case of our analysed component we should not worry. The issue arises when we flip the coin and state the principle inversely; it provides us even more valuable information. It tells us which classes should not be kept together in a component: those that are not reused together.

Why Not?

Because once we are using another component, we have a dependency for the whole component, for all the classes. Yeah, maybe we are only using one class of it, but still we are going to suffer all the inconvenience of a big dependency:

  • Every time that component changes we will need corresponding changes.
  • Or maybe they keep a clean interface so no changes in our side are required, but for sure we are going to have to compile the whole component again and again.

The latter was the major drawback for us at WELT, especially when compiling on a slower machine, as the virtual ones assigned in a remote based CI solution.

An Example

In WELT News we convert the HTML text into NSAttributedString to be displayed in our Articles Views. At first we tried the built-in framework to carry out this operation, having something like this:

And it worked fine, but unfortunately very slow. Moreover, since the execution should be performed on the main thread, the UI was blocked until it finished, which was not acceptable for our stakeholders.

Luckily we got to know DTCoreText, a project that duplicates the Apple method, only doing it 18x times faster. We were very happy with that approach, so we decided to integrate it in our library.

The dilemma popped up when realizing that DTCoreText uses DTFoundation behind the scenes, a collection of utility methods and category extensions that Cocoanetics is standardizing on.

So DTFoundation includes a lot of classes that are not related to our use case. Just to render our text faster we have to compile classes related to a large variety of topics such as SQLite, ZIP files, AWS …

We depend on tons of things we do not need

Even if these components might eventually be useful to us, we are currently not using them, and therefore shouldn’t have to include them. We should not compile them in our project, as it will slow build times.

We might argue that these frameworks will only be built once, since every dependency manager and CI will enable us to cache and reuse them. Even with that, it is worth to decrease the compilation times for that first time, or cut down the size of the cache. On top of that, if the component is updated because of an unrelated change and we are always up to date with our dependencies, it will compile every time it needs to update.

The Solution

Photo by Olav Ahrens Røtne on Unsplash

The CRP tells us that when we depend on a component, we should aspire to depend on every class in that component. As Robert C Martin points in Clean Architecture, we want to make sure that the classes we put in a component are inseparable- that it is impossible to depend on some and not on the others.

In other words, classes that are not tightly bound to each other should not be in the same component. That goes against the Umbrella Toolset libraries that contains big collections of utilities applied to a big number of different topics.

To avoid the caveats mentioned above, we plan to extract the classes reused together to achieve the use case of HTML conversion into NSAttributedString into their own component. This way we will only have to compile the necessary number of classes reused together to achieve our goal.

Its first cousin, the ISP

This probably all sounds very familiar to you. Most likely it reminds you of the Interface Segregation Principle, the I from SOLID:

Clients should not be forced to depend upon interfaces that they do not use.

Indeed, the ISP advises us not to depend on classes that have methods we don’t use, the CRP advises us not to depend on components that have classes we don’t use. The foundation behind them is the same:

Don’t depend on things you don’t need!

The Death of Socrates, Jacques-Louis David

We are usually told, don’t reinvent the wheel. And rightly so. If there is already a proven open source solution to our problem which is backed up by a large community of users, it would serve us well to use it.

But before adding the dependency blindly to our codebase, we should stop and analyze the component, to make sure that we are not going to be dependant on things we do not need. This will give us time, and time is our most valuable asset.

So, just as when Socrates strode through the city’s central marketplace and declared provocatively, “How many things I don’t need!”, we should, in our life as in our code, confidently assert:

Don’t depend on things you don’t need!

--

--

César Vargas Casaseca
Axel Springer Tech

Senior iOS Developer at Automattic. Allegedly a clean pragmatic one. Yes, sometimes I do backend. Yes, in Kotlin, Javascript or even Swift.