How to structure the code of iOS apps

How-to-structure-the-code-of-iOS-appsA couple of years ago a company hired me for a last moment rescue of an app they were developing for a client of theirs. After months of development the app was late and full of bugs. The goal was fix as many bugs as possible in one week, before we delivered the app to the final client. This was not the first time I had to rescue a project and probably will not be the last. Any freelancer can tell you that, as deadlines get closer, companies sometimes bring in extra power to be able to meet them. As I poked around the code it became clearer and clearer what the source of the problem was.

At the same time it was clearer and clearer that it was not possible to finish this on time. Every time I fixed something, something else broke, in a worse way. Every fix led to more work. After a couple of days I delivered the bad news to my client: “These are not simple bugs. The problem is another: the architecture of the app is completely wrong. The solution would be to rewrite the complete app, but I cannot do this in a such a short time. I can try to fix as many bugs as I can cutting some corners, but this will only solve the symptoms and not the problem. More issues will surface in the future”.

I am the first one to consider a rewrite of an app as the last possible course of action. When you rewrite an app, the work spent on it gets completely thrown away. It is a big waste of time and money, so I always try to save the code if possible. Still, sometimes it is necessary and more efficient to start from scratch. We tried for a week the simple route, but new and harder bugs kept surfacing as predicted.

They were not able to meet the deadline. They apologized to their client and hired an external team to rewrite it, as I had to move to other projects I had scheduled. This of course cost them more money, time and reputation. This was not the first project I had where the only choice was to throw away the entire code base with the thousands of euros spent on it. Unfortunately, this happens regularly.

There are usually many bad programming practices that bring a project to this state. The root cause, though, is usually a complete misunderstanding of the architecture of iOS apps. I already wrote about the basic concepts you should know to develop iOS apps. Now let’s see how they all come together.

At the center of every iOS app we can find the Model-View-Controller pattern. Generally speaking, design patterns should be part of the toolkit of every developer, to avoid situations like the one I described above. MVC is a pattern exists on many different platforms and not only on iOS. The difference though is that in iOS the MVC pattern is at the center of any app you will write. It is not an optional pattern you can chose to avoid: iOS is designed to work with apps written in this way.

The MVC pattern does not draw hard boundaries between its three layers though, and some objects live across these boundaries. At the one between the view layer and the controller layer we find view controllers. View controllers have a central role in iOS and are a required part of any app. Because of their central role, the iOS SDK provides the UIViewController class, which encapsulates the required behavior. Every view controllers has to descend from this class.

A view controller in iOS usually represents and manages a single screen of the app. Every view controller has an associated view, which contains all the user interface elements for the managed screen. In this way, view controllers form the skeleton of your app. If you think about an app as the screens it can have, there will be a view controller for each one of them.

View Controller

iOS apps have a single window that fits the whole screen of a device, unlike computers, where apps can have multiple windows of different sizes. In the MVC pattern, the window of an app is a view (and indeed is a subclass of UIView in iOS). The views managed by view controllers are added and removed to the window, making the app flow from one screen to the other.

Here is how it happens. First of all, the UIViewController class itself includes functionality to display other view controllers. A view controller can present another view controller modally: the new view controller is placed over the previous one, usually moving in from the bottom of the screen. Although needed, this is a specific and limited way to transition from one view controller to the other. The flow of the screens of an app is usually managed in a different way.

There is a set of special view controllers in iOS, called containers, which have a different role. Instead of managing a single screen of an app, containers manage a set of view controllers. They are responsible of managing the lifecycle of contained view controllers and the transitions between them on the screen. Containers are view controllers themselves, so they also have a managed view. They use this view to display other view controller views as they come on or get off the screen.

This allows us to specify the flow of iOS apps through a sequence of view controllers, without worrying about how to get their views on the screen. A container takes care of this, animating the transitions and managing the lifecycle of each view controller for us.

Container

Any non-trivial app has at least one container view controller. Since containers are view controller themselves, they can also be contained inside other view controllers. This allows to build more complex navigations in your app. A common case, for example, is a tab bar controller containing navigation controllers. In this way you can use the tab bar to switch between sections of your app, while you can navigate the hierarchy of each section with navigation controllers. The Facebook or Twitter apps are two popular apps that use this pattern.

On the other end of the spectrum we have the model layer. Model classes encapsulate the logic of an app they represent the data of the app and the operations on them. They are not concerned about how the user interface behaves. For this reason, they do not know anything about views or controllers. View controllers are responsible for moving the data between the model layer and the view layer. For example, if you are building a social media app, you might have model classes holding the data of users, posts, comments, pictures, videos, etc. These classes would only hold data though, and would not hold code related to presentation, like views, colors, fonts, etc.

Controllers do not only interact with pure model classes though. Sometimes model controllers are necessary to organize and manage the data or interact with different parts of the system. Some examples might include: data sources for table views, managers for network requests or controllers to collect data from the sensors such as the GPS, accelerometers, the compass etc. This is the final picture:

Model

It is important to look what arrows connect and their direction. View controllers communicate pretty much with every object in the chart. For this reason they tend to grow too much and include a lot of code that should go into other classes. They need to communicate with the model to update it and to move data to the view layer. They also need to communicate with the container to trigger the transitions between screens.

Views communicate mainly with view controllers, since they need to receive the data to display and pass events back so that the view controllers can update the model. There is no connection from the views to the model in the MVC version of iOS, even if it is present in other versions of MVC (although, let’s be real. Sometimes it can happen in iOS too). To simplify the passage of data to the views, it is possible to use the another design pattern called Model-View-View-Model. MVVM is just a variant of MVC, so it is not hard to adopt.

The model and model controllers, though, have no arrows “up”. This is because the model of an app needs to be independent from the rest of the architecture. Model classes communicate between them, but have no knowledge about views or view controllers. This way when we change the interface of the app, the model does not change and becomes reusable also in other apps.

Continues below

These are only few of the fundamental aspects of professional iOS apps. Learn the other ones in my free course

100% privacy. No games, no spam. Unsubscribe at any time.

Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+
  • Thomas Richardson

    Love this article Matteo. Their are so many tutorials on iOS that one can be bombarded. Even when your learning from some of the more popular tutorial sites they teach in a method that dosnt encompass this very basic design concept (MVC)

    • Thanks! Indeed, I am following this approach because I find most of the things I read in tutorials disappointing.

      • dc_cafe

        I tried a build/learn/iterate approach, and I ended up deep in trying grock the concepts of “design patterns” and MVC (and it’s variants). I have to admit, this is one of the most fundamental things a good product developer needs to know. And in fact, I would say to use this as an extension into other tutorial. Kudos, for a good intro and a well written foundation.

  • Sumita Banerjee

    Awesome tutorial. very nicely described. Can you please provide a sample app to demonstrate the MVVM architecture?

  • Appels+Oranjes

    Hi Matteo, one question: I like your approach but what if I have 31 endpoints? That is: should I create (at least!) 31 models?

    • What do you mean by endpoints?

      If you are talking about “entities”, I would say yes. For example, if you build an app for Facebook, you would have a model for users, one for posts, one for comments, etc. Every concept in your app has a model type.

      If by endpoint you mean network API to which you connect, then no. I rarely see apps connected to more than one, let alone 31. But if you need to connect to different APIs, you would still need one model type for each concept. Then you need to write code to bridge each of these models to their representation on a selected API.

      • Appels+Oranjes

        Endpoint is, in a restful API context, basicly the URL to which direct a request:

        mydomain/…/search/param,
        mydomain/…/events/event, etc.

        I worked on an app with 31 endpoints.

        So, using your design (that I like, just to be clear), I should create 31 models because endpoints return completely different JSON data.

        • Ah, I see what you mean.

          It depends, but usually the answer is no. You create a model for an entity, not an endpoint.

          If we take the Facebook example I made above, you would have a model for a user, a model for a post, a model for a comment.

          Let’s say you have an endpoint which returns your profile. This contains your user data, and the timeline for your posts and the comments. You would use three model types for just this endpoint.

          Then you have an endpoint which returns your list of friends. This endpoint would return a list of users. Here you reuse the same user entity you used above.

          Another endpoint might return your news feed. Here you have posts of your friends and comments. This endpoint uses two model types, which are the same you would use in your timeline.

          So when talking about data, you have models for data entities: users, posts, comments, etc.

          Then you would have to represent the API endpoints. If you look at my other article on networking, here you create a model for each endpoint, in addition to the data models you already have for entities.

          But API endopoints usually have a lot of common functionality. They all have a URL and a set of parameters. So you probably can create far fewer than 31, even just 1 if that is enough.

          • Appels+Oranjes

            Ok, you say (for example) I could have an endpoint /user and one endpoint /users, that returns an array of Users: in that case I only need one entity User.

            And one endpoint could return data that can be managed by more than just one entity.

            So, the reply to my answer is: you could have 31 endpoints with 31 entities as a result only if they didn’t share any data.

          • Yes, indeed.