An iOS app architecture (Part I)

An iOS app architecture

The aim of this post (and the followed ones) is to expose an iOS app architecture that I have been using for developing iOS apps during last two years. In the end is not strictly one of the known ones: VIPER, MVVM, MVP…, but obviously with many commons concepts.

What is an architecture?

From wikipedia: Software architecture refers to the high level structures of a software system, the discipline of creating such structures, and the documentation of these structures. These structures are needed to reason about the software system. Each structure comprises software elements, relations among them, and properties of both elements and relations.[1] The architecture of a software system is a metaphor, analogous to the architecture of a building.[2]

In practice a software architecture allow to absorb the product owner requirements without increasing your app source code complexity. If does not, start to refactor before is too late.

Outlining the architecture

As a professional painter we have to outline the available canvas and decide which functionaly will be implemented in each part.

Once done, now is crystal-clear which part will be occupied by fingers, palm and wrist.

Moving to software engineering, I am talking about 3-tier architecture model, this model has been used for in other technologies (e.g web, client-server). It splits the app in 3 layers (View, Domain, Data), its philosophy is very clear:

  • Each layer has a very concrete purpose
  • Each layer has a defined interface for requesting its implemented services.
  • A layer can only interact with its adjacent one.

Rule number I. Visualize mentally that each component (and its subcomponents) from your architecture as an apartment in a building. A component can only interact with its immediate upper and lower components floor. If you want to go from 4th floor to 2nd one, you have go through 3rd.

For instance if we want to present ddbb query result in a view:

🚫 🙅 Forbidden. Access directly from a view to a database handler to make a request. Comming in to iOS context, figure out that you get the realm ddbb handler and implements the query from the ViewController that will present the results.

✅The approach. Define one interface method in the Data and Domains layer that returns the requested data (e.g. in form of array) to the view, so View layer unique responsibility is draw the data returned by domain layer.

With this simplistic example, Domain layer does nothing apart from calling Data layer, you would think that this layer is redundant, but truly believe me it is not. If some data processing has to be done, Domain layer is the place.

From this rule you will get the following benefits:

  • Use TDD for validating Domain and Data layers with unit tests
  • Reduce the number of code-lines in the view (Massive ViewControllers problem).

Rule II. No-shared responsibility. Every component has a concrete responsibility and no other component can perform it, but the designated one.

For instance in our app we have different screens and each one is presented information retrieved from a rest service.

🚫 🙅 Forbidden. Implement in every view the service call for retrieve the data to present.

✅The approach. Place all the service calls in a component, as this component is retrieving data, it will be placed in the Data layer. So the view will request to domain the rest data, and domain will request to data layer, finally the data will be returned to view via domain.

From this rule you will get the following benefits:

  • Use TDD for validating a component with a concrete responsibility.
  • In case that some component fails (e.g rest api’s), we have to review a file, not bunch of scattered code in all project.
  • Reduce the number of code-lines in the view (Massive ViewControllers problem).

The architecture

As you can see in the architecture block schema, there are from 5 up 7 seven levels, so is essential that each component only interacts with its upper and lower components.

iOS app architecture

The exception that confirms the rule

There is one case that Rule II will be overridden, and is the case when two (or more view are presenting some information) and this information is updated/deleted from Data Layer. In this case the presenters get subscribed to data events broadcasted by Data Manager, and when presenter gets the notification then fetchs for the latest data to present.

iOS app architecture

Implementation

iOS app architecture

Basically the implementation is an almost empty project, with the basic folder organisation for holding different layers ant its components that matches with architecture presented:

We have added a very basic app start up sequence, is very important to keep this part of code under a tight control, leave only necessary code in AppDelegate.swift:

InitAppSequencer is the responsible for the start up sequence, this class dispatches the different operations needed during start up app sequence.

Up to this moment the unique task (operation) that app must do is just present the main screen.

If you want to know more about sequencers you can find in my pot post Design iOS app start up sequence (with Operations)

Pods

The pods that highly recommend and will improve dramatically the quality of your code are:

  • R.Swfit. Get strong typed, autocompleted resources like images, fonts and segues in Swift projects
  • Swiftlint. A tool to enforce Swift style and conventions

You can download the code used in this post from here.

Conclusion

In this post has been presented the layered structured of this architecture and the rules that governs the communication between each component.

What’s Next

In the next posts will be presented in detail: View, Domain and Data layer.

References

 

     

Design iOS app start up sequence (with Operations)

The aim of this post is just to propose a design pattern for implementing the start up sequence for an iOS App. When Coordinator pattern is not enough for your app needs, then  is time to move to a upper level of abstraction.

The reality

Lets pretend that we want to develop an iOS that allows us to compose music and  connect to a bluetooth speaker to play our creations. At the beginning the start up sequence coordinator just presents the main screen. But as the sprints are passing by, the product owner ask to implement the following requirements during the start up sequence:

  • Autoconnect to default speaker. During start up process, the app  tries to autoconnect to last connected speaker. When the app has never been connected to one, is presented a coordinator with an assistant for scanning and connect to any surrounding bluetooth speaker.
  • Force update. The app checks from a service whether can continue or require a user to update the app.
  • Tutorial. When the app has a new release or is the first time app execution is presented the tutorial slider.
  • Country/Language detection. In the first time app execution is set the language according device automatically, if language is not available then is presented to user a list with the languages supported by the app.
  • Push notifications. When the user presses on the push notification alert message, the app is started up presenting a view showing more details about the notification. On dismissing the view, appears the main app view.
  • Present a customized splash screen with an animation.
  • AutoLogin/Login/Create account. When user was previously logged in, the app request a rest service for a token_id that will be used in further rest api’s calls. Otherwise is presented an assistant for entering credentials or create a new account.
  • …Last but not least, start up sequence can not take much longer (no more than 3  secs.), when no user interaction is required.

If an sprint takes 2 weeks, Could you figure out how messy was the start up coordinator passed four months? … yes it was 😨

Why a coordinator is not enough (for this case)

A Coordinator, as well known as Router in VIPER,  is a great pattern for removing presenter the knowledge about which is the next presenter to show (Great… less lines in the ViewController 🎉). The presenters logic flow now is concentrated in a single class and second presenters can be reused by other flows. There are two articles that talks about coordinators, I highly recommend them (Coordinators Redux and An iOS Coordinator pattern)

But times goes by and oncoming features are integrated in the app start up sequence, the number of lines will start to grow and code become harder to understand. Not using coordinator pattern would have been hundred times worst 🤡.

This pattern does not fit in our start up sequence, basically because we were forcing to put in the same flow tasks that could be splited in different flows and in some cases can be launched in parallel. Is clear that I need a another point of view.

Design iOS app start up sequence

From now on, for avoiding naming mistakes we will call Sequencer to our brand new supercoordinator.

REQUIREMENTS

First of all,  we have to split the start up sequence in independendent isolated operations. The start up sequence does not have to be unique, depending on the context we have several dependency graphs.

TASKS DEPENDENCY SCHEMA

First time execution

s1

This is the worst case scenario because all operations must be serialised, the unique operations that can be parallelised are Present Splash (PS) and Force Update (FU). Fetch Configuration depends on PS because in case that did not exist country configuration for country device is presented a UI menu, so it is mandatory to wait until PS finishes for using the UI.

The rest of operations, login (LOG), tutorial (TUT), connect first speaker (CFS) must be serialised because requires user input.

Forze update (FU) has no dependency, because when update is required it will abort all the operation sequence.

Regular start up sequence

In this scenario: the country, the auto login information and also there is a known speaker to connect is known by the app, so all those operations can be parallelised.

s2

This is the best case scenario up to 5 operations can be launched in parallel.

Push notification start up sequence

s0
The unique difference between this an previous scenario is that with that using that pattern you can replace easily replace Tutorial (TUT) operation by Present push information (PPI) operation.

OPERATIONS LAUNCHER

Operations (NSOperations previously also known) is a mechanism for parallelizing tasks on iOS. Is very useful when the app has to do a lot of massive and repetitive taks, like for example fetch images from a server and present them on a collection view. This really removes the dust from device cpu cores 🚀.

Another great advantage that provide us Operations is that is direct to define depenendencies between tasks, so tasks will be executed in the order defined by the dependency graph.

An operation can be cancelled so in case that Force update requires to update the app, it can cancel the rest of operations and take control of UI.

🖐️ I highly recommend to view Advanced NSOpetations (WWDC 2015)

IMPLEMENTATION

This is the implementation of first time execution:

This is incomplete, because the point is to dynamically build the task dependency depending on the scenario that app starts up.

An operation would be something like this:

For understanding concurrent operation, please take a look at Ray Wenderlich video AsyncOperations

TESTING

For validating the sequencer is as easy as creating a unit test that validates the dependencies between operations in the deferents scenarios that app can start up.

And for validating an operation, just add completion block to the operation. The test will launch the operation, and the unit test will be executed in the completion block. In case of tutorial the unit test will validate is (or not) the topViewController, and DataManager has knowledge that has been presented and does not have to be presented next time (unless the app will be updated to upper version)

Conclusion

We have tried to demonstrate the approach presented is more accurate than Coordinator pattern when we want to sequence (and paralelize) something as heterogenous as a start up sequence.

References