We usually hear about intangible or difficult to measure benefits of implementing a good architecture. I would like to prove to you that the benefits are far more mundane.
In this trySwift talk, I will showcase practical, real-world examples of how a good architecture for your application makes your life easier. Using my interpretation of the Clean Architecture for iOS in Swift, I will show how it helps to do things such as reusing an existing use case in another view controller, or using a different backend, in case we are forced to do so. I will also cover briefly how this architecture helps with testability.
In this talk, I’m going to cover three things:
- The superpowers that you will get from a good architecture.
- Scenarios, with a live coding session.
- A summary of everything we covered.
The Benefits of Good Architecture (1:34)
Let’s start with superpowers and the benefits that you would get from a good architecture.
The first benefit that you get is less repetition. If you write your code more than once in your application, you’re probably doing it wrong, because you have more chances to have this code written wrong twice. You have to fix the same bug twice. You have to spend the time to write the code twice or more, and you don’t want that because time is money.
With better architecture, you also get more reusability. Reusability means that you can reuse the same class in different applications; which is good, especially if you are an agency and you’re developing apps for customers.
It is also easier to maintain because, since the responsibilities of the classes are separated into different objects, when something is going wrong, you know which object to blame, and you know where to look for the bug. The more time that you spend separating responsibilities, the easier it gets to debug your application.
Get more development news like this
It is also more testable, and this is good for reducing the amounts of bugs. This is like the Holy Grail of the development. You want to be independent of a persistence framework, for example, because they may discontinue it, and if you are independent of one of those, then your application would survive the change. If you’re not, then you will have to rewrite the whole app to adapt to the newer, shiny framework that you want to use for persistence.
Finally, if you are getting requests for changes in your applications frequently, and you want to be able to adapt the application to this new situation. If you have a good architecture, that allows you to add these new use cases very fast. That is money for you; that is money for your company.
I know some of you are not that interested in money. You do this for passion. That is why they hired you, because you’re passionate about what you do. So there is pride in writing good code.
Let me show you the three superpowers you can get from a good architecture.
Super Reusablility (4:54)
The first one is super reusability. If you have been writing applications for a while, you have probably been using some kind of architectural pattern, like Model-View-Controller, Model-View-Presenter, or Model-View-View-Model.
These classes are just three groups, and the responsibilities are much more in your applications. So, usually, a class ends up having many more than one responsibility.
You want to have some things separated from these classes, so you probably want to have your presentation logic somewhere else, because you want to reuse that logic in more than one view controller. If you have this in a different class, then you can reuse it.
You’re also probably using more than one data source, and if that is the case, you want to do some caching and some arbitration on how the objects are going to be handled when there are conflicts, and so on. So you want to have this responsibility in another layer.
It is also important that business cases that sometimes are written as Core Data extensions, you can have these totally separated from the persistence framework that you’re using.
Super Independence (6:41)
The second superpower that you get is super independence. This is probably the coolest superpower. That’s because it is completely based on the dependency inversion principle.
What the dependency inversion principle tells us, is that higher level code should not depend on lower level code. So if your code is actually using a persistence framework, for example, that it was the persistence on the network, and you have parse objects, then you’re probably doing it wrong.
Instead, what you want to do is, have an abstraction and have a dependency on that abstraction. Make your class depend on that abstraction and then make your framework code implement it. This sounds a little bit complicated, but once you learn how to do it, it is really easy and it is very powerful.
Super Testablility (8:01)
The third superpower is super testability. Testability is about many things, but you have to make your dependencies explicit. Which is good because you understand what the dependencies of your code are, and you know how to deal with them properly.
It is also important that you make dependency injection easier. That is a little bit difficult because usually the view controllers, particularly if you are using a storyboard, are created for you, so you need to pass the dependencies to the storyboard and the controllers.
It also benefits from all the other principles, not only the solid ones but any other design principle that you may apply to object-oriented design.
Now we’re going to move on to the scenarios, so if you can, follow along the live coding on the video.
Scenario 1 (9:21)
The first scenario is going to be about reusing logic, using an app that is designed for selecting good developers. This is an app to interview programmers and to know the actual, relevant skills for hiring them.
It is based on two skills; their knowledge on emacs, and their ability to drink caffeine. Based on those two skills, we are going to be able to use our secret sauce and rate the programmer, to know how good this person is going to be for our job.
Suppose the program manager has told us to reuse this application and they use the use case that is taking care of changing the favorites status but doing it from the table view controller, instead of the detail view controller.
We are told to take a long tap, and use it to change the favorite status. This is loosely based on the clean architecture. There are entities, in this case, we only have one that is the programmer, and this is the one that we are dealing with.
There are the use cases that is an encapsulation of our business logic, we even have a use case factory, which is the one we are going to use in order to create a new use case. We also have presenters, the entity gateway, which is the provider of objects that we are going to deal with, and the views.
By the view, I refer to the addition of the view, plus the view controller. So we also have the connectors, they are the ones responsible for transitions and navigation and injecting the dependencies.
What we are going to do first is, we’re going to add one method here that you would use to recognize a long tap, and take care of it. It is only obtaining the information from the location where the long tap is happening, and it tries to find out the right row where this will happen, and inform the presenter. Because the views, in this case, are as dumb as possible.
So in the storyboard I will connect the long-press gesture recognizer with the method that I have just defined. I will also connect the table view, with the long-press gesture recognizer, as one of the recognizers that are going to be used.
Now, let me take care of the presenter, that is responsible for dealing with events that come from the table view. So in this case, this is going to be the list presenter. So I’m going to add the new method, the one that we were invoking before, that is going to take care of the long tap, and I’m going to use the ID of the data that I’m using.
Finally, I need to have a new use case generated. So I’m obtaining the ID from the row that was passed to this method, then I’m creating a use case, using a use case factory, and adding the parameters as a tuple.
We want the use case to be executed. The use case will toggle the favorite status of the programmer. Finally, it will be notified through the completion, where we want to have the data be updated in the table view.
Notice that there’s no additional information about the use case happening in the presenter. The presenter is unaware. The use case is implementing a protocol so now when I go to this one with just these three steps in the presenter.
Scenario 2 (16:16)
The second scenario is going to be how to replace your backend, in case that you get screwed by some persistence framework.
So I have a protocol that is defined, and there are operations that I am interested in. These methods are asynchronous. They can create a new programmer. They can get a programmer based on the ID, fetch a list of developers, and update them.
The only thing that I need to do in order to have this running, with a VFrame, a kind of backend, is to actually create that new file, this new backend, add all this content. It may be a long file, but most of it is very simple. It’s just doing what this method is required to do with this given back end.
In this case, I’m going to use CloudKit, but it can be any other, as long as it follows the protocol, and the protocol is not leaky, and it is not using the specific object that this framework’s providing you with, it will work.
The only thing that I have to change in the rest of the application is that I go to the AppDelegate, and instead of having the entity gateway created with a memory repository, I’m going to create an instance of this CloudKit ripple, and I should be able to run it.
The backend here is different, the data’s different and it still is able to navigate, change the status of the person and go and change whatever you have there.
Scenario 3 (19:50)
The third scenario is about testing. So, this example that I have shown to you is about 1600 lines, more or less, and I have written tests for everything, except what I wrote here in these scenarios. This has 247 tests, and it should work properly. Everything should be tested, but the things that we wrote together here.
So if we go to the coverage, everything but the three last things that we changed, the programmers, table view controller, the memory repo, that is not using the data population and the CloudKitRepo.
They are completely tested here, which means that the application is fully testable, even those that we haven’t written yet.
In summary, I think that architecture is good for everybody. That is not only good for big enterprise apps but for everybody because these benefits apply to even the smallest application.
If you’re going to build an application that is only one view controller, and you don’t plan to maintain it at all, then maybe you shouldn’t care about this, but anything that is more complicated than that, you should care a bit about architecture.
And you should learn the principles that are behind the decisions instead of applying an architecture blindly, which I think is a wrong way to approach an architecture. You should understand where the rules of this design, of this architectural program that you use come from.
You have to learn some design patterns. It’s beneficial for implementing an architecture that is helpful. For example, the use case factory is a design pattern, and it is providing me with independence between the number of use cases and the rest of the presentation logic that I’m doing there.
You don’t have to commit to the best architecture from the beginning; you can apply all these steps gradually. And you can improve your application in each iteration. So you can get your superpowers too, and I think that’s going to be very useful!
About the content
This talk was delivered live in September 2016 at try! Swift NYC. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.