Jekyll2024-02-06T08:02:26+00:00http://dmtopolog.com/feed.xmltopolog’s tech blogThings your mom didn't tell you about iOStopologShift in the protocol paradigm2023-01-10T00:00:00+00:002023-01-10T00:00:00+00:00http://dmtopolog.com/protocol-paradigm-shift<p>In this article we will be talking about the recent changes related to protocols: opaque, existential and generic types; <code class="language-plaintext highlighter-rouge">some</code> and <code class="language-plaintext highlighter-rouge">any</code>; runtime and compile-time polymorphism.</p>
<p>I won’t describe the details of those features, but will share my thoughts on how they change the perception of protocols, and why we can finally say that protocols in Swift are completely different from what they are in Objective-C.</p>
<p>Those changes originated from the discussion initiated by Joe Groff at the forum (back in April 2019 <a href="https://forums.swift.org/t/improving-the-ui-of-generics/22814">“Improving the UI of generics”</a>) and have been gradually released in 5.x Swift versions. <em>(The initial post was itself a result of previous discussions among the members of the Core team and can be traced back to 2016, the time in between Swift 2 and Swift 3 when <a href="https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md">Generics Manifesto</a> was created)</em>.</p>
<h3 id="the-dualism">The dualism</h3>
<p>From the origins of Swift, <em>protocol</em> (as a language feature) was always in between two completely different worlds: compile-time constraints and runtime flexibility.</p>
<h4 id="runtime-polymorphism">Runtime polymorphism</h4>
<p>From Objective-C protocols inherited their dynamic essence. In this meaning “protocol” is what called “interface” in most of other languages. It’s a capability to define some contract (variables, functions) which then will be implemented by specific data types. So the compiler doesn’t know about a specific type, and exact implementation of the contract is being “attached” only in runtime using <em>dynamic method dispatch</em>. It’s more flexible, but less specific and impossible to optimise by the compiler.</p>
<p>Here is an example of runtime polymorphism:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Animal</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">Cat</span><span class="p">:</span> <span class="kt">Animal</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Animal</span> <span class="p">{}</span>
<span class="k">var</span> <span class="nv">someAnimal</span><span class="p">:</span> <span class="kt">Animal</span> <span class="o">=</span> <span class="kt">Cat</span><span class="p">()</span>
<span class="n">someAnimal</span> <span class="o">=</span> <span class="kt">Dog</span><span class="p">()</span> <span class="c1">// we can reassign a value of another type to this variable</span>
<span class="k">var</span> <span class="nv">animals</span><span class="p">:</span> <span class="p">[</span><span class="kt">Animal</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Cat</span><span class="p">(),</span> <span class="kt">Dog</span><span class="p">()]</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Animal</code> is a specific type both for the variable <code class="language-plaintext highlighter-rouge">someAnimal</code> and for the element of the <code class="language-plaintext highlighter-rouge">animals</code> collection. Compiler has no idea about the exact types regarding those vars. The exact type doesn’t matter here at all. So protocol behaves like a type itself. This type is called <strong>existential type</strong>. As you can see variable of existential type can hold values of different concrete types as far as they conform to the protocol. Collection of existentials can be heterogeneous, meaning it can also hold values of different concrete types.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">pat</span><span class="p">(</span><span class="nv">animal</span><span class="p">:</span> <span class="kt">Animal</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// `animal` has a value of existential type `Animal`</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="compile-time-polymorphism">Compile-time polymorphism</h4>
<p>At the same time in Swift world protocol always played a big role in <em>generics</em>. Generics is a system that allows us to specify a set of requirements/constraints that can be later translated by a compiler into specific types. As the types are clear in compile time, the implementations are “connected” to the calls at that stage as well, so <em>static method dispatch</em> is being used. Hence the compiler can help you a lot here during development as well as optimising the code while compiling it.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">pat</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Animal</span><span class="o">></span><span class="p">(</span><span class="nv">animal</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// `animal` is a value of some specific type, conforming to `Animal`</span>
<span class="p">}</span>
<span class="nf">pat</span><span class="p">(</span><span class="kt">Dog</span><span class="p">())</span>
<span class="nf">pat</span><span class="p">(</span><span class="kt">Cat</span><span class="p">())</span>
</code></pre></div></div>
<p><strong>Generic type</strong> <code class="language-plaintext highlighter-rouge">T</code> is being resolved into a specific type for each function call. So we know the exact type (<code class="language-plaintext highlighter-rouge">Cat</code> or <code class="language-plaintext highlighter-rouge">Dog</code> in this case) inside the <code class="language-plaintext highlighter-rouge">pat()</code> function. In this simple example it doesn’t bring us much value, but in a more complex construction it may have a big difference.</p>
<h4 id="alternative-terms">Alternative terms</h4>
<p>On the Swift forum you may also encounter such terms as <strong>type-level</strong> and <strong>value-level abstraction</strong>. They describe the same dualism from the perspective of the compiler.</p>
<p>Generic types (that represent compile-time polymorphism) provide abstraction on a type level so the compiler resolves the abstract types and then can already operate specific types, completely preserving their details (functions, properties). So we have <strong>type-level abstraction</strong> here.</p>
<p>In case of existentials and runtime polymorphism the exact types covered by protocols are not known for the compiler, so they are replaced by existential types (one protocol - one existential type). So the compiler have only ideas about the existential type and the values that will be assigned to it in runtime. Hence <strong>value-level abstraction</strong>.</p>
<p>In <a href="https://forums.swift.org/t/improving-the-ui-of-generics/22814#type-level-and-value-level-abstraction-1">this forum post</a> you can read more about it.</p>
<h3 id="the-paradox">The paradox</h3>
<p>This may not sound like an important thing, but there was a lot of confusion and frustration among the developers regarding the dualism of protocols, up until recently (before the discussed changes were released). Sometimes you were not allowed to use the same protocols in different use cases mixing runtime and compile-time capabilities. In some situation it could also cause some errors and unexpected behaviours (both in runtime and in compilation time). <em>(In our previous articles <a href="/protocol-faces/">“Several faces of protocols”</a> and <a href="/do-protocols-break-srp/">“Do protocols break Single Responsibility Principle?”</a>; we dived into the subject in more details)</em></p>
<p>There were always different opinions inside the Swift Core Team and most active part of the community regarding Protocols. Some people admitted that there was this feature dualism between the runtime and compile-time capabilities. For instance Dave Abrahams <a href="https://forums.swift.org/t/lifting-the-self-or-associated-type-constraint-on-existentials/18025/42">in one of the discussions on the forum</a> said:</p>
<blockquote>
<p>I have always thought a big part of our problem is that protocols that are meant to be used for type erasure are fundamentally different from those meant to be used as constraints, yet we declare them the same way.</p>
</blockquote>
<p><em>(The author of this post also belonged to this camp, as you could guess from the previous articles. We even argued that it could have been better to have two separate language features instead of one).</em></p>
<p>But the majority considered all the capabilities of protocols as one big feature, that temporary had some gaps and contradictions.</p>
<p>Since the first versions, protocols in Swift played more important role as generic types other than existential ones. Runtime polymorphism is less safe, predictable and controllable (as it puts developer in charge, not a compiler), it erases type details, it implies dynamic memory allocation and reference counting, it’s slower and cannot be optimised by compiler. Compile-time capabilities in contrary were more interesting for the swift core team and the community as they are integrated into compiler and interact with other compile-time features.</p>
<p>Existential type is quite a simple language concept, so it wasn’t even widely discussed until recently (not much to talk about). So most public discussions regarding protocols are being held in the context of generics. <em>(There is an opinion that Swift as “Protocol-oriented language” means more “Generics-oriented language”… but it doesn’t sound as fancy)</em>.</p>
<p>Existential type is what protocols in Swift inherited from Objective-C as “default behaviour”. You didn’t need any additional syntax to define or return an existential type. In contrary the other features like generic types, when-condition, opaque types require some specific words or constructions. Eventually it started to look paradoxical that <em>major features</em> of protocols related to the generic types require more explicit syntax then <em>secondary feature</em> - existentials. The ideas how to tackle it had been discussed for quite some time before the changes even got to proposal stages.</p>
<p>But first things first…</p>
<h3 id="filling-in-the-gaps">Filling in the gaps</h3>
<p>To consider all the capabilities as one feature some visual gaps should have been fixed.</p>
<p>The biggest gap, as the Core team saw it, was the absence of the ability to return a generic type from a function. Existentials could be used both as parameters and result values. But on a compilation level you could only pass a generic type as a parameter, but were not able to describe the result value. That’s how <strong>opaque types</strong> appeared (not without SwiftUI playing a noticeable role).</p>
<p>When opaque types were introduced and then expanded to more use cases, the next challenge was to minimize the interference between generic and existential types. Meaning that one shoul be possible to use in case of the other.</p>
<p>The idea of so called <strong>generalized existentials</strong> was already mentioned in <a href="https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md#generalized-existentials">GenericsManifesto</a>. The essence was to make it possible to use generic-type protocols (the ones with associated type or self-constrained) as existential. Another challenge was to turn such existential back to generic when you need it. Some smaller potential improvements were needed for specific use cases. Some of the feature limitations were artificial (kind of legacy), the other required significant changes, but most of the goals were achieved. A number of discussions were held on forum followed by proposals, and eventually several feature changes were released into the language (see References).</p>
<p>As a result, mixing existentials and generic types became seamless. You can create a generic type protocol and use it as existential and vice-versa. The amount of related compilation issues drastically decreased. Now understanding the difference between runtime and compile-time cases is not needed in most of the cases… as it just work.</p>
<h3 id="completing-the-paradigm-shift">Completing the paradigm shift</h3>
<p>But one “small” change stands out of the list of improvements: <strong>explicit existentials</strong> (introducing <code class="language-plaintext highlighter-rouge">any</code>). It didn’t add any new capabilities.</p>
<p>As time was passing by since the beginning, developers were more and more discouraged to use Existentials. Compile-time capabilities of protocols on the other hand were more and more extended and promoted. They became heavier in functionality and more valuable for the developers. Every other developer, when talking about protocols, kept enumerating the capabilities without even mentioning the original runtime polymorphism. It was just some small feature in the back of their minds, almost a nice side-effect of using protocols.</p>
<p>People in the community (first of all, the Core team) kept questioning the status quo: having existentials as default was considered less and less acceptable. But you cannot just swap the syntax for two different language features, so it was decided to deprecate the default behaviour and create a special syntax for existentials.</p>
<p>That’s why in case of existential declarations (see the code example before) we now should write:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">someAnimal</span><span class="p">:</span> <span class="n">any</span> <span class="kt">Animal</span> <span class="o">=</span> <span class="kt">Cat</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">animals</span><span class="p">:</span> <span class="p">[</span><span class="n">any</span> <span class="kt">Animal</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Cat</span><span class="p">(),</span> <span class="kt">Dog</span><span class="p">()]</span>
</code></pre></div></div>
<p>I’m saying “should” but now it’s a transitive period before the old syntax gets deprecated (expected in the next major language version). So we actually “have to” adopt this new way.</p>
<p>The change makes usage of existentials more explicit. Now it becomes a conscious choice rather than default option. It also eliminates some confusion when mixing existentials with compile-time constrains.</p>
<p>But the most important thing here, imho, is the shift of the protocol paradigm, from legacy Objective-C like runtime interface to a big part of a compile-time abstraction. The shift started when protocols were introduced in Swift, but only now it seem to be completed. From now on protocols will be perceived differently, now there are no doubts that protocols are first of all made for generic or opaque types, associated values, where-conditions and so on, and runtime polymorphism officially takes the second (third, fourth) place.</p>
<p>What can we see in future?</p>
<p>Who knows, maybe eventually the default syntax (defining a value, parameter or result with a protocol without any additional words) will be reassigned back, but this time to the generic/opaque types. That would look like a logical completion of the shift. Possibly in a couple of major releases we will see such syntactic simplification as a new feature in Swift.</p>
<p>The other possible continuation of this story could be compiler warnings that advise you not to use existential when your use case can be covered by a generic/opaque type (Isn’t it already there? Seems logical and easy to implement… but I haven’t heard about it yet).</p>
<p>Even though we might see some other enchancements in this area, as well as further development for the compile-time capabilities of protocols, the shift of the paradigm is completed. Bye-bye dynamic interfaces!</p>
<h2 id="references">References</h2>
<h4 id="main-changes-regarding-opaque-types">Main changes regarding opaque types:</h4>
<ul>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md">[SE-0244] Opaque Result Types </a> <br />
Introducing opaque return types. That’s the feature that was introduced mainly for SwiftUI back in Swift 5.1</li>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0328-structural-opaque-result-types.md">[SE-0328] Structural opaque result types</a>
Extending the use cases for the opaque return type. Now it can be a part of another result structure (a tuple or a closure)</li>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md">[SE-0341] Opaque Parameter Declarations</a> <br />
An ability to use opaque types as parameters: more lightweight syntax for parameters with compile-time polymorphism.</li>
</ul>
<h4 id="additional-changes-regarding-to-opaque-types">Additional changes regarding to opaque types:</h4>
<ul>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md">[SE-0346] Lightweight same-type requirements for primary associated types</a> <br />
An ability to use more precise and complex compile-time parameters with more lightweight syntax.</li>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0360-opaque-result-types-with-availability.md">[SE-0360] Opaque result types with limited availability</a> <br />
More future-proof abstraction of opaque types. Now it allows you to return new types as previously declared opaque types. Capability specifically added for making APIs with opaque result types less breakable.</li>
</ul>
<h4 id="main-changes-regarding-existential-types">Main changes regarding existential types:</h4>
<ul>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md">[SE-0309] Unlock existentials for all protocols</a><br />
Now generic type protocols can be used in some cases (still with limitations) as existentials.</li>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md">[SE-0335] Introduce existential any</a> <br />
Making usage of existencial types more explicit.</li>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md">[SE-0352] Implicitly Opened Existentials</a> <br />
An ability to use existential types in some compile-time constrained cases (generics).</li>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0353-constrained-existential-types.md">[SE-0353] Constrained Existential Types</a> <br />
Making it possible to use compile-time constrained protocol as existensials and still keep those constraints (being able to utilize them after)</li>
<li><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0375-opening-existential-optional.md">[SE-0375] Opening existential arguments to optional parameters</a> <br />
Allows an argument of (non-optional) existential type to be opened to be passed to an optional parameter.</li>
</ul>
<h4 id="key-discussions-at-the-forum">Key discussions at the forum:</h4>
<ul>
<li><a href="https://forums.swift.org/t/improving-the-ui-of-generics/22814">Improving the UI of generics</a></li>
<li><a href="https://forums.swift.org/t/lifting-the-self-or-associated-type-constraint-on-existentials/18025">Lifting the “Self or associated type” constraint on existentials</a></li>
<li><a href="https://forums.swift.org/t/improving-the-ui-of-generics/22814">Improving the UI of generics</a></li>
<li><a href="https://forums.swift.org/t/reverse-generics-and-opaque-result-types/21608">Reverse generics</a></li>
</ul>
<h4 id="other-relevant-reading">Other relevant reading:</h4>
<ul>
<li><a href="https://medium.com/@PavloShadov/https-medium-com-pavloshadov-swift-protocols-magic-of-dynamic-static-methods-dispatches-dfe0e0c85509">Swift Protocols: Magic of Dynamic & Static methods dispatches ✨</a></li>
<li><a href="https://www.hackingwithswift.com/articles/247/whats-new-in-swift-5-6">What’s new in Swift 5.6</a></li>
<li><a href="https://www.hackingwithswift.com/articles/249/whats-new-in-swift-5-7">What’s new in Swift 5.7</a></li>
<li><a href="https://www.swiftbysundell.com/articles/referencing-generic-protocols-with-some-and-any-keywords/">Using the ‘some’ and ‘any’ keywords to reference generic protocols in Swift 5.7</a></li>
</ul>Dmitrii IvanovIn this article we will be talking about the recent changes related to protocols: opaque, existential and generic types; some and any; runtime and compile-time polymorphism.Modularity. What problem does it solve?2022-10-03T00:00:00+00:002022-10-03T00:00:00+00:00http://dmtopolog.com/modularity-3-problems<p>In this article we will discuss the problem that can be solved by modularity and how exactly modularity can make you project much better thing to deal with.</p>
<p>This post is part of the series on modularity:</p>
<ul>
<li><a href="/modularity-1-boundaries">Modularity. Boundaries</a></li>
<li><a href="/modularity-2-encapsulation">Modularity. Encapsulation</a></li>
<li><a href="/modularity-3-problems"><strong>Modularity. What problem does it solve?</strong></a></li>
</ul>
<p> </p>
<h1 id="when-does-the-time-come">When does the time come?</h1>
<p>When is it right time to think about modularization in your project?</p>
<p>It doesn’t look like you should divide your project into the modules from the very creation. You definitely need to follow all the major architectural principles (separation of responsibilities, clear dependencies, architectural layers, DRY, KISS etc.) when building your logic, but you are likely not able to foresee how the project will look like in a year or even several months. Your business ideas, direction of development, target audience, your main features… all may change. At the beginning of its existence, your app can turn into something completely different within several months. So, it doesn’t make sense to start dividing and incapsulating high-level parts of business logic at this point. If you start to design your modules and interactions between them too early, you will be constantly wasting time on restructuring and changing.</p>
<p>The other extremum is when the situation has already gone so far that you need to stop (or at least significantly slow down) development of your features to modularize the project. At this point you might already have more than a dozen of developers, serious commitment to deliver features, ambitious goals… but the project is a mess, technical debt is enormous, bugs are all around and releasing new features gets harder and harder. If you found yourself here, then you should have already modularized your project some time ago. I’m not saying that now it’s already “too late”, no, but it costs you much more troubles to do it now than before.</p>
<p>As always, the truth is somewhere in between, and of course it’s not the same for different teams and different projects.</p>
<h3 id="making-the-terms-clear">Making the terms clear</h3>
<p>First let’s clarify what we mean when you say that the project is <strong>a monolith</strong>.</p>
<p>It might have different dependencies, some of them might be your own modules or subprojects. You might already have some separated modules for common data types, or design components, or network layer. You might have several different targets for extensions (watch, Siri, widgets, etc.). BUT more than 50% of your logic belongs to one “main” target (you can roughly count the number of lines of code together with the number of classes/functions). So, you have a monolith.</p>
<p>When we say that the app/project is <strong>modular</strong> we mean that it consists of a number of modules (separate targets inside one project or one main project and several dependent ones). All the code is spread across those separate logical parts and there are strict physical boundaries between them (read more about the boundaries and different types of projects in <a href="/modularity-1-boundaries">our previous article</a>).</p>
<h1 id="reasons-to-think-about-modularization">Reasons to think about modularization.</h1>
<p>Let’s try to figure out some concrete signs of the project that requires modularization. If your project has some of them it’s likely the time to modularize it.</p>
<p>The most popular reason for moving some code to a separate modules is situation when you need to share some logic between several apps. It might be network layer, common data types, functions or UI elements. We omit it here as it’s quite intuitive and doesn’t require much explanation. But there are more than that.</p>
<h3 id="poor-logical-separation">Poor logical separation</h3>
<p>At some point your project becomes so big and the flows are so complex that there is not a single developer who understands how all the features work. Plenty of features are not a compilation per se, but quite often those features are poorly separated architecturally. It’s really hard to measure such “architectural health” but if you start asking yourself the following questions you are likely to understand how good/bad the situation is.</p>
<ul>
<li>Do you have clear boundaries between the screens, flows and features?</li>
<li>Do you clearly understand the dependencies of the features?</li>
<li>How easy it is to use your features/flows in a different context (in a different part of the app)?</li>
<li>How do you handle shared functions between the features?</li>
<li>Do you have any incapsulated services for networking, persistence, analytics, logging, error handling… or maybe you put all of these to the extensions (not the best approach)?</li>
<li>Do you have a lot of data types that don’t really belong to anything?</li>
<li>Do you use Dependency Injection?</li>
<li>How many singletons do you have?</li>
<li>How often do you prefer to use a singleton instead of passing a dependency explicitly?
etc.</li>
</ul>
<p>One of the bad indicators is situations when you try to grasp the context of an unfamiliar feature/service… but you just can’t. It’s not properly separated from the others; you cannot easily figure out all of the dependency. When tracking down the behavior in the debugger you keep on jumping from one place to another between separate classes and even parts of the app.</p>
<p>The more features you have, the more obvious your architectural imperfections are.</p>
<h3 id="a-lot-of-conflicts">A lot of conflicts</h3>
<p>Not only conflicts in pull requests matter, but generally how often one developer blocks another in their daily work. Starting from a team of 2 developers you need to somehow synchronize the work and coordinate the effort. With a badly architectured app and some decent number of features it can be painful for just 2 devs to work together. If the health of the code base is better, then even 3-5 developers can work on one monolithic project with a decent (though not the best) efficiency. But the more the team grows the more time on coordination you need to spend. Deliveries never grow proportionally to the size of team (twice more developers are never able to deliver twice more features), but if the amount of deliveries hardly increases when scaling the team that’s a bad sign.</p>
<p>The chances are that you do have some separation of logic and areas of responsibility for developers. Maybe you even have some feature teams dedicated to some specific app parts. But ask yourself how clear the boundaries between them are. How much code reside in the “gray areas” which don’t belong to anything? How often it’s not clear who supposed to work on this or that piece of logic? Or the other way around: there are some parts of the app that are constantly being changed by different developers (because many features/services depend on it).</p>
<p>At some point (sooner than later) constant conflicts and lack of clarity may cause serious frustration in the team (which, as we know, decreases satisfaction level and productivity and can even cost you some team members)</p>
<h3 id="compilation-speed-is-unbearable">Compilation speed is unbearable</h3>
<p>If you have a big monolithic target you need to recompile it entirely after every small change. As far as I understand the compiler still tries to make some smart chooses to decrease the compilation time (more with ObjC code then with Swift), but it doesn’t significantly change the situation. Hence when debugging the code, changing and rebuilding it all the time takes an eternity (can easily reach an hour or two cumulatively during the day). You frequently distract yourself, read articles, browse social media or do something else, so the efficiency drops even lower.</p>
<h3 id="re-running-all-the-tests">Re-running all the tests</h3>
<p>Quite likely if your project is not modularized your test coverage is not so high. Testability and modularization are not strictly connected, but there is a correlation. But let’s imagine that the architectural health is decent in your project, and even if it’s a monolith the code is testable and different parts are logically separated from each other. So, it is possible to have a good test coverage. Let’s keep on dreaming and consider you have it. In this case you need to run all the tests (locally or on CI) for every little change. And as the codebase (and test coverage) get bigger it becomes more and more frustrating (hundreds of UI tests take tens of minutes).</p>
<p>I heard about some smart solutions when people create some functionality clusters and per each change define what part of the tests should be rerun. But in a monolith, it’s just an overkill and waste of resources.</p>
<hr />
<p>The points above are quite subtle, there are no clear metrics or specific app qualities that you can monitor. It also usually hard to sell it to business to get time for the necessary refactoring. But if you proceed thinking further you can easily see how they affect the main properties of your product.</p>
<h3 id="decreasing-project-reliability">Decreasing project reliability</h3>
<p>It’s a combination of bad architectural separation and code base growth (not talking about general code quality). When you have “spaghetti code” where everything depends on everything it’s hard to change something without breaking something else. You might notice that the number of bugs, crashes and other side effects increases. That quickly influences your users, their satisfaction and opinion about your app (the rating, in-apps or sales inside the app logically go down).</p>
<h3 id="decreasing-efficiency">Decreasing efficiency</h3>
<p>You spend more time on the maintenance and fixes, more time to synchronize work of different developers, more time on compiling, debugging and testing the code, slow CI pipelines together with high probability of merge conflicts make every merge long and painful, it takes hours and even days to figure out how things work inside an unfamiliar piece of logic… Eventually the development of the new features stalls. If you delivered several features per week/month/sprint before, now this amount noticeably decreased. It’s hard to stick to the planning and meet the deadlines because issues pop here and there all the time.</p>
<p>Maybe you already hear such complaints from the business.</p>
<h1 id="how-does-modularization-help-you">How does modularization help you?</h1>
<p>I’m not saying that all the issues will be magically fixed if you modularize your project. Each of the issues above normally has several reasons and all cases are different. You definitely need to also think about using advanced architectural patterns and best practices, apply some code guidelines and QA-tools, check your work processes and the informational channels inside the team. But I’m sure that modularization will help you to improve the situation in all those aspects.</p>
<p>On a high-level, modular project is more structured than the monolith. Module is a separate unit, an extra layer of abstraction. When working on the host app (outside the module) we deal with modules instead of vague “parts” or “layers” that didn’t have clear responsibilities and boundaries. Specific data types on this level become just implementational details, so we can cut these details off. When working inside the module we don’t need to think about the context of the entire app, it doesn’t matter anymore while the module keeps its interface stable. So regardless of your context you decrease the cognitive load by reducing the context.</p>
<p>Modules are independent projects (even when having dependencies from each other) so the scope for the work processes also decreases. We are talking about several small project instead of one big monolith, hence we have independent, much faster development loops (develop, test, release). We have smaller number of features and much less conflicts: now instead of 10 devs working on the same monolith we have 5 teams of 2 devs working each on independent module.</p>
<p><em>(More details about context separation, and modules as independent projects you can read <a href="/modularity-2-encapsulation">here</a>)</em></p>
<p>We don’t have problems with long compilation and lengthy test runs anymore, as all of these is specific to the module. (You still need to debug and test the integration code, but the amount of work decreases several fold).</p>
<p>In big teams (when more than 4-6 devs work on one app) organizational structure normally changes together with the project. Modules work better when they have clear maintainers. So, the next logical step might be to establish some feature teams with clear areas of responsibility.</p>
<p>No-brainer that because all of this the overall productivity and satisfaction increases because generally it just gets easier to work. The prerequisite-consequence connection here is quite straightforward.</p>
<p>Regarding the reliability, reducing both the context and the connections between the parts of the app (and side effects as a result) changes the situation dramatically (in a good way of course =)). Code changes become easier to make and test, issues become easier to localize and debug.</p>
<p><em>(It was a high level overview of the benefits. In future posts I plan to talk more in details about each one of them: scalability, compilation, work processes etc. mentioning also the challenges that modularisation brings)</em></p>
<h2 id="bonus-improvements">Bonus improvements</h2>
<p>I already mentioned that there are a number of things that influence the maintainability and reliability of your code that are not directly related to modularization. Some of them are also got improved on the way together with modularization. Some changes are inevitable side effect of the refactoring needed for modularization. Some other improvements just look more logical in a new modular context. The others were just waiting in the backlog for ages and transition to the modular architecture is a good occasion to fix it on the way.</p>
<p>Some examples:</p>
<p>Usage of <strong>dependency injection</strong> and less <strong>singletons</strong> will come naturally together with modularization. When you don’t have implicit shared state between the modules you are forced to pass the dependencies explicitly. Inside the modules you can still avoid it and use the dark side of the force (singletons and internal instantiation of dependencies), but you are likely to start questioning this approach as it is not a default approach. If you know all about DI and singletons and just were waiting for the occasion to refactor some legacy shared state (which usually requires quite some changes all around the app), migration to the modules is the time to do it.</p>
<p>You might also rethink some responsibilities in your code (as now you need to draw bold lines between them), on the way you might refactor a couple of <strong>god classes</strong> the ones that have couple of thousands of lines of code and a bunch of different responsibilities.</p>
<p>Modularity itself and advanced architectural techniques will increase <strong>testability</strong> of your code.</p>
<p>When designing your modules, you normally would discuss inside the team some general architectural patterns and approaches that your future modules supposed to share. If you go further, you may even write down some guidelines on how the future modules should be created, structured and maintained. That will bring more <strong>architectural and/or code style consistency</strong> to your code base that increases the productivity even more in future.</p>
<p>You can refactor some small pieces of the <strong>legacy ObjC code</strong> that just don’t fit your new modular frame of the project.</p>
<p>Of course, you should understand that making several migrations on the same time is not a right thing to do. As well as fixing too much of technical debt simultaneously. So, if you have some small things here and there, go ahead and fix them on the way along with modularization. But if the side fixing of something requires too much additional effort, please put it aside and pick it up later when modularization is over. I know how difficult it can be and how big is the temptation to cure all the diseases at once. I’m sure you will have enough work to do with modularization only, so don’t unnecessary increase the scope even more. All your dirty legacy will be cut into pieces and nicely split into modules together with the rest of your code, so it will be anyway easier to handle it after the modularization.</p>
<p> </p>
<hr />
<p>I hope you liked this piece of reading. If you have any questions, suggestions or corrections you can reach me out <a href="https://twitter.com/dmtopolog">on Twitter</a></p>Dmitrii IvanovIn this article we will discuss the problem that can be solved by modularity and how exactly modularity can make you project much better thing to deal with.Do protocols break Single Responsibility Principle?2020-10-06T00:00:00+00:002020-10-06T00:00:00+00:00http://dmtopolog.com/do-protocols-break-srp<p>This post continues the ideas mentioned in the previous ones from this series:</p>
<ul>
<li><a href="/protocol-extensions/">Protocol extensions</a></li>
<li><a href="/protocol-faces/">Several faces of protocols</a></li>
<li><a href="/pitfalls-of-protocol-extensions/">Pitfalls of protocol extensions</a></li>
</ul>
<hr />
<p><strong>UPD: (Nov 2022)</strong> Now after the number of changes in Swift 5.6 and 5.7 something changed… See <a href="/protocol-paradigm-shift">Shift in the Protocol-paradigm</a></p>
<hr />
<p>The power of the protocols in Swift is their main weakness. There are too many use cases for protocols, they try to embrace too many different language features, <strong>too many responsibilities</strong>.</p>
<p>I’m quite far from designing programming languages, but as I understand it there is a kind of a dichotomy regarding every language feature.</p>
<p>On the one hand, every feature has to be designed as generic as possible. So with the least amount of features you can cover all the use cases for the language users. The language is usually considered too verbose if it has too many language concepts. Also the more concepts it has the harder it is to learn it. Everybody wants to attract more people to use the language (it’s not as simple of course, but there is much truth in this statement).</p>
<p>On the other hand, with too generic features come some specific problems. One of them is breaking the Single Responsibility Principle (SRP).</p>
<p>Let’s remember why we all appreciate SRP in the first place. Why do we apply it to our functions and data structures:</p>
<ul>
<li>It makes code simpler and easier to comprehend</li>
<li>It minimizes the number of side effects</li>
</ul>
<p>SRP is quite a universal design idea so in language design, it means exactly the same.</p>
<p><em>Here we omit all that is related to the internal implementation of the feature and just focus on the external side of the deal: the language as a tool.</em></p>
<p>Stuffing several concepts in one language feature breaks the SRP. The feature gets more difficult to understand. It is like a word with several meanings: you need to grasp a fair amount of context to know exactly what is the meaning in this very case. It increases complexity and requires more cognitive resources.</p>
<p>By breaking SRP we increase the number of side effects. And in the case of language concepts, it means that one use case for the feature language can interfere with another.</p>
<p>That’s basically what happened with <strong>inheritance</strong>. We use one term (and one language concept) for two different things: <em>interface inheritance (subtyping)</em> and <em>implementation inheritance (code inheritance)</em>. When not separated in the language, they cause some troubles (if you are not familiar with the problem you can <a href="https://www.google.com/search?q=inheritance+is+evil">find a lot of discussions in Google</a>). Developers mix one with another in the same data structure because it’s so easy to do that. You might stick to the interface inheritance in your class and refuse to use the implementation one to keep your code clean. But one day you decide to override one of the methods in a subclass (not a big deal). Then you put instances of a superclass and subclasses into the same array (we like generic code, right?). But next day you already find yourself down-casting a superclass</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">animal</span> <span class="k">in</span> <span class="n">animals</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">dog</span> <span class="o">=</span> <span class="n">animal</span> <span class="k">as?</span> <span class="kt">Dog</span> <span class="p">{</span>
<span class="c1">// you can pat it</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>That’s not where you want to be. (It’s not like down-casting is bad, but you might want to think about your design again if you have to do it).</p>
<p>Back to protocols.</p>
<p>We mentioned all the different responsibilities of protocols and protocol extensions in <a href="https://dmtopolog.com/protocol-faces/">Several faces of protocols</a> and <a href="https://dmtopolog.com/protocol-extensions/">Protocol extensions</a>.</p>
<p>It’s easy to mix several roles in one single protocol. Let’s say, you start with a simple dynamic interface. Then you find some common implementation patterns in types conforming to the protocol so you add an extension and put all this shared code into it (why duplicate the implementation in different places). Then you put some additional functions to an extension that is not in the protocol at all (some free functionality, nice!). It’s already quite a complicated piece of logic, but you may subclass a class which adopts this protocol, or inherit the protocol in another protocol, or even make another extension to this child protocol where you override some functions…</p>
<p>In these cases, you eventually have this Frankenstein monster with several responsibilities, quite a complex and even confusing logic, some hidden functionality. But the worst thing is your inability to change a small thing because it has side effects and breaks something else (related to a different responsibility of the object).</p>
<p>You may ask:</p>
<blockquote>
<p>What after all SRP has to do with side effects in protocols? What are “side effects” in the context of protocols?</p>
</blockquote>
<p>When the object has several responsibilities it has different pieces of internal logic responsible for different things. But as it’s still one object you cannot fully separate these functional parts. These parts are connected, usually they have some shared state, or code, or i/o channels… and as far as there is something shared, you have a possibility for side effects. You change something related to one piece of functionality, but it also relates to something else (maybe something you don’t expect).</p>
<p>The simplest example regarding protocols may be when you have your protocol as a dynamic interface and use it in the heterogenous array. Then you start using the same protocol as a compile-time constraint. Then you want to add a self/associatedType requirement… and boom - you cannot do that…</p>
<blockquote>
<p>Error: Protocol ‘YourProtocol’ can only be used as a generic constraint because it has Self or associated type requirements</p>
</blockquote>
<p>Here is your side effect. These two ways of using protocol don’t get along with each other well. The compiler helps you here, and doesn’t allow to get too much into mixing this protocol flavours (because it makes its - compiler’s - job impossible). So you need to redesign your code to either use one or another. But in other cases (let’s say mixing default implementation and additional functionality in protocol extension) compiler will not help you. The code compiles and works, but it’s not so nice to maintain further.</p>
<p>We already mentioned that this multi-concept situation is not good for readability either. Sometimes when looking at the specific protocol it’s hard to tell what is its purpose (is it used with an extension as a mixin, or as a generic constraint, or for the dynamic polymorphism?). Would it really be more cognitive load to handle several different language features, then to handle the one, but with several flavours and separate use cases (which sometimes don’t work along so well)?</p>
<p>You may say:</p>
<blockquote>
<p>Stop blaming the language if you just cannot use it properly.</p>
</blockquote>
<p>And that would be partly reasonable, but not entirely.</p>
<p>If we are completely aware of all the possible ways of using the feature and all the subtle differences between them, if we can perfectly separate one from another and keep it in mind all the time we work with this code, if we don’t make shortcuts and always write clean code… then yeah, there is nothing wrong with such swiss-army-knife features. But we are not that good.</p>
<p>If we have a choice between making things quickly and making things properly sometimes we pick the first option (even the best of us… it’s just the matter of frequency).</p>
<p>But what if we wouldn’t have such a possibility to screw things up? Wouldn’t it be better to have different language tools for different use cases? Is it possible to split already released (and highly appreciated) language feature into several independent ones? In the case of protocols, I don’t think it’s possible at this stage of language development.</p>
<p>But let me briefly remind you that such a “split” already happened in Swift history. Some of you might remember that before Swift 2.2 there were two different language features named ‘typealias’. But then it was decided to separate them. That’s how we got <code class="language-plaintext highlighter-rouge">associatedtype</code>. (You can brush up the memories <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0011-replace-typealias-associated.md">here</a>)</p>
<p>No doubt that protocols are much more massive in terms of compiler logic and usage in our code. It’s also clear that what was possible in Swift 2 times might not feasible now. I’m just saying that sometimes this situation when several concepts appeared to be encapsulated in one feature it might be realized and fixed at some point.</p>
<p>The conclusion here is simple: let’s try to be more aware of what we use under the hood when we write our code. It’s great to know our tools and language possibilities, but even better to be conscious about the language concepts underneath.</p>
<p>And remember about SPR. It’s like with drinks: better not to mix ;-) Cheers! 🍻</p>Dmitrii IvanovIn previous posts we've mentioned all the different use cases for this language feature. Now let's consider some hidden complications which we get together with all the power.Pitfalls of protocol extensions2020-09-29T00:00:00+00:002020-09-29T00:00:00+00:00http://dmtopolog.com/pitfalls-of-protocol-extensions<p>This post is basically a continuation of the topic started in the previous ones.</p>
<p>In <a href="/protocol-extensions/">Protocol Extensions</a> we’ve mentioned all the different use cases for this language feature. Now let’s consider some hidden complications which we get together with all the power.</p>
<p>First of all, most of the drawbacks of general swift extensions are fair here as well. (I discussed them in <a href="/dark-side-of-extensions/">Dark side of extensions in Swift</a>).</p>
<p>But there are also some additional ones.</p>
<h3 id="protocol-extensions-bring-extra-complexity">Protocol extensions bring extra complexity</h3>
<p>One of the main drawbacks of the extensions, in general, is the implicitness of added functionality. When you look into the object’s implementation you have no idea about it’s extended functionality. The extension may reside in a separate file far away in the project tree. So if you are not too familiar with the code you are likely to be unaware about it.</p>
<p>In the case of protocol extensions, the situation might be even more complicated. We have a protocol, we have its extension and we have a data type which conforms to this protocol (maybe this conformance also implemented via a data-type extension).</p>
<p>Compare the following 4 cases. The same function call on a structure can be backed up by different chains of object relations:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">collection</span> <span class="o">=</span> <span class="kt">MyCollection</span><span class="p">()</span>
<span class="n">collection</span><span class="o">.</span><span class="nf">filter</span><span class="p">()</span>
</code></pre></div></div>
<p><img src="/images-posts/2020-09-29-pitfalls-of-protocol-extensions/chain1.png" alt="" /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyCollection</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">filter</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// functionality</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><img src="/images-posts/2020-09-29-pitfalls-of-protocol-extensions/chain2.png" alt="" /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyCollection</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">extension</span> <span class="kt">MyCollection</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">filter</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// functionality</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><img src="/images-posts/2020-09-29-pitfalls-of-protocol-extensions/chain3.png" alt="" /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyCollection</span><span class="p">:</span> <span class="kt">Filtrable</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">protocol</span> <span class="kt">Filtrable</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">extension</span> <span class="kt">Filtrable</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">filter</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// functionality</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><img src="/images-posts/2020-09-29-pitfalls-of-protocol-extensions/chain4.png" alt="" /></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyCollection</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">extension</span> <span class="kt">MyCollection</span><span class="p">:</span> <span class="kt">Filtrable</span> <span class="p">{}</span>
<span class="kd">protocol</span> <span class="kt">Filtrable</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">extension</span> <span class="kt">Filtrable</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">filter</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// functionality</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>When you investigate or debug such code, you usually manipulate such chains of connections from the object to its function. And at every moment you have to keep many of them in your head. And we all know that the context we are able to keep in mind is limited. So every extra connection, as well as every level of abstraction, makes it more difficult to mentally juggle entities when we reason about the code.</p>
<p>A protocol extension also brings us some extra actions in the IDE when we investigate this <code class="language-plaintext highlighter-rouge">Object - Functionality</code> relations.</p>
<p>Let’s imagine you want to gather information about the object’s functionality. Other than the basic implementation it might conform to a number of protocols. Possibly, you have no idea if they are just interfaces or these protocols also have extensions. So you check them one by one, finding out which have extensions and what it inside these extensions. Only after that finally, you get the full picture of the object’s behaviour.</p>
<p>The other way around: you want to jump to a function definition. You click to a method call in the code (<code class="language-plaintext highlighter-rouge">collection.filter()</code>) and it leads you to some protocol extension. If you know Xcode well enough, you cannot be sure that there is a connection between the object you are interested in and the functionality in this extension. Sometimes our IDE gets confused and redirects us to some other function with the same signature. So you need to get back to the object and find if it conforms to that extended protocol.</p>
<h3 id="more-use-cases-for-protocols">More use cases for protocols.</h3>
<p>In <a href="https://dmtopolog.com/protocol-faces/">Several faces of protocols</a> we mentioned different use cases which shape different flavours of protocols. Extensions add a couple more: <strong>default method implementation</strong>, <strong>additional functionality</strong>.</p>
<p>That’s great how many use cases protocols have in Swift. But as a result, when you see a protocol in code, you cannot say for sure WHAT exactly are you looking at. Is it an interface or a compile-time constraint? Does it have an extension somewhere in the project? What is there in the extension: some default implementations (which ones?) or maybe even some additional functionality?</p>
<p>Honestly, even generics seem to be a more straight forward language concept in Swift than protocols.</p>
<h3 id="static-nature-of-additional-functionality">Static nature of additional functionality.</h3>
<p>In <a href="https://dmtopolog.com/protocol-extensions/">Protocol Extensions</a> we described a difference between <strong>default implementation</strong> and <strong>additional functionality</strong>. Now let’s focus on the static nature of the latter one.</p>
<p>Imagine the following situation:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// we have a var of a type `NewsProvider`</span>
<span class="k">var</span> <span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span>
<span class="c1">// at some point we assign it an instance of a specific type</span>
<span class="n">newsProvider</span> <span class="o">=</span> <span class="kt">RussiaTodayNewsProvider</span><span class="p">()</span>
<span class="c1">// at some point we do the filtering</span>
<span class="k">let</span> <span class="nv">filteredNews</span> <span class="o">=</span> <span class="n">newsProvider</span><span class="o">.</span><span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="n">filter</span><span class="p">)</span>
<span class="c1">// NewsProvider.applyFilter() implementation is used</span>
</code></pre></div></div>
<p>Even if <code class="language-plaintext highlighter-rouge">RussiaTodayNewsProvider</code> has its own implementation of the function it won’t be called because the type of <code class="language-plaintext highlighter-rouge">newsProvider</code> is <code class="language-plaintext highlighter-rouge">NewsProvider</code>. And as far as <code class="language-plaintext highlighter-rouge">NewsProvider</code>-extension has this method it will be used.</p>
<p>If you initially define a variable with specific type instead of the protocol the specific implementation will be used instead of the default one.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">RussiaTodayNewsProvider</span>
<span class="n">newsProvider</span> <span class="o">=</span> <span class="kt">RussiaTodayNewsProvider</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">filteredNews</span> <span class="o">=</span> <span class="n">newsProvider</span><span class="o">.</span><span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="n">filter</span><span class="p">)</span>
<span class="c1">// RussiaTodayNewsProvider.applyFilter() implementation is used</span>
</code></pre></div></div>
<p>More examples on that can be found <a href="https://medium.com/better-programming/swift-why-you-should-avoid-using-default-implementations-in-protocols-eeffddbed46d">here</a> and <a href="https://medium.com/@leandromperez/protocol-extensions-gotcha-9ef1a42c83b6">here</a>. And <a href="https://oleb.net/blog/2016/06/lily-ballard-swift-dispatch/">here</a> is a deeper (but still quite short) explanation of the reasons by Ole Begemann.</p>
<h3 id="changes-in-function-signature">Changes in function signature.</h3>
<p>Imagine you have a protocol extension with the default functionality and you have some data types which have their own implementation of this functionality.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// default implementation</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">RussiaTodayNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// specific implementation... cause you know...</span>
<span class="c1">// these official Russian news agencies require some extra filtering</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The first obstacle which might come to your mind is that you are not able to use the default implementation inside the overridden one. In case of filtering it could make sense if we want to apply some additional processing over the default implementation. So if that’s the case and we don’t want to duplicate code we need to redesign something here. But let’s leave it as it is for now.</p>
<p>At some point you change something regarding this <code class="language-plaintext highlighter-rouge">applyFilter()</code>-function in the protocol or the extension (rename the method, change its signature,…). Let’s say your smart ML-guys trained a model which can filter the fake news out of the news feed, and we might want to test it on some news aggregators:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">,</span> <span class="nv">removeFakeNews</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// default implementation</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Of course, the compiler makes sure that the protocol and the extension are aligned in it (in case we forgot to change it there). But if you forgot about the poor <code class="language-plaintext highlighter-rouge">RussiaTodayNewsProvider</code> with its unique implementation, you cannot expect any help. The compiler will just treat the old (unchanged) implementation there as a separate method (which won’t be called at all anymore).</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span>
<span class="n">newsProvider</span><span class="o">.</span><span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">,</span> <span class="nv">removeFakeNews</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
</code></pre></div></div>
<p>When in the view model you will call filtering for <code class="language-plaintext highlighter-rouge">RussiaTodayNewsProvider</code> the default implementation will be invoked, not the specific one. Everything will work, but the result will be not the one you would expect.</p>
<h3 id="composition-over-inheritance-or-is-it">Composition over inheritance… or is it?</h3>
<p>It’s quite trendy in all the software development to talk about <a href="https://en.wikipedia.org/wiki/Composition_over_inheritance">composition over inheritance</a> (let me omit it here).</p>
<p>It’s quite trendy in Swift community to consider extensions as a good example of this principle. With the extension, we can share code and apply additional functionality without resorting to subclassing. So extensions help you to avoid <a href="https://www.google.com/search?q=inheritance+is+evil">such evil</a> as subclassing. That’s true indeed (if you DO consider subclassing an “evil”) and that was one of the selling points in that <a href="https://developer.apple.com/videos/play/wwdc2015/408/">famous Dave Abrahams’ talk</a>. Extensions bring you an ability to reuse functionality of another entity without subclassing it. Moreover, you can do it with value types (which cannot be “subclassed” by definition).</p>
<p>The thing is that protocol extensions are not closer to <a href="https://en.wikipedia.org/wiki/Composite_pattern">composition</a> than inheritance. Extensions do alleviate some inheritance-related pains: the connection between the data type and the extension is a bit looser, you don’t have big chains of inheritance, you don’t inherit any state from a protocol, you can conform to several extended protocols (simulating “multiple inheritance”).</p>
<p>Still protocol extension to me look closer to inheritance than to composition. Consider:</p>
<ul>
<li>you implicitly add some functionality to your data type (just one conformance/inheritance line of code)</li>
<li>the connection between your data type and the extension still quite tight (compile-time connection, no real-time flexibility)</li>
<li>the functionality of your data type is spread across several objects (base implementation, data-type extensions, protocol extensions), so it’s hard to grasp the full picture of an object</li>
<li>you might rely on the extended functionality in the basic implementation, so if something changes there you cannot guarantee your code keeps on working as intended.</li>
</ul>
<p>Doesn’t it remind good old inheritance?</p>
<p>Another complaint people have to inheritance is testability. When you test a subclass you cannot decouple it from all its parent classes, so you have to test all the hierarchy altogether. Let’s take a look at what we have in case of the protocol extensions.</p>
<h3 id="testability">Testability</h3>
<p>If you like your code to be covered with tests, protocol extensions will give you some hard time. As we mentioned earlier we have a compile-time coupling between the extended functionality and the base implementation, no real-time dynamism. It’s hard to separate these two pieces and test them separately.</p>
<p>Let’s get back to our News app at the point when we moved <code class="language-plaintext highlighter-rouge">applyFilter()</code> function to the extension removing it from the protocol (so we have an extension with <code class="language-plaintext highlighter-rouge">additional functionality</code>):</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">extension</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">NewsFeedViewModel</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span>
<span class="c1">// ...</span>
<span class="kd">func</span> <span class="nf">filterNews</span><span class="p">()</span> <span class="p">{</span>
<span class="n">newsProvider</span><span class="o">.</span><span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="n">filter</span><span class="p">)</span>
<span class="c1">//...</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here we cannot test the view model independently from the <code class="language-plaintext highlighter-rouge">NewsProvider</code>-extension. We want to test the logic inside <code class="language-plaintext highlighter-rouge">filterNews()</code> method. We can mock the <code class="language-plaintext highlighter-rouge">newsProvider</code>, but because of the static nature of the additional functionality in the protocol extension (static method dispatch), there is no way to override <code class="language-plaintext highlighter-rouge">applyFilter()</code> method. And as it’s impossible to mock it, you cannot test if the view model calls it in a proper time. So you either skip to test it, or to test the filtering itself together with <code class="language-plaintext highlighter-rouge">filterNews()</code> method.</p>
<p>Testing the functionality of the extension itself is possible. But in order to do that you need to create a dummy class, make it conform to the protocol, make some dummy implementation of all the protocol’s functions not implemented in the extension. After that, you will be testing this dummy class.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">DummyProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="c1">// here you need to implement all the protocol methods</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">sut</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="o">=</span> <span class="kt">DummyProvider</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">sut</span><span class="o">.</span><span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="n">filter</span><span class="p">)</span>
<span class="kt">XCTAssertEqual</span><span class="p">(</span><span class="n">expectedResult</span><span class="p">,</span> <span class="n">result</span><span class="p">)</span>
</code></pre></div></div>
<p>In the end, you do test what you want to test, but it’s not so convenient.</p>
<h2 id="some-alternative-thinking">Some alternative thinking</h2>
<p>Sometimes protocol extensions as a feature remind me of a singleton. This pattern is also very convenient to use, it makes the functionality much more accessible and saves you a lot of extra lines of code. But most of us know that it comes at a high price.</p>
<p>Here are some principles which help me not overuse protocol extensions:</p>
<ul>
<li>
<p>Start with a plain composition and move to protocol extensions only when you see some clear benefits in it. Protocol extensions are fancier, more interesting to write, even a bit less code. But does it really overweight all the drawbacks I mentioned before?</p>
</li>
<li>
<p>Don’t use protocol extensions just in sake of following some abstract concepts (protocol-oriented approach, to be “Swifty”, composition over inheritance). Consider readability, explicitness and self-explanatory as more important features of your code. In a lot of cases when you use protocol extensions you decrease these qualities.</p>
</li>
<li>
<p>If you feel like you need some optional methods in your protocol, consider splitting this protocol into parts. Seems like you already have at least two groups of functions (required and optional) in one protocol. Follow the <a href="https://en.wikipedia.org/wiki/Interface_segregation_principle">Interface segregation principle</a>.</p>
</li>
<li>
<p>Try to avoid changing <strong>default implementation</strong> into <strong>additional functionality</strong> in your protocol extensions. In case if you have some default implementation which nobody overrides, instead of removing the function declaration from the protocol consider redesign this functionality into a separate entity. Switch to a plain composition.</p>
</li>
<li>
<p>Don’t mix different protocol flavours in one. Keep your interfaces, compile-time constraints, and traits in separate objects. A lot of problems started when different functions are mixed in one object (protocol). If you mix dynamic interfaces with compile-time constraints or default implementation with the additional functionality, you are likely to be in trouble. If you still want to use all the powers of protocol extensions learn to distinguish them and separate. You can even consider using different naming conventions: “NewsProviderInterface”, “FilteringTrait”, and so on.</p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p><img src="/images-posts/2020-09-29-pitfalls-of-protocol-extensions/scales.png" width="200" /></p>
<p>I think as always it’s all about the trade-offs.</p>
<p>Extensions are very powerful language concept which can help you to write more generic and reusable code. That’s a benefit.</p>
<p>Extensions bring additional complexity to our code. Protocol extensions bring even more complexity because of their different flavours/faces. That’s a drawback.</p>
<p>Then it’s up to you to set the priorities. Just remember that usually there are several solutions/approaches/patterns that you can use for any task.</p>
<p>… And please don’t be afraid that your code becomes less “Swifty” or less “protocol-oriented”. It doesn’t.</p>Dmitrii IvanovIn previous posts we've mentioned all the different use cases for this language feature. Now let's consider some hidden complications which we get together with all the power.Protocol extensions2020-09-22T00:00:00+00:002020-09-22T00:00:00+00:00http://dmtopolog.com/protocol-extensions<p>When people speak about how powerful protocols are in Swift, in a lot of cases they consider protocol extensions as part of this power. That’s unfair because it’s a separate language feature, and an interesting one. Here we will detach and dissect it.</p>
<h3 id="data-type-extensions">Data type extensions</h3>
<p>Before talking about the “protocol extensions” let’s say a couple of words about the extensions in general (as a starting point).</p>
<p>Extensions is a powerful concept which was inherited from ObjC but there it’s called “category” (there are also “extensions” in ObjC, but it’s <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocCategories.html">a slightly different animal</a>)</p>
<p>Extensions let you add some functionality to the data types you don’t own. With the extension you can not only add new functions but also computed instance or type properties, provide new initializers or make a type to conform a protocol (more about that in the <a href="https://docs.swift.org/swift-book/LanguageGuide/Extensions.html">official docs</a>)</p>
<p>However, people also like to add extensions to their own types for reasons like grouping and separating the logic (some examples <a href="https://www.natashatherobot.com/using-swift-extensions/">here</a> or <a href="https://cocoacasts.com/four-clever-uses-of-swift-extensions">here</a>).</p>
<h2 id="protocol-extensions">Protocol extensions</h2>
<p>Protocol extensions are different. You cannot “extend” a protocol because by definition a protocol doesn’t have an implementation - so nothing to extend. <em>(You could say that we “extend a protocol WITH some functionality”, but even an extended protocol is not something we can apply a function to.)</em> Instead you still extend a data type, but through the conformance to a protocol.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">UITableViewDataSource</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="c1">// extension to a data type</span>
<span class="kd">extension</span> <span class="kt">ViewController</span><span class="p">:</span> <span class="kt">UITableViewDataSource</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="c1">// extension to a protocol</span>
<span class="kd">extension</span> <span class="kt">UITableViewDataSource</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>As well as protocols themselves, protocol extensions also have several faces. The difference may seem subtle, but in reality, it makes quite a big difference.</p>
<h3 id="default-implementation">Default implementation</h3>
<p>First of all people use protocol extensions for default method implementation.</p>
<p>Let’s say you have a protocol with couple of methods and some structs conforming to it:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">ReutersNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">AssociatedPressNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Fetching is different for each news provider (different urls, data parsing,…) but filtering might be identical. Hence to avoid code duplication we move this functionality into the protocol extension:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">ReutersNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">AssociatedPressNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now all the data type which conform to <code class="language-plaintext highlighter-rouge">NewsProvider</code> get this functionality for free, without any duplication of the logic. In case you have some special news provider which needs to use some other filtering logic you can easily redefine it:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">RussiaTodayNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// specific implementation... cause you know...</span>
<span class="c1">// these official Russian news agencies require some extra filtering </span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="optional-protocol-functions">Optional protocol functions</h4>
<p>You can also use protocol extensions to make optional functions in your protocol. Lets say some of the news providers offer a photo feed.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="c1">//required</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span>
<span class="c1">// optional</span>
<span class="kd">func</span> <span class="nf">fetchPhotos</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">Photo</span><span class="p">]?</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchPhotos</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">Photo</span><span class="p">]?</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>That’s still the same <code class="language-plaintext highlighter-rouge">default implementation</code> use case, but we provide empty implementation by default. And if the news provider is capable of fetching photos it overrides this function. So the specific implementation will be used instead of the default one.</p>
<p>In our client code, we use something like that to do something if there is some implementation.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="k">let</span> <span class="nv">photos</span> <span class="o">=</span> <span class="n">newsProvoder</span><span class="o">.</span><span class="nf">fetchPhotos</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">showPhotoFeed</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">photos</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="additional-functionality">Additional functionality</h3>
<p>Let’s get back to <code class="language-plaintext highlighter-rouge">func applyFilter(filter: Filter)</code> which we moved from the concrete provider-structs to the NewsProvider-extension.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>At some point we might notice that there is no providers that override this method, so we can remove it from the protocol and just leave it in the extension. Everything will be working the same.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">applyFilter</span><span class="p">(</span><span class="nv">filter</span><span class="p">:</span> <span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now it’s not a <code class="language-plaintext highlighter-rouge">default implementation</code> but an <code class="language-plaintext highlighter-rouge">additional functionality</code> instead. The former is something that you can redefine in your object. But the latter is something that you get (for free) together with the protocol and you cannot do anything with it.</p>
<p>In this case protocol extension works like data type extension, but functionality is added in a more implicit way (more about it later).</p>
<p>Additional functionality in protocol extensions is exactly what lets us implement a data-type agnostic behavior. And then share it between several different data types. Each data type just declares the conformance to the protocol and gets the functionality from the protocol extension. That’s something similar to <a href="https://en.wikipedia.org/wiki/Trait_(computer_programming)">traits</a> and <a href="https://en.wikipedia.org/wiki/Mixin">mixins</a>.</p>
<h3 id="static-vs-dynamic">Static vs dynamic.</h3>
<p>Why is it important to distinguish these two types of functions in protocol extensions?</p>
<p>The thing is that in case of the <strong>default implementation</strong> <em>dynamic method dispatch</em> is being used. Whereas for the <strong>additional functionality</strong> it is <em>static method dispatch</em>. (If you need a deep dive into <strong>method dispatch</strong> in Swift read <a href="https://www.rightpoint.com/rplabs/switch-method-dispatch-table">this great article</a>. More details about this very difference in protocol extensions can be found <a href="https://oleb.net/blog/2016/06/lily-ballard-swift-dispatch/">here</a>.)</p>
<p>In practice it has some interesting consequences:</p>
<ul>
<li>additional functions are tightly linked to the function declaration (as far as the function is not in a protocol, there is no separate declaration at all)</li>
<li>they exist in the heap even when the data type exists in the stack</li>
<li>they are static, so they are faster and they can be optimized by the compiler</li>
<li>they are not dynamic, so they cannot be overwritten in runtime</li>
</ul>
<h3 id="to-be-continued">To be continued</h3>
<p>Here we discussed what the animal is “protocol extensions”. We mentioned 2 types of functions and the general differences between them. In the <a href="/pitfalls-of-protocol-extensions/">next article</a> we are diving more into practical cases and pitfalls regarding using this feature in our code. Stay tuned ;-).</p>Dmitrii IvanovWhen people speak about how powerful protocols are in Swift, in a lot of cases they consider protocol extensions as part of this power. That’s unfair because it’s a separate language feature, and an interesting one. Here we will detach and dissect it.Dark side of extensions in Swift2020-09-14T00:00:00+00:002020-09-14T00:00:00+00:00http://dmtopolog.com/dark-side-of-extensions<p>Extension is a very powerful concept in Swift. It has several different applications in our code: we can extend the data types we don’t own, we can extend our own ones, we can separate the functionality and protocol conformance. Each one of them gives us additional capabilities and extra powers, but some of them come together with drawbacks which we have to consider.</p>
<h2 id="extensions-are-implicit">Extensions are implicit</h2>
<p>First of all, everything I say in this section is very subjective. Matters like “readability”, “clearness” and “explicitness” are different for everybody.</p>
<p>The idea of the extension as a concept is that you just add some functionality <code class="language-plaintext highlighter-rouge">X</code> to the object <code class="language-plaintext highlighter-rouge">Y</code>. This object <code class="language-plaintext highlighter-rouge">Y</code> is already a piece of some complete encapsulated logic. But we want more. For some reason, we decide to structure this additional logic to look like a part of the object <code class="language-plaintext highlighter-rouge">Y</code>.</p>
<p>Sometimes it is indeed some functionality which logically (another subjective term) belongs to the entity you extend. So when you see <code class="language-plaintext highlighter-rouge">Y.X()</code> in code you understand clearly what it means “to do <code class="language-plaintext highlighter-rouge">X</code> with <code class="language-plaintext highlighter-rouge">Y</code>”.</p>
<p>Consider <code class="language-plaintext highlighter-rouge">shuffle()</code> for the array or <code class="language-plaintext highlighter-rouge">toggle()</code> for bool <em>(both of these functions were not parts of a standard library in the early days, so lots of folks had them in the extensions)</em>. There are some good examples from Paul Hudson <a href="https://www.hackingwithswift.com/articles/141/8-useful-swift-extensions">here</a> or John Sundell <a href="https://www.swiftbysundell.com/articles/writing-reusable-swift-extensions/">here</a>.</p>
<p>When you see something like that it’s pretty self-explanatory:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">phrase</span> <span class="o">=</span> <span class="s">"The rain in Spain"</span>
<span class="nf">print</span><span class="p">(</span><span class="n">phrase</span><span class="o">.</span><span class="n">wordCount</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">items</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">decode</span><span class="p">([</span><span class="kt">TourItem</span><span class="p">]</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">from</span><span class="p">:</span> <span class="s">"Tour.json"</span><span class="p">)</span>
<span class="n">article</span><span class="o">.</span><span class="nf">cacheOnDisk</span><span class="p">()</span>
</code></pre></div></div>
<p>In other situations that additional functionality <code class="language-plaintext highlighter-rouge">X</code> semantically might not look like it should belong to the object <code class="language-plaintext highlighter-rouge">Y</code>. So <code class="language-plaintext highlighter-rouge">Y.X()</code> doesn’t give you the full context when you see it in code. <em>(It’s fair not only for extensions but for regular object functions as well. But with extensions it’s much easier to add something which shouldn’t belong to the extended entity)</em></p>
<p>For instance, imagine somebody added a smart extension to <code class="language-plaintext highlighter-rouge">Double</code>, you have no idea about it and just found such code:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">length</span> <span class="o">=</span> <span class="mf">25.4</span><span class="o">.</span><span class="n">mm</span>
<span class="k">let</span> <span class="nv">aMarathon</span> <span class="o">=</span> <span class="mi">42</span><span class="o">.</span><span class="n">km</span> <span class="o">+</span> <span class="mi">195</span><span class="o">.</span><span class="n">m</span>
</code></pre></div></div>
<p>What do you think are the units of these constants? Is <code class="language-plaintext highlighter-rouge">length</code> in millimeters? What about <code class="language-plaintext highlighter-rouge">aMarathon</code>? How can you further use them? Is it some value for UI, or something to encode and pass to the backend?</p>
<p>To answer these questions and be able to use such extensions in code you need to check the implementation, because there is some missing (implicit) logic here:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Double</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">km</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span> <span class="o">*</span> <span class="mf">1_000.0</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">m</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">cm</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">mm</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span> <span class="o">/</span> <span class="mf">1_000.0</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">ft</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span> <span class="o">/</span> <span class="mf">3.28084</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>What we miss is “conversion to meters”. You cannot get this information from the name of the function.</p>
<p>And if you don’t use this extension every day, quite possible you might need to do it next time you see such code.. What was that meters or centimeters? You might even add a quick-help comment to fill in this logical gap and save yourself some time in the future:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
Conversion to METERS
*/</span>
<span class="kd">extension</span> <span class="kt">Double</span> <span class="p">{</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>…but you still need to jump to a definition every time you are not sure. There is a piece of implicit logic here.</p>
<p>IMHO, that’s not the best way to use extensions… even though that’s what Apple suggests in the <a href="https://docs.swift.org/swift-book/LanguageGuide/Extensions.html#ID152">official documentation</a></p>
<p>It would be more explicit to create some primitive converter or a global static conversion function. Which option is more comprehensible?</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">aMarathon</span> <span class="o">=</span> <span class="mi">42</span><span class="o">.</span><span class="n">km</span> <span class="o">+</span> <span class="mi">195</span><span class="o">.</span><span class="n">m</span>
<span class="k">let</span> <span class="nv">aMarathon</span> <span class="o">=</span> <span class="kt">Converter</span><span class="o">.</span><span class="nf">kmToMeters</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="o">+</span> <span class="mi">195</span>
</code></pre></div></div>
<h2 id="illusory-separation">Illusory separation</h2>
<p>When you introduce an extension you consider the boundary between the base implementation and the extension. In the case of extending the objects, you don’t own this boundary is quite physical. But if you own the base implementation and it’s even in the same module, the boundary gets quite weak.</p>
<p>If the boundary is weak it tends to disappear. <em>(Partly I tried to explain it <a href="https://dmtopolog.com/modularity-1-boundaries/">here</a>)</em> The entropy of every system tends to increase. Like two gases or two liquids of the same density, two pieces of logic tend to mix without a physical boundary.</p>
<p>You might use extensions <a href="https://www.natashatherobot.com/using-swift-extensions/">to group separate pieces of functionality</a>. In case of separating protocol conformance or a private/public logic the boundary is always clear. But sometimes it’s not clear how to assign something to a group or a category.</p>
<p>Let’s say we have a blog post data model and we prefer to separate all the persistence-related logic in an extension:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">BlogPost</span> <span class="p">{</span>
<span class="c1">// general logic</span>
<span class="p">}</span>
<span class="c1">// Persistence</span>
<span class="kd">extension</span> <span class="kt">BlogPost</span> <span class="p">{</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">write</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But at some point we got validation logic which should be applied to the post before it’s being written on disk. On one side, it’s used exclusively for writing on disk. On another side, it’s not directly related to working with disk. Should we put it to the base implementation or to the extension?</p>
<p>Another confusion is with stored properties. We cannot add them to extensions. So when we have some extension-related properties we have to keep them outside of the extension - in the main data-type implementation.</p>
<p>Let’s say we want to add a directory path to our Persistence-extension, so we don’t need to pass the full path from the outside.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">BlogPost</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">directoryPath</span> <span class="o">=</span> <span class="s">"path/to/a/directory/"</span>
<span class="c1">// general logic</span>
<span class="p">}</span>
<span class="c1">// Persistence</span>
<span class="kd">extension</span> <span class="kt">BlogPost</span> <span class="p">{</span>
<span class="nf">init</span><span class="p">(</span><span class="k">for</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">path</span> <span class="o">=</span> <span class="n">directoryPath</span> <span class="o">+</span> <span class="n">id</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">write</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">path</span> <span class="o">=</span> <span class="n">directoryPath</span> <span class="o">+</span> <span class="k">self</span><span class="o">.</span><span class="n">id</span>
<span class="c1">// implementation</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>It kinda spoils the entire idea of logical separation, because now we have a piece of extension-related logic in the main implementation.</p>
<p><em>(ObjC runtime - the only alternative - may bring us more additional headache here, so it doesn’t worth to be used just to compensate this logic-separation flaw, imho)</em></p>
<p>If you add extensions to your own objects it might get even worse if you (or somebody from your team) start to use the extension functionality in the object’s implementation:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="c1">// main functionality</span>
<span class="kd">func</span> <span class="nf">anotherFunction</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">function</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">//...</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Sounds borderline impossible with a simple example. Why would one do that? Nonsense. But in a big project you might not remember all the layer/logic separation and what functionality belongs to which layer (to the object or to its extension). So when you implement <code class="language-plaintext highlighter-rouge">anotherFunction()</code> helpful autocomplete might suggest you to use <code class="language-plaintext highlighter-rouge">function()</code> and it might be exactly what you need.</p>
<p>As a result, your encapsulation is totally broken. Not only your extension relies on the base implementation (which kinda OK), but the other way around as well. These two pieces of logic get coupled too tightly. With extensions to your own objects, the boundary between them is quite vague by definition. But such interconnections make it just a formality.</p>
<h2 id="srp-violation">SRP violation</h2>
<p>Single responsibility principle (SRP) pushes us to divide our code (project, module, class) into a number of separate logical units. Each unit should have only one (in practice, as least as possible) responsibility(-ies).</p>
<p>An extension is not a separate logical entity. Its functionality logically belongs to its base data type. So even when you put an extension to a separate file or the base type resides in a different module, for the client code there is still no boundary at all.</p>
<p>So in fact all the functionality you add via the extensions are additional responsibilities of your base type.</p>
<p>Guess who has a physical boundary? A separate object does! Hence if you create a separate object instead of the extension in the majority of cases you will write the same amount of reusable code. But architectural-wise it will be cleaner.</p>
<p>Instead of having some generic persistence-extension to you data models you just wrap the same functionality into a <code class="language-plaintext highlighter-rouge">PersistenceHelper</code> (don’t even start to nag about the naming!), instead of caching extension - <code class="language-plaintext highlighter-rouge">CachingHelper</code>, instead of converting-extension - <code class="language-plaintext highlighter-rouge">Converter</code>, instead of the one which builds a network request out of the base model - separate <code class="language-plaintext highlighter-rouge">RequestBuilder</code>-object,… and so on.</p>
<p>I strongly believe that lots of small classes with narrow responsibilities are better than one fat object which knows how to do everything. Even if this fat object is logically perfectly separated between several files or modules.</p>
<h2 id="but-look-at-the-standard-library">But look at the standard library!</h2>
<p>In swift standard library there are plenty of extensions. People like to use it as proof that you should use as many extensions as possible. So if the swift core team doesn’t seem to care about all the drawbacks I mentioned here, why the others should.</p>
<p>First, let’s mention it again: these possible issues are the dark side of this language concept. And there is a bright one as well… maybe even several. So that’s the matter of balance and priority.</p>
<p>Second, the standard library is on a different level of abstraction than the code you write (in majority of cases). We create and use objects of a much higher grade of abstraction, which contain more context and which are more complex entities. The standard library has no layers of abstraction underneath - just the language itself. It is a much more generic thing which suppose to satisfy lots of different use cases.</p>
<p>We shouldn’t blindly copy everything that Apple does. What works there is not necessarily good for our projects (and the other way around).</p>
<h2 id="conclusion">Conclusion</h2>
<blockquote>
<p>With great power comes great responsibility
(Peter Parker… <a href="https://en.wikipedia.org/wiki/With_great_power_comes_great_responsibility">or some Frenchman from XVIII century</a>)</p>
</blockquote>
<p>I’m not talking you out of using extensions. I just want you to keep in mind all that nuances when using this language feature.</p>
<p>We didn’t mention protocol extensions here, because that’s a different topic. Hope to cover it in next posts, stay tuned!</p>Dmitrii IvanovExtension is a very powerful concept in Swift. It has several different applications in our code: we can extend the data types we don’t own, we can extend our own ones, we can separate the functionality and protocol conformance. Each one of them gives us additional capabilities and extra powers, but some of them come together with drawbacks which we have to consider.Several faces of protocols2020-09-02T00:00:00+00:002020-09-02T00:00:00+00:00http://dmtopolog.com/protocol-faces<p>“Protocols is one of the most powerful language features in Swift” - people say. Hard to argue. In Swift you can do a lot of different things with protocols. (Maybe even too many… but we will talk about it later).</p>
<p>Let’s try to look at them from a distance and understand what <code class="language-plaintext highlighter-rouge">language features</code> are masked by this simple word “Protocol” in Swift.</p>
<hr />
<p><strong>UPD: (Nov 2022)</strong> Now after the number of changes in Swift 5.6 and 5.7 something changed… See <a href="/protocol-paradigm-shift">Shift in the Protocol-paradigm</a></p>
<hr />
<p> </p>
<p><em>(I don’t want to overload you with the code examples here as there are plenty of them out there. But I need to illustrate some ideas with code (and I don’t want to just copy-past somebody’s examples… even if they are great ;-)).
So let’s consider some News Feed app that fetches and displays recent news as our playground.)</em></p>
<h2 id="1-protocol-as-a-dynamic-interface">1. Protocol as a dynamic interface</h2>
<p>First of all, protocol is a good old interface. It’s a contract which one object relies on and another object conforms to.</p>
<p>In our News App we have a <code class="language-plaintext highlighter-rouge">NewsProvider</code>-protocol and <code class="language-plaintext highlighter-rouge">FeedViewModel</code> which relies on this protocol.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">News</span><span class="p">]</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">FeedViewModel</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span>
<span class="k">var</span> <span class="nv">news</span> <span class="o">=</span> <span class="p">[</span><span class="kt">News</span><span class="p">]()</span>
<span class="kd">func</span> <span class="nf">reloadNews</span><span class="p">()</span> <span class="p">{</span>
<span class="n">news</span> <span class="o">=</span> <span class="n">newsProvider</span><span class="o">.</span><span class="nf">fetchNews</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We have several screens for different news agencies. For each agency we create a dedicated provider-class that conforms to the <code class="language-plaintext highlighter-rouge">NewsProvider</code>-protocol. So it can be injected into the view model and can be used as a data source.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ReutersNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">AssociatedPressNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">RIANovostiNewsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">FeedViewModel</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span>
<span class="k">var</span> <span class="nv">news</span> <span class="o">=</span> <span class="p">[</span><span class="kt">News</span><span class="p">]()</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">NewsProvider</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">newsProvider</span> <span class="o">=</span> <span class="n">newsProvider</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">viewModel</span> <span class="o">=</span> <span class="kt">FeedViewModel</span><span class="p">(</span><span class="nv">newsProvider</span><span class="p">:</span> <span class="kt">ReutersNewsProvider</span><span class="p">())</span>
</code></pre></div></div>
<p>As simple as that. We abstract the interface from the implementation so later we can use various implementations of the <code class="language-plaintext highlighter-rouge">NewsProvider</code> interface without view model feeling a difference. In lot’s of other languages it’s called directly “Interface”. In Objective-C it’s called “Protocol” and Swift inherited the name for this language concept there.</p>
<h3 id="what-do-we-gain-here">What do we gain here</h3>
<p>This application of protocols in Swift help you to implement <a href="https://en.wikipedia.org/wiki/Composite_pattern">composition</a> or <a href="https://en.wikipedia.org/wiki/Delegation_pattern">delegation</a> in your Swift code.</p>
<p>Here we have an example of <strong>dynamic (or runtime) polymorphism</strong>. When <code class="language-plaintext highlighter-rouge">FeedViewModel</code>-class is being compiled there is a reference to a <code class="language-plaintext highlighter-rouge">NewsProvider</code>-protocol. When the user enters Reuters page in our app we instantiate a view model and inject <code class="language-plaintext highlighter-rouge">ReutersNewsProvider</code>. So a specific class appears in the view model only in runtime.</p>
<p>Runtime polymorphism goes together with the dynamic dispatch. In the compile-time, we don’t know which exact method of which exact object will be called. It gives some runtime flexibility but blocks the compiler to do most of the compile-time optimizations.</p>
<p>With dynamic polymorphism, we can also create a heterogeneous collection. For example, our <code class="language-plaintext highlighter-rouge">FeedViewModel</code> can hold an array of different news providers and fetch news from all of them if we decide to design an aggregation screen in our app.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">FeedViewModel</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">newsProviders</span> <span class="o">=</span> <span class="p">[</span><span class="kt">NewsProvider</span><span class="p">]()</span>
<span class="k">var</span> <span class="nv">news</span> <span class="o">=</span> <span class="p">[</span><span class="kt">News</span><span class="p">]()</span>
<span class="kd">func</span> <span class="nf">addNewsProvider</span><span class="p">(</span><span class="nv">provider</span><span class="p">:</span> <span class="kt">NewsProvider</span><span class="p">)</span> <span class="p">{</span>
<span class="n">newsProviders</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">provider</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">reloadNews</span><span class="p">()</span> <span class="p">{</span>
<span class="k">for</span> <span class="n">provider</span> <span class="k">in</span> <span class="n">newsProviders</span> <span class="p">{</span>
<span class="n">news</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="nv">contentsOf</span><span class="p">:</span> <span class="n">provider</span><span class="o">.</span><span class="nf">fetchNews</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="2a-protocol-as-a-compile-time-constraint">2a. Protocol as a compile-time constraint</h2>
<p>Other than traditional dynamic polymorphism, protocols can be also used for static polymorphism. In this case, a protocol is resolved to a specific data type not in runtime, but in compile-time.</p>
<p>Let’s rewrite our <code class="language-plaintext highlighter-rouge">addNewsProvider</code>-function this way:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">FeedViewModel</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">newsProviders</span> <span class="o">=</span> <span class="p">[</span><span class="kt">NewsProvider</span><span class="p">]()</span>
<span class="kd">func</span> <span class="n">addNewsProvider</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">NewsProvider</span><span class="o">></span><span class="p">(</span><span class="nv">provider</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="p">{</span>
<span class="n">newsProviders</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">provider</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Functional-wise nothing changed but now we use generics. Instead of saying that we pass “something conforming to NewsProvider” we say that “we pass the exact type T which conforms to NewsProvider”. Now we use Swift generics system. For this piece of code where <code class="language-plaintext highlighter-rouge">T</code> is a parameter, it’s still dynamic polymorphism: the real type appears here only in runtime. You can still keep this <code class="language-plaintext highlighter-rouge">newsProviders: [NewsProvider]</code> for example.</p>
<p>But in the client code now the compiler has to resolve it to a specific type. So you cannot do something like that:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="kt">NewsProvider</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="kt">ReutersNewsProvider</span><span class="p">(),</span> <span class="kt">AssociatedPressNewsProvider</span><span class="p">()]</span>
<span class="n">viewModel</span><span class="o">.</span><span class="nf">addNewsProvider</span><span class="p">(</span><span class="nv">provider</span><span class="p">:</span> <span class="n">arr</span><span class="o">.</span><span class="nf">randomElement</span><span class="p">()</span><span class="o">!</span><span class="p">)</span>
</code></pre></div></div>
<p>It’s impossible to know in the compile-time what specific provider will be passed to a view model, so the compile prohibits it.</p>
<p>We can still use 100% runtime polymorphism for this protocol when the flow doesn’t touch generics:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="kt">NewsProvider</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="kt">ReutersNewsProvider</span><span class="p">(),</span> <span class="kt">AssociatedPressNewsProvider</span><span class="p">()]</span>
<span class="k">var</span> <span class="nv">provider</span><span class="p">:</span> <span class="kt">NewsProvider</span><span class="p">?</span>
<span class="n">provider</span> <span class="o">=</span> <span class="n">arr</span><span class="o">.</span><span class="nf">randomElement</span><span class="p">()</span><span class="o">!</span>
</code></pre></div></div>
<p>Here the compiler doesn’t mind not knowing the actual type of <code class="language-plaintext highlighter-rouge">var provider</code>.</p>
<h2 id="2b-restricted-protocol-as-a-compile-time-constraint">2b. Restricted protocol as a compile-time constraint</h2>
<p>As soon as we add an associated type to the protocol or <code class="language-plaintext highlighter-rouge">Self</code>-constraint your protocol loses all the dynamic polymorphic capabilities.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">NewsProvider</span> <span class="p">{</span>
<span class="kd">associatedtype</span> <span class="kt">NewsModel</span>
<span class="kd">func</span> <span class="nf">fetchNews</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">NewsModel</span><span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now your protocol cannot be used other than as a generic constraint. All that cannot be checked in the compile-time is prohibited. Now the contract gets so complicated that it cannot be resolved other than figuring out the exact type which you want to where this protocol is expected. No more dynamic polymorphism. No more <code class="language-plaintext highlighter-rouge">var provider: NewsProvider?</code> or <code class="language-plaintext highlighter-rouge">newsProviders: [NewsProvider]</code>. Every time you try to do something like that you receive the notorious error:</p>
<blockquote>
<p>Protocol <em>‘YourProtocol’</em> can only be used as a generic constraint because it has Self or associated type requirements</p>
</blockquote>
<p>You can either use some constraint type <code class="language-plaintext highlighter-rouge">T<NewsProvider></code> (when possible) which will be resolved to a specific type by the compiler, so you accept these static rules). Or you can use <a href="https://www.google.com/search?q=type+erasure+in+Swift">type erasure</a> and insist on some runtime logic in resolving the types.</p>
<p>If you are not sure what “Self-constraint” is take a look at <code class="language-plaintext highlighter-rouge">Equatable</code>:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">Equatable</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="o">==</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="k">Self</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="k">Self</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In every specific case of conforming <code class="language-plaintext highlighter-rouge">Self</code> will be replaced to a specific data type (as you cannot compare two different ones even if they both conform to Equatable). So it’s not so wrong to say that <code class="language-plaintext highlighter-rouge">Self</code> behaves here the same way as <code class="language-plaintext highlighter-rouge">associatedtype</code> does.</p>
<p>BTW, some of these runtime-restrictions are related to something called “existentials”. This concept is used in other programming languages and was mentioned in the <a href="https://github.com/apple/Swift/blob/master/docs/GenericsManifesto.md#existentials">Generics Manifesto</a> and there were <a href="https://forums.Swift.org/t/status-of-generalized-existentials/13982">at least one discussion</a> and <a href="https://github.com/austinzheng/Swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md">a proposal</a> how it can be solved in Swift. But seems like there is no further work in this direction.</p>
<h3 id="what-do-we-gain-here-1">What do we gain here</h3>
<p>Associated types and Self-constraint (which kinda associated type as well ;-)) are something that allows us to do all this protocol-oriented (or generic-oriented) programming. It allows us to write flexible code which can abstract huge chunks of functionality from the specific types, so they can be reused.</p>
<p>With such a powerful concept you can abstract, for instance, your <a href="https://faical.dev/articles/modern-Swift-data-access-layers.html">data store logic</a>, your <a href="https://github.com/maxsokolov/TableKit">table view logic</a>, or every here and there for <a href="https://www.Swiftbysundell.com/articles/specializing-protocols-in-Swift/">abstracting smaller pieces of logic</a>.</p>
<p>The price of this abstraction is quite high. It’s not only the limitations we just mentioned but also some additional cognitive load (hope to tackle it more in detail in one of the next posts).</p>
<h2 id="3-protocol-as-a-code-generation-directive">3. Protocol as a code generation directive</h2>
<p>Sometimes protocol can be used additionally to tell the compiler to generate some code. Usually, that’s some boilerplate code for the type conforming to this protocol.</p>
<p>For instance, if you male your model to conform to <code class="language-plaintext highlighter-rouge">Codable</code> the compiler will generate the implementation of Encodable/Decodable functions for you. The same happens with <code class="language-plaintext highlighter-rouge">Equatable</code>. Implementation can be generated if all the nested data types conform to the protocol. If you need to add some logic you are free to reimplement the protocol-methods yourself, but in the majority of cases it’s not needed.</p>
<p>If you are interested in how the compiler does it check out <a href="https://nshipster.com/Swift-gyb/">this (as always) brilliant article at NSHipster</a></p>
<h3 id="what-do-we-gain-here-2">What do we gain here</h3>
<p>Protocols in these cases still play their role as interfaces. Some mechanisms from a standard library need your custom data types to conform to these protocols. But the code generation is a nice addition which makes you free from implementing the same code again and again. Of course, it’s applicable only to specific use cases when implementation follows some pattern so can be easily automated. (Think about your own protocols, maybe it could make sense to use some code generation for some of them as well ;-) )</p>
<h2 id="to-be-continued">To be continued…</h2>
<p>I suppose you noticed that we haven’t said a word about <code class="language-plaintext highlighter-rouge">extensions</code> here, although a lot of folks think they a necessary part of “Protocol-Oriented development” (I’m not among them). Here is the <a href="https://dmtopolog.com/protocol-extensions/">next post dedicated to the protocol extensions</a>.</p>Dmitrii Ivanov“Protocols is one of the most powerful language features in Swift” - people say. Hard to argue. In Swift you can do a lot of different things with protocols. (Maybe even too many… but we will talk about it later).Modularity. Encapsulation2020-08-03T00:00:00+00:002020-08-03T00:00:00+00:00http://dmtopolog.com/modularity-2-encapsulation<p>What is this post about:</p>
<ul>
<li>what modularity gives us in terms of context separation</li>
<li>what can help us to keep the module properly encapsulated</li>
<li>what is a Showcase app and how it can improve our development</li>
<li>what might be some possible challenges</li>
</ul>
<p>This post is part of the series on modularity:</p>
<ul>
<li><a href="/modularity-1-boundaries">Modularity. Boundaries</a></li>
<li><a href="/modularity-2-encapsulation"><strong>Modularity. Encapsulation</strong></a></li>
<li><a href="/modularity-3-problems">Modularity. What problem does it solve?</a></li>
</ul>
<p> </p>
<h3 id="module-interface">Module interface.</h3>
<p>Module is a complete piece of functionality restrained by the Module Public Interface (MPI). And in the case of a library or a framework, this interface is very formal. No data flows between the module and the host app can bypass MPI. All the module’s dependencies are also explicit and can be easily checked. So modularity gives you a better overview of the functionality within the module. You can easier imagine the module’s place in the project. You can clearly see what data goes in and out of the module. You can easier estimate the effort to add, remove, or replace it.</p>
<p>On the other side, strict boundaries imply a more tedious procedure to change them. When different parts of a host app (or even several host apps) rely on the interface it shouldn’t be easy to modify it. You have to carefully consider if the modification breaks the interface (backward incompatible) or not. If so you have to think about the way to minimize these breaking changes and even provide a migration plan to the module consumers.</p>
<p>Modules require documentation. If you want to have an isolated piece of code (your module) and not feeling the need to check the implementational details every time, proper documentation of the MPI is your choice. It will cost you some additional time to cover the module interface with documentation, but it will pay you back with the amount of time saved. True encapsulation cannot exist without a clear understanding of what the module does, how the consumer supposes to use it, what are the inputs and outputs. And there are basically two ways to get it: jumping into the implementation of the module or checking the documentation. The ladder is much quicker and nicer than the former.</p>
<p>Needless to say that MPI documentation (as every documentation) requires constant maintenance. You might add some dedicated steps in your work process/pipeline in order not to forget about documentation. For instance, you can add a separate checkbox in your module’s CI when you release a new module version: <code class="language-plaintext highlighter-rouge">update the documentation if the MPI changes</code>.</p>
<h3 id="context-separation">Context separation.</h3>
<p>One of the greatest benefits of modularisation is context separation.</p>
<p>You all know that our brains are not capable of holding big chunks of information for a long time. So when working on the code (writing or debugging) the lesser context you need to keep in mind the better.</p>
<p><img src="/images-posts/2020-08-03-modularity-2-encapsulation/IMG_E0532-2000.JPG" alt="" /></p>
<p>In a badly modularised project, different pieces of functionality are tightly coupled to each other. So when you work on, let’s say, some networking code you might need to consider not only this piece of code but several parts of business logic which use this code, all the state which is changed by this code and maybe even a persistency functionality which is wired into this code. Basically, you need to keep in mind half of your app.</p>
<p>If you work on a properly encapsulated networking module on another hand, you care only about the actual networking code and the MPI of the module. The module interface cuts all the functionality into the internal module context and the external one. And in most cases, you don’t need to know anything about the external context when working inside the module. So work gets easier, you spend less time on it and it costs you much less cognitive energy to do it.</p>
<p>The ability to treat modules independently from the rest of the app puts you in a better position when you need to refactor or modify the code. You have fewer dependencies (and the ones you have are more clear) and fewer side effects. Consequently, it’s also easier to test this code, to mock the dependencies, and to detach the code you test from any global state.</p>
<h3 id="showcase-app">Showcase app.</h3>
<p>If your module is a feature module (it has some UI as well as business logic) you may want to introduce a Showcase app for the module.</p>
<p>Showcase app is a representation of the features and use cases your module provides. It’s feasible to make one only if you can separate the feature from the rest of the code both: regarding the UI and regarding the business logic. Otherwise, you will need to include a lot of irrelevant code to the showcase app, and it will be not so different from your major app.</p>
<p><img src="/images-posts/2020-08-03-modularity-2-encapsulation/IMG_E0533-2000.JPG" alt="" /></p>
<p>Let’s imagine we have a Profile feature module. It has a UI with the ability to display and modify profile settings. It also contains business logic to connect to a back end to fetch and update the data. So all of this is wrapped into a framework which we use in our major application.</p>
<p>If we create a showcase app for this module it will likely look like a simple app displaying the profile page and maybe several more pages to edit the info. The profile-framework is a dependency of this app. When the app launches we inject some mock network layer to the framework (replacing remote calls with some refilled JSON data) and get the profile screen back from it. So nothing more than our module provides.</p>
<p>First of all, as it follows from the name, <code class="language-plaintext highlighter-rouge">showcase app</code> tells us what the module has and how its functionality can be used. In the profile showcase app, you might be able to input some user data and see this data on the screen. Under the hood, the module will do all the regular work: sending the data to the (mock) backend and displaying it. The module works exactly the same as in the real app.</p>
<p>This <code class="language-plaintext highlighter-rouge">showcasing</code> helps to display the capabilities of the module to the product owners and to module consumers. It might be easier to check the changes in the business logic in this simple app then in the main project. You don’t need to navigate to the profile all the time, and when using the showcase app you are more focused on the feature.</p>
<p>This app is a simplified host app. You can easily display all the main use cases there as well as some edge cases. For instance, error handling is generally not so reproducible while debugging but it might be important to see how the errors are being handled. You can create a specific mock response from the backend with an error and a separate use case in the UI to trigger this response. So with one tap you can reproduce the error and see how your feature reacts to it.</p>
<p>With such a sample-app you present not only visual functionality to the product owners but also the way to use the MPI to the integrators. Generally, the module interface should be as simple as possible, intuitive, and properly documented. But in reality, all the interfaces can be misunderstood and wrongly used. Hence it’s very helpful to display this right approach and good practices in code.</p>
<p>As a developer who needs to integrate a new module in my project I’d always like to check some examples where this module is already integrated: how to set up the configuration, which objects to instantiate, how to handle callbacks. Showcase app is a great way for module maintainers to provide all this information.</p>
<p>It also easier to develop and debug your module utilizing this small focused app. You get faster compilation and waste no time on the navigation to the feature after every app relaunch. Think about how many times you do this routine (change code - recompile - relaunch - navigate to the feature) during the workday. And when relaunching the app is a matter of a couple of seconds you don’t switch to checking your facebook feed in between.</p>
<h3 id="encapsulation-is-never-perfect">Encapsulation is never perfect</h3>
<p>Sometimes there are still connections between 2 properly separated modules. For instance, some changes in one module require changes in another. Or some functionality simply migrates from one screen (in module1) to another (in module2). In this situation, you cannot develop and integrate a new version of the module completely independently. It’s necessary to synchronize development and releases of 2 (but maybe even more) separate modules. When they are maintained by different teams with separate backlogs and priorities, that adds quite a complexity, which inevitably slows down the delivery of the change.</p>
<p>You can work on a perfectly separated module, but there is always a stage when the module (or its new version) needs to be integrated into the app. That’s where the development is not much different compared to a non-modular approach. You are in between the module and the host app so you have to switch from your cozy small module context to the entire app.</p>
<p>Moreover, when integrating the module (or debugging this integration) you might encounter some technical difficulties. For example, it might be challenging to step between the module and the host app code in the debugger because it’s not compiled at the same time. Not talking about the misalignment between the module and the host app. Some cache might not be updated and as a result, you use the old version of the module… which compiles without a problem but causes some weird runtime issues.</p>
<p>We will touch these points in detail again later when talking about the work process peculiarities of developing a modular app.</p>
<p> </p>
<hr />
<p>I hope you liked this piece of reading. If you have any questions, suggestions or corrections you can reach me out <a href="https://twitter.com/dmtopolog">on Twitter</a></p>Dmitrii IvanovWhat is this post about: what modularity gives us in terms of context separation what can help us to keep the module properly encapsulated what is a Showcase app and how it can improve our development what might be some possible challengesModularity. Boundaries2020-06-08T00:00:00+00:002020-06-08T00:00:00+00:00http://dmtopolog.com/modularity-1-boundaries<p>In this post we’ll talk about why boundaries matter and what types of modularity can we distinguish in our projects based on the existing boundaries.</p>
<p>This post is part of the series on modularity:</p>
<ul>
<li><a href="/modularity-1-boundaries"><strong>Modularity. Boundaries</strong></a></li>
<li><a href="/modularity-2-encapsulation">Modularity. Encapsulation</a></li>
<li><a href="/modularity-3-problems">Modularity. What problem does it solve?</a></li>
</ul>
<p> </p>
<h2 id="boundaries-and-interfaces">Boundaries and interfaces.</h2>
<p>When working with code we all the time are dealing with boundaries between different subsystems. It happens on different levels of abstraction. Functions, classes, modules, frameworks, layers are all encapsulated entities separated from the other world by different kinds of boundaries.</p>
<p>An interface is a legal way to cross the boundary. It describes all the possibilities to pass data or execution control between entities.</p>
<p>Boundaries can be physical or purely architectural.</p>
<p>You cannot call the method of a class from another class if it’s not public, the same works with frameworks. We have a clear separation of contexts and physical boundaries between different subsystems. The interfaces here are enforced by the compiler, linker, or some other tool. You cannot cross such a boundary without changes in the interface.</p>
<p>But sometimes the boundaries are just marked architecturally. We may separate our classes into architectural layers and set some rules on how these layers should communicate to each other, but there is no tool to enforce the interface.</p>
<h2 id="modularity">Modularity</h2>
<p><code class="language-plaintext highlighter-rouge">Module</code> is quite a general term. It may mean both a physical separation (compiled library or framework, some package, subproject, different repository) or just an architectural concept without any formal interface. So for clarity let’s call them <strong>physical modules</strong> and <strong>architectural module</strong>. The former obviously have some physical boundaries, the latter - don’t.</p>
<h3 id="architectural-modularity">Architectural modularity</h3>
<p>Let’s imagine in our Swift project we have UI, business logic and network layers. Within the business logic layer, we have some feature modules. Inside the team, you have some agreements and guidelines like calling the network layer only from the business logic and not from the UI, or not calling one feature from another. All the classes of the modules and layers are properly documented and carefully separated into different folders. But all the files reside in one project and belong to the same target (we will talk about the targets in a minute). It’s an example of pure architectural modularity.</p>
<p><img src="/images-posts/2020-06-08-modularity-1-boundaries/IMG_E9634.JPG" alt="" /></p>
<p>The thing is: there are no technical ways to enforce these agreements. Everything may even work and you manage to keep your boundaries and interfaces clean. But it’s so easy to break this balance. Nothing stops the developer to do something wrong: for instance, calling NetworkManager directly from a UITableViewCell. Or using some module’s “internal” classes from outside of the module.</p>
<p>Why would one do that? A lot of possibilities:</p>
<ul>
<li>you haven’t written the rules down</li>
<li>somebody forgets or confuses the rules</li>
<li>somebody just disagrees with the architectural concepts in use, or maybe mostly agrees but treats some case as an exception, or thinks that the agreements are outdated</li>
<li>a new developer, who doesn’t know or doesn’t respect the agreements, doesn’t hesitate to follow them</li>
<li>somebody makes a shortcut under the pressure of a deadline</li>
</ul>
<p>As the team of developers working on a product evolves (some people leave, new people come) all these agreements and assumptions stop work at some point. If some shortcuts can be physically done without extra effort, maybe even unintentionally, it will be done sooner or later. So the boundaries will be crossed and interfaces will be bypassed.</p>
<p>So we need some physical boundaries and technical limitations. There should be strict rules and ways to enforce people to follow the rules, to make it more difficult to make mistakes.</p>
<h3 id="physical-modularity-on-ios">Physical modularity on iOS</h3>
<p>When talking about architectural modularity we can abstract from the language as the concepts are common for all the software development. But if we dive into the implementational details we cannot generalise much anymore.</p>
<p>Regarding physical modularity on iOS, we have to distinguish (Objective-)C modules from Swift modules. Modules are being created by the compiler and we have two different compiler frontends - clang and swiftc - which deal with modules differently.</p>
<p><em>(More about these different compilation pipelines you can find in one of my previous posts - <a href="https://dmtopolog.com/code-optimization-for-swift-and-objective-c/">Compiler code optimization for Swift and Objective-C</a>. If you need more technical insights into clang modularity check out the <a href="https://clang.llvm.org/docs/Modules.html">documentation</a> for more info regarding Swift take a look <a href="https://forums.swift.org/t/explicit-module-builds-the-new-swift-driver-and-swiftpm/36990">here</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html">here</a> and <a href="https://swift.org/package-manager/]">here</a>)</em></p>
<p>Although there are some significant differences between Swift and (Objective-)C modules (name spacing, access control,..) the essence is the same.</p>
<blockquote>
<p>A module is a single unit of code distribution — a framework or application that is built and shipped as a single unit and that can be imported by another module with <code class="language-plaintext highlighter-rouge">import</code> keyword.</p>
</blockquote>
<p>When you split your code into physical modules you create several different units instead of one. Your code can still sit all in one place (repo/workspace/project) but different parts of it are being assembled independently. When one module refers to another they consider each other as different projects with physical boundaries which can be crossed only via some allowed ways - MPI (Module Public Interface).</p>
<h3 id="targets-and-access-control">Targets and access control</h3>
<p>In iOS projects modules are configured by <strong>targets</strong>. Target is a final product that will be compiled out of your source files. It may be a framework or a library, an application, an application extension, or something else. Some of these products can be used as modules.</p>
<p>It’s quite intuitive that physical separation of units reinforces the boundaries between them.</p>
<p>If the file belongs to <code class="language-plaintext highlighter-rouge">Target A</code> and doesn’t belong to <code class="language-plaintext highlighter-rouge">Target B</code> you need to do some deliberate actions to make this file accessible inside <code class="language-plaintext highlighter-rouge">target B</code>. It’s a two-step process. Firstly you need to make the file (or it’s part) public, so available from the outside of the module. Secondly, you need to import module <code class="language-plaintext highlighter-rouge">A</code> into the module <code class="language-plaintext highlighter-rouge">B</code>.</p>
<p>Access control levels are another tool for boundary reinforcement.</p>
<p>In ObjC (as inherited from C) we have private implementation (.m) and public interface (.h). Hence we have 2 ways of establishing some physical code access boundaries: putting something into a class interface and importing such interfaces in other files. If some functionality of <code class="language-plaintext highlighter-rouge">Class A</code> is public it’s not enough to use it in <code class="language-plaintext highlighter-rouge">Class B</code>. You also need to explicitly import the interface of <code class="language-plaintext highlighter-rouge">Class A</code> in <code class="language-plaintext highlighter-rouge">Class B</code>. That stays the same regardless of whether or not <code class="language-plaintext highlighter-rouge">Class A</code> and <code class="language-plaintext highlighter-rouge">Class B</code> are in the same module or in different ones.</p>
<p>In Swift, we have more different access levels (with corresponding modifiers): object-level (<code class="language-plaintext highlighter-rouge">private</code>), file-level (<code class="language-plaintext highlighter-rouge">fileprivate</code>), module-level (<code class="language-plaintext highlighter-rouge">internal</code>), project level (<code class="language-plaintext highlighter-rouge">public</code>, <code class="language-plaintext highlighter-rouge">open</code>). Modifiers help us to build clear MPI for the module. Until <code class="language-plaintext highlighter-rouge">Class A</code> is declared as <code class="language-plaintext highlighter-rouge">public</code> or <code class="language-plaintext highlighter-rouge">open</code> nobody can use it outside of its module. So changing the module interface has to be explicit and cannot be made by mistake. It’s also something that can be easily spotted during the code review.</p>
<p>As we can see in Swift we are not able to build custom boundaries between architectural modules as we can do it in ObjC by (not) importing interfaces. But we have more options when it comes to modules. In Swift physical modularity does really help you with the boundaries.</p>
<h3 id="other-boundary-enforcements">Other boundary enforcements</h3>
<p>All your physical modules can be put into separate projects and even separate repositories. In case of including just a binary, you detach two modules even more. The main project in this case has no references to the source code of the dependency.</p>
<p>The next level of separation is using versioning for your modules. In this case, to make a change in the MPI you need to make a change in the module’s project, release a new version of the module, and then integrate the new version into your host project. (I’m not saying you need module versioning for making the boundaries stronger. I see it the other way around: if you do have the versioning in place - or you plan to have it due to other reasons - it will also enhance the boundaries.)</p>
<p>Different modules can be developed by different teams/departments. This makes all the small changes in the module just to fit the needs of the host app borderline impossible. (If you have such a complex structure you are more likely have several different host apps that use the module. Module maintainers are likely to have their module as a product, so they have their own goals and backlog.)</p>
<h3 id="types-of-a-project">Types of a project</h3>
<p>There are 3 basic types your iOS project can be in according to the actual physical boundaries between your modules: monolith, modular monorepo (mono repository), and multirepo (each module in a separate repository).</p>
<p><img src="/images-posts/2020-06-08-modularity-1-boundaries/IMG_E9635.JPG" alt="" /></p>
<p><strong>Monolith</strong></p>
<p>That’s a position most of the project start from. All the project’s code resides in one repository without any modular separation. It’s a totally valid condition when your project is relatively small and there are not so many developers working on it. Even if you have some architectural separation but without physical modules, your app is still a monolith.</p>
<p><strong>Modular monorepo</strong></p>
<p>Here your project is divided into several physical modules. All the code still resides in the same repository but there are some real borders and interfaces between the different parts. That’s quite a typical setup for mega repos in tech giants like Google or Facebook.</p>
<p><strong>Multirepo</strong></p>
<p>Basically the project structure is the same as with modular monorepo: you have some host app and you have some dependencies. The only difference is that the modules have their own projects. Usually, you have to use some dependency manager to build your main project altogether. You treat your modules (which become <code class="language-plaintext highlighter-rouge">frameworks</code> or <code class="language-plaintext highlighter-rouge">libs</code>) the same way you treat 3rd party dependencies.</p>
<h2 id="false-dichotomy">False dichotomy</h2>
<p>It’s quite a common misconception that <code class="language-plaintext highlighter-rouge">monorepo</code> and <code class="language-plaintext highlighter-rouge">modular project</code> are something completely opposite. Like in one case you have all your code in one place, no architecture, no separations of concerns - one big spaghetti bowl. In contrast, you might have all your features and core components sitting in separate repositories, perfectly encapsulated, and having no idea about each other.</p>
<p>Even without such extremums, this entire contraposition is still false. If it wasn’t clear before this post should have evaporated this idea.</p>
<p>Modularity (which can be different as we described here) tells you how well different parts of your project are separated from each other. Monorepo and multirepo are the ways to store these parts, manage the changes in the codebase, and adopt these changes in the host project.</p>
<p><img src="/images-posts/2020-06-08-modularity-1-boundaries/IMG_E9636.JPG" alt="" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>Boundaries are just one of the aspects of modularity. Boundaries are your safety net. The stronger the boundaries between your modules are the safer your project is in terms of keeping the separation of concerns.</p>Dmitrii IvanovIn this post we’ll talk about why boundaries matter and what types of modularity can we distinguish in our projects based on the existing boundaries.Thoughts on rewriting the Messenger App by Facebook.2020-03-04T00:00:00+00:002020-03-04T00:00:00+00:00http://dmtopolog.com/fb-rewriting-messenger<p>In my small iOS information bubble <a href="https://engineering.fb.com/data-infrastructure/messenger/">this post from FB-engineering</a> exploded so nobody could ignore it.</p>
<p>The funny thing is that people look to this article like into a mirror and everybody sees what they want to see. Some people blame FB for a waste of resources, the others praise them for being bold, switching to a new architecture and putting performance first. Some folks start the funeral for ReactNative the others find it as the prove that cross-platform (Flutter or even Kotlin Multiplatform) is our nearest future. That’s really awesome!</p>
<p>I also wanted to tweet something while I was reading it but eventually I had several thoughts (and interpretations) which I wanted to write down.</p>
<p>First of all it’s a very compelling reading because indeed there are not so many stories about such big projects. I personally cannot remember one in our iOS world since Airbnb and <a href="https://medium.com/airbnb-engineering/react-native-at-airbnb-f95aa460be1c">their experience of taking up and throwing away React Native.</a>.</p>
<p>I used to work quite extensively with the real-time messaging app (web-sockets, high load, async UI) and the one which heavily relied on DB-to-backend data syncing (fancy CoreData context models, dozens of tables, millions of data entries, several simultaneous data streams). It was two different projects and both of them was full of technical challenges. FB has both in one app, which they managed to redo from scratch. That’s something to appreciate for sure. If you want to say “it’s only a matter of resources”, believe me it’s not.</p>
<p>The post is definitely written in collaboration between the engineers and the marketing people. The language is quite generic and there are lots of vague constructions. But at the same time it’s full of technical details, chosen approaches and the attitude to some FB-maintained technologies. They managed to say a lot without saying it explicitly. So people will keep on interpreting it in all the possible ways.</p>
<p>I’m really surprised how many people treated it as the beginning of the end for RN. <a href="https://twitter.com/dan_abramov/status/1234801507805138945">This small thread</a> from <a href="https://twitter.com/dan_abramov">Dan Abramov</a> - one of the leading guys in RN-team - nails it I think.</p>
<p>Nobody in FB ever said that RN is a universal tool for all the problems. It would be weird to consider that FB plans to move everything to RN and stop doing native development (especially if you know how fast their native teams grow and how much they invest into compilers and other low level native tools).</p>
<p>People focus on React native and nobody seems to remember ComponentKit, async UI and the declarative paradigm? Is it something which made sense 5-10 years ago (and were likely to be used in an old Messenger app) but looks more as a burden now.</p>
<blockquote>
<p>While UI frameworks can be powerful and increase developer productivity, they require constant upkeep and maintenance to keep up with the ever-changing mobile OS landscape. Rather than reinventing the wheel, we used the UI framework available on the device’s native OS to support a wider variety of application feature needs.</p>
</blockquote>
<p>That pretty much explains everything. Nowadays in many cases the benefits some custom fine-tuned solution give you are smaller than the headache you have maintaining them. It’s always a trade-off: whether to pick an existing general solution or to reinvent a wheel which would perfectly fit your needs. No development and maintenance overhead against your custom use cases and specific requirements. When it comes to custom alternatives for system frameworks, there are some additional points to consider. System frameworks grow bigger covering more and more edge cases and encapsulating more and more functionality. Moreover in case of iOS when you use some system components you are relatively protected from the future changes and API updates. So the choice is getting more obvious.</p>
<p>BUT what works for some cases doesn’t work for the others. And you still might need to write some C components replacing the native libs for performance or create your own layer atop of SQLite.</p>
<p>About that new SQLite wrapper.. I heard it’s quite a revolutionary engine (would be interesting to see more details about it). There is an idea that this MSYS is a future core components for all the major FB products (main app, Instagram, WhatsApp). It makes sense considering how much effort did they invest into it. I don’t believe such an effort (several years and 100 developers) can take place only because the old app was such a crap or because they need to occupy the engineers with some interesting big task. Seems like Messenger is a pilot (used by millions of people.. but still not the main product) for the company to test some new technologies and approaches, which will shape and improve the future of the entire company? How else you can sell such a project to your managers? ;-)</p>
<p>This story also made me think about the trade-offs of big companies between huge resources and lack of flexibility. What was the state of the art 6-8 years ago is dusty legacy now, but you cannot easily get rid of it or migrate to a new technology. At that times there were no architecture in iOS except MVC, not so many people actively used patterns, DRY, KISS and all the other basic principles, “reactive” and “declarative” were not parts of a developer’s vocabulary. I can imagine some leftover from that wild-west times. You can still find tons of code to satisfy some requirements which make no sense now, code you cannot easily refactor or remove. Building from scratch might be a good idea in this case (which is not for majority of the other (normal) cases)</p>
<p>The last thing. While reading I was eager to find some mentioning of Swift. But… custom C components… SQLite wrapper… ‘categories’ of basic views… So the same C-based stack as before for the new app in 2020. I know FB has reasons and I do understand most of them, I really like ObjC for its freedom and runtime capabilities… I think I’m just awaiting some new era which starts when FB deploys the first swift code into their main app ;-)</p>Dmitrii IvanovIn my small iOS information bubble this post from FB-engineering exploded so nobody could ignore it.