iOS App Architecture for Web Developers

Chloe Verity
Songkick Tech
Published in
7 min readJul 9, 2020

--

Part 2: Views

This article is part of a series of posts dedicated to helping web developers new to mobile development bridge the gap between building web apps and iOS using the common MVC pattern, as told by three developers at Songkick who have recently made the jump themselves.

If you haven’t read the previous post, please click here.

Views in iOS

As you will recall from our previous post (by one of our Swift Senseis in residence, Alex) on the Model-View-Controller pattern, Views are the third of the MVC trio that — yep, you guessed it — represents all that we see. It’s the representation of the user interface , the “face” of our app. Views are not where the business logic of the program resides — we’ve seen that in the models — instead, the View illustrates the interface, detecting user interaction and enabling navigation through the app. The interface allows the user to create an action that might send new information or instructions back to the Controller, which will then relay those message(s) to the Model. A View should not speak directly to the model; instead it should communicate to the ViewController that manages it.

The MVC approach; as we saw in the last post

When a controller has decided what should be presented on the page, it loads the View. The View is likely composed of a set of UIView objects, which then draw their specified contents, which may in turn be other views.

Views, similarly to HTML elements, can be nested inside each other, where the parent views are a superview, and the children are subviews. Apple provides a base class (UIView) that all other view classes should inherit from. Since these view classes are now subclasses of UIView, they inherit all of its properties and methods; the parent class now becomes the superclass. Also similarly to HTML, this allows for the repurposing of views across the app; whether it be a more basic, built-in view like a UIButton or UILabel, or a more complex, custom view that is created by you for a specific purpose, unique to your app.

How do I create a View?

Great question. You have options! In general, your app (depending on its size) will likely use a combination of these options.

XIB it!

One thing unique to Swift is Interface Builder. Where developing on the web can have a slow feedback loop, stopping and starting to check on the results of a small change, IB allows you to fully visualise your app (on any iOS device you want, no less). You can tie components to the edge of a screen, or to parent or sibling views using constraints in autolayout, leading to a great user experience. Changing things like size, colour, text, in the attributes and size inspector, and even managing its connections (i.e. interactions) to a specific event in the View Controller (we’ll talk more about this later) in the connections inspector means that everything is relatively easy to manage — especially for newcomers to both your codebase and Swift in general. It’s also a great way to prevent your codebase from becoming inflated with unnecessary lines of code, and reducing the amount of bugs you might produce in them. Saying this, it’s worth noting that version control in XIBs (and Storyboards too) can get a touch hairy if several developers on your team are working on the same files.

The Programmatic approach

You can of course, write the same code programmatically (and actually, more, since XIBs lack some customisations that are possible in code). If performance is key for your app, it’s important to know that the file loading time of a XIB file is slightly longer than building the same UI programmatically. Some developers also go programmatic over XIBs since you have more control, and it can be easier to debug, though more difficult to visualize.

A never-ending Storyboard

A storyboard outlines the sequence of screens in an app, for example a signup flow, and the relationships between them. It can be a great way to visualize the UI flow of certain user journeys, and divide up what would otherwise be a complex navigation sequence into smaller, more manageable flows. Conversely, some dev teams avoid storyboards since it’s hard for multiple developers to work on them at once; and the larger your app the more complicated the web of flows may become.

Example of how storyboard is used to visualise user flows

As I said earlier, the larger your app gets, the more likely it is that for certain situations and user flows, you will pick which of these suits your needs. Using any of these paths you can create a UIView, in each of which you will have access to the object library to use buttons, labels, switches, the whole shebang! This is particularly easy in IB or Storyboard since dragging and dropping one of these objects is essentially all it takes to add an instance of it into your View. Whichever option you choose, you should try and specify a View’s relative size and position to its superview; creating rules with Auto Layout will then govern how the size/position of the view will change in response to changes in the rest of the view hierarchy.

Outlets and Actions

As I said previously, we don’t want any business logic to reside inside the view, since its sole responsibility should be presenting the UI that the ViewController tells it to. Ideally, the View shouldn’t even be aware of the model.

So, how do we handle user interaction if there’s no logic allowed?

Outlets are used to make connections between objects in the views, and variables in the code. You can make these connections by ctrl-dragging from the XIB file into the ViewController. Now that we have the reference to the View stored in the ViewController, you have access to its properties and the ability to update them. You might need them, for example, to fetch dynamic text or images; so you simply place an outlet for a View in your ViewController. You don’t need to store references in the ViewController for each and every View in the hierarchy, really only the ones that need to be changed later, or with which you need to specify or report a user interaction.

To add a new outlet, action, or outlet collection, ctrl-drag the component from the XIB to the ViewController

Actions are similar to Outlets, but go in the opposite direction. Outlets are variables that connect code to the layout, and are loaded when the XIB is loaded; using Actions is a way of making layouts trigger code — they are functions that the objects loaded by the XIB can call. An Action takes one argument: a sender. The sender is the specific UI element that will be calling the method. Actions are also created by ctrl-dragging from the View element to the ViewController.

Context please!

Let’s say we have a UIView that contains a UIButton and a UILabel. We want the UILabel to change its text when the UIButton is pressed.

When the UIView’s controller instructs the view to draw itself, the Outlet variables for both the UIButton and UILabel will be loaded, and hence creates their connection from the Interface Builder to the property in the ViewController. You can check that this connection exists by looking in the connections inspector in the inspector toolbar.

The Connections inspector in XCode

When a user interacts with the View, for example pressing our UIButton, the relevant Action for that specific UIButton will be triggered, and the ViewController will interpret the interaction; usually a state and/or some information will be passed back to the Model. What will typically happen is the Model will use this information to perform subsequent logic, and will pass its findings to a ViewController (maybe the same one, maybe different). That ViewController in turn will interpret, and will load a View (or Views) which will then draw itself on the screen. In our case, once the Action is triggered, the ViewController will be made aware; and can subsequently perform a function that uses the Outlet tied to the label to change its text.

In this image, the button is linked to an IBAction that will update the label’s text

Where the ViewController is what handles the events that come down the responder chain, it’s the View that usually handles their own touch events, and reports the results to the ViewController via an associated delegate i.e. our Action method.

Conclusion

In this part we’ve covered what a View is, how does it tie in with our MVC model, how we can set one up, and how to use one to allow a user to interact with your UI.

I’m aware I’ve slightly stepped over into ViewController territory in this post, though it’s difficult to separate the two since each View is managed by a ViewController. We’ll cover a lot more of the responsibilities of a ViewController in our third and final post by our next Swift Wizard, Felix!

--

--