Tutorial: Improving Your iOS App Architecture – Part 1 of 2

This tutorial is an excerpt from our book series iOS App Development for Non-Programmers, available in the iBookStore for the iPad, and on Amazon.


Part 1 of this post looks at a sample App with standard iOS architecture and points out the pitfalls you need to avoid. Part 2 of this post demonstrates the same sample App with an improved architecture that helps make the App easier to design, create and maintain.


The Importance of Solid Architecture


Architecture. The word is usually associated with constructing a building. Whether it’s as small as a shed or as large as a skyscraper, architecture is important to make sure the design is sound. This is no less true of constructing software. Whether it’s a small iOS App or a large business application, a good architecture can make it easier to design, build, and extend your software.


Why should you care about your App’s architecture? A poorly designed App is more difficult to design, create, and maintain. A well-designed App is the gift that keeps on giving. You will save yourself, time, energy, and cost by creating an App with a solid architecture. 


Design Patterns to the Rescue


Recently, when I needed to build a horse shed, rather than starting from scratch, I went to the Internet to see what architectural plans other people were sharing. This was my first time building a horse shed, and I knew others had already learned from experience what to do, and what not to do. There were specific patterns for building this kind of structure that I wanted to learn rather than make all the mistakes myself.


Fortunately, others have come before you and have built many different types of software applications. What these developers and architects have learned has been distilled into some common design patterns. The great thing about these patterns is they work well regardless of the tools you are using—whether it’s Objective-C and iOS, Java and Android, or C# and Windows Phone. In this chapter, you will learn about some of these design patterns that Apple has implemented in iOS App development, and you will see how you can easily improve on some of these patterns.


Your App Will Change


Let’s be clear about this. Your App will change. Not just once or twice, but many times over—and that’s even before you release it to the App store for the first time. After it’s released, your App will change even more as others use it, provide feedback and suggest enhancements. You have to be prepared for that, because it’s a reality of writing Apps.


If your App is designed to anticipate change, this process is much easier. If your App is not designed to anticipate change, you are headed for a lot of tedious and unnecessary work. Keep in mind that the majority of your time is not spent in the initial creation of the App—the majority of your time is spent updating and enhancing the App as you roll out subsequent versions. Also, there are changes that come from Apple as they release new devices and add new features to iOS. 


If you get a change request and you find that you need to tear apart your App to implement the change, then you didn’t do your job as an architect! You want to make sure you have designed your App to anticipate change to make this process as smooth as possible.


Where Do You Put Your Code?

In Book 1: Diving In, you learned there are three main parts of an App:

  • User Interface

  • Core Logic

  • Data

Having these separate parts in mind when creating your App is a great start in creating a solid architecture. When these different parts are too tightly bound together, you create what is known as a monolithic architecture that is difficult to change. It creates a situation where you can’t change one part of the App without changing the other.

Fortunately, it’s not difficult to follow sensible architectural principles. Ultimately, where you put your code has everything to do with how easy it is to write your App, extend it, and maintain it. Putting your code in logical, predictable places also helps you avoid the game of “where’s the code?” that many developers play on a daily basis.

Model-View-Controller

A more formal way to look at the three main parts of an App is by means of the Model-View-Controller design pattern. This design pattern has been around for many years and helps you clearly partition the main parts of your App so it is easier to design, enhance, and maintain. 

Because of my background in software architecture, when I first came to the iOS platform back in 2008, I was intrigued to hear that Apple encouraged the use of the Model-View-Controller design pattern in iOS App development.

Here’s how the Model-View-Controller (also known as MVC) pattern maps to the main parts of your App:

  • Model →  Data

  • View → User Interface

  • Controller → Core Logic

Let’s break this down by first looking at a traditional implementation of the MVC pattern, and then we can look at Apple’s implementation.

Model

The Model is your application’s data and, in iOS, usually takes the form of entities. An entity represents an object in the real world. For example, if you are creating an App that handles customer orders, you might have a Customer entity, Order entity, and Product entity. 

An entity has properties that represent the attributes of a particular real-world objects.

For example, a Customer entity might have companyName, webAddress, phone, and address properties that hold this information for each customer as shown in Figure 11.1.

Figure 11.1 A CustomerEntity object

You will learn much more about entities in the next chapter on Core Data.

View

The View is the part of the App the user interacts with directly and contains user-interface objects such as buttons, text fields, sliders, and so on. Because of this, Apple refers to each screen full of information in an App as a view.

Controller

The Controller acts as an intermediary between the Model and the View. The Controller is where your core logic goes. Figure 11.2 shows the interaction that occurs between the Model, View, and Controller.


Figure 11.2 Model, View, Controller interaction


In the MVC pattern, the user interacts with the view—they touch, tap, pinch, or flick a user-interface object. In response, the View passes a call to the Controller, and the Controller does something based on that interaction. For example, the user might change information in a text field, touch a Save button and the controller could take that new information and update a Model entity.


Conversely, sometimes when you save a Model entity, it gets new or default values. For example, if you save a new invoice entity, it may be assigned an invoice number. So, the model can fire an event that tells the controller, “I’ve got a new invoice number.” The controller can then update the view and the new invoice number can be displayed in the user interface.


As already mentioned, the Controller is the place where you should put your App’s core logic. For example, in a Calculator App you could have a Calculator controller object that adds, subtracts, multiplies and divides (Figure 11.3). If you have a Customer Service App, you could have a Customer controller object that creates new customers, puts customers on credit hold, and so on.



Figure 11.3 A Calculator controller object


Separation of Concerns


So, MVC is a great pattern for “separating concerns”—keeping the three parts of your App separate. You want to be able to use your data with any user interface. You want your core logic to be used in multiple places. You want your data and core logic to be independent of your user interface.


In the Model View Controller diagram shown in Figure 11.2, there are communication lines between the Model and the Controller, and between the Controller and the View, but you will never see communication between the Model and the View. They never communicate directly with each other. This allows your data and core logic to be used independently of the user interface.


Apple’s MVC Implementation


Design patterns are great. However, I have found that, at times, the implementation of a particular pattern may not be true to the pattern’s original intent. I was actually surprised at how Apple implemented the MVC pattern, because it wasn’t a traditional implementation—and this isn’t just a matter of semantics, or theory. Unfortunately, Apple’s implementation of MVC doesn’t provide well-defined boundaries between the three main parts of your App—which is the whole purpose of the MVC pattern.


Nothing speaks louder than a real example, so I have created a few sample Apps that follow Apple’s MVC pattern. By way of comparison, I have also recreated the Apps using a better implementation of the MVC pattern.


Calculator Sample App User Interface


The first sample is a Calculator App that looks and acts just like the built-in iOS Calculator App. Follow the steps in this section to get a look at how the user interface of the App has been designed.


The sample code for this post can be downloaded at the following link:


Sample Code


  1. In Xcode, open the CalculatorDemo project in this book’s sample code.

  2. In the Project Navigator, select the MainStoryboard file. You should see the scene shown in Figure 11.4.
  3. Figure 11.4 The CalculatorDemo project’s main scene

    This looks dangerously like the built-in Calculator App, and intentionally so. Let’s see what it looks like at run time. 

  4. Make sure the Scheme control at the top left of the Xcode window is set to iPhone Simulator 6.0, and then click the Run button. No surprises here. It looks and behaves just like the built-in App.

  5. Click any number, and then click an operator such as the minus sign. Notice a highlight appears around the operator, just as it does in the built-in Calculator App (Figure 11.5). This indicates the currently selected operation.
  6. Figure 11.5 The minus operator is highlighted.

  7. Click another number and the equal sign, and you can see the Calculator is fully functioning. You can use the memory keys, add, subtract, read, and clear the Calculator memory. You can even use common Calculator shortcuts. For example, if you type 1 + =, and then repeatedly click the = key, the Calculator continually adds one to the previous number.

  8. Next, hold down any of the number or operator keys and notice the shading changes slightly to indicate the button has been pressed.

Now let’s take a look behind the scenes to see how the Calculator App works. 


  1. Go back to Xcode and click the Stop button. Look in the Project Navigator, and you can see there are just a few files as shown in Figure 11.6.
  2. Figure 11.6 The CalculatorDemo project files

    The ViewController class is associated with the MainStoryboard file, and contains the user-interface logic and core logic for the calculator.

  3. Let’s take a closer look at the user interface. Click the MainStoryboard file again and look in the Document Outline window on the left side of the Interface Builder editor where you can see a list of all user-interface objects. There are five images (identified by the “img” suffix) and, as you might expect, there are quite a few button objects (Figure 11.7).

  4. Figure 11.7 UI objects in the Document Outline pane

  5. Select imgCalculatorMain in the Document Outline pane, and then go to the Attributes Inspector pane (click the third button from the right in the Inspector toolbar). Notice the Image attribute is set to CalculatorBackground.png (Figure 11.8)
  6. Figure 11.8 CalculatorBackground is the main image file in the Calculator App.

  7. To see what this image looks like when it’s not on the view, go to the Project Navigator, expand the Supporting Files group and select the CalculatorBackground.png file. This displays the image in the Interface Builder editor (Figure 11.9).
  8. Figure 11.9 The Calculator background image

    You may be surprised to see the buttons are not separate images. The calculator background is a single image that includes all the buttons you see at run time! How is the illusion of separate buttons created when you run the App? There is an invisible, rectangular button over each of the buttons.

  9. To see this, go back and select the MainStoryboard file in the Project Navigator. Click over the number 9 button. As you can see in Figure 11.10, you have selected an invisible button that is positioned directly over the button.

    Figure 11.10 There is an invisible button located above each button in the main App image.

    With the invisible button selected, go to the Attributes Inspector. Notice in the header at the top of the Inspector pane it indicates the object you have selected is a Button as shown in Figure 11.10. By default, this button is invisible, allowing the number button on the image below it to show through.

    In the Attributes Inspector, the State Config list box allows you to select different button states. Each of these options represents a state the button can be in at run time:

    • Default – Indicates the state of the button when the user is not interacting with it.
    • Highlighted – Indicates the state of the button when the user is holding the button down.
    • Selected – Indicates the state of the button when it is selected.
    • Disabled – Indicates the state of the button when it is disabled.

    When you select a state from the State Config combo box, it displays the Title, Image, Background, Font, Text Color and Shadow Color for the selected button state. This allows you to specify different visual effects for each button state.

    With the Default option selected (Figure 11.10), notice there is no Image specified. That’s why this button is transparent at run time in its normal state.

  10. In the State Config list box, select the Highlighted state. Notice the Image attribute is set to CalculatorButtons.png as shown in Figure 11.11.
  11. Figure 11.11 The Highlighted attributes of the Calculator buttons

  12. To see what this image looks like, go to the Project Navigator and under the Support Files group, select the CalculatorButtons.png file. The image will look like Figure 11.12. It’s a gradient that changes from black at the top to light grey at the bottom, which is the opposite of the gradient you see when the image isn’t pressed as in Figure 11.10.
  13. Figure 11.12 The CalculatorButtons.png file

    At run time when the user presses their finger on the invisible, it enters the Highlighted state, and this image becomes visible.

  14. Now go back to the Project Navigator and select the MainStoryboard file again. If it’s not selected, reselect the invisible button over the number 9 button. Next, scroll down in the Inspector window until you see the Alpha setting (Figure 11.13). 
  15. Figure 11.13 The Alpha attribute specifies the transparency of a UI object.

    This setting specifies the transparency of an object. The value of Alpha can range between zero and 1. To make the effect more subtle, the Alpha value of the button is set to approximately .2 as shown in Figure 11.13.

  16. Also, notice in Figure 11.13 the Tag property of the invisible 9 button is set to 9. Each of the numeric buttons have their tag number set to the number they represent (the 8 button is set to 8, and so on). The decimal point button ( . ) has its Tag property set to -1. You will see in the next section how these tag numbers are used.

  17. When the user touches a number button at run time, even though it’s invisible, it fires the same events as a regular button. 
  18. To see this, with the invisible number button still selected, go to the Connections Inspector by clicking the button on the far right of the Inspector toolbar. As shown in Figure 11.14, there is a connection from the button’s Touch Up Inside event to the numberTouched action method of the view controller. All number buttons (as well as the decimal point button) are connected to the numberTouched action method.

    Figure 11.14 Calculator buttons are all connected to the numberTouched action method.

  19. Now let’s take a look at the operator buttons. Go back to the scene in the Interface Builder editor, select the plus ( + ) button, and then look at the Connections Inspector again. As shown in Figure 11.15, the button’s Touch Up Inside event is connected to two action methods:

    • highlightOperation: – This method adds the highlight around an operation button when it’s selected. This connection only exists for the add, subtract, multiply, and divide buttons.

    • operationTouched: – This method performs the function associated with the selected key (add, subtract, multiply, divide, and so on).
  20. Figure 11.15 Operation button connections

  21. Next, click on the Calculator’s numeric display at the top of the view as shown in Figure 11.16. 

  22. Figure 11.16 The Calculator’s numeric display is a label.

  23. Go to the Attributes Inspector by clicking the fourth button from the left in the Inspector toolbar. As you can see in Figure 11.16, the Calculator’s numeric display is a label whose Alignment is set to right justified. By default, the iOS label control doesn’t have a margin setting, so to make it look like the label does have a left and right margin, the label does not extend the full width of the view. Its Background is set to transparent so the color in the view behind it shows through.

This should give you a basic understanding of how the user interface works in the Calculator sample App.


Calculator Sample App Core Logic


Now let’s take a closer look at the view controller code files to see how the core logic code in the view controller interacts with the Calculator user interface.


  1. In the Project Navigator, select the ViewController.h file. At the top of the header file is a declaration of an Operation enumeration. This enumeration details all operations that can be performed by the Calculator:
  2. typedef enum {
    OperationNone,{
    OperationAdd,{
    OperationSubtract,{
    OperationMultiply,{
    OperationDivide,{
    OperationEquals,{
    OperationClear,{
    OperationMemoryPlus,{
    OperationMemoryMinus,{
    OperationMemoryRead,{
    OperationMemoryClear,{
    OperationPositiveNegative{
    } Operation;

  3. Below the Operation enum declaration are three property declarations, two of which are Interface Builder outlets. Figure 11.17 shows how the outlet properties connect to the Calculator.

    Figure 11.17 Calculator outlet connections

    • lblTotal – An IBOutlet property that is connected to the label comprising the Calculator’s display.

    • operationHighlightImages – An IBOutlet collection property that is connected to the highlight images of the add, subtract, multiply, and divide buttons.

    • value – A regular property that holds the current string value displayed in the Calculator’s numeric display label.

  4. There are also three action-method declarations in the ViewController.h file. Figure 11.18 shows how these action methods connect to the user-interface controls.

    • operationTouched: is an action method connected to the Touch Up Inside event of all operation buttons.

    • highlightOperation: is an action method connected to the Touch Up Inside event of the add, subtract, multiply, and divide buttons. This method adds the highlight around the selected button, and removes it from all other buttons (because only one operation can be highlighted at a time).

    • numberTouched: is an action method connected to the Touch Up Inside event of all number buttons, including the decimal point button.
  5. Figure 11.18 Calculator action-method connections

    These properties and methods are all user-interface-specific since they are tied directly to UI controls. 

  6. As already discussed, you should keep the user interface separate from the core logic. To help make a division between the user-interface-specific methods and the Calculator core logic methods in the view controller, I have physically grouped them together. To see this, go to the Project Navigator and select the ViewController.m file. In the jump bar at the top of the Code Editor, click the section on the far right as shown in Figure 11.19.
  7. Figure 11.19 Click the section on the far right of the jump bar to bring up a list of class members.

    At the bottom of the popup list, you can see two sections labeled Calculator User Interface and Calculator Core Logic (Figure 11.20). Notice the three user-interface-specific methods shown in Figure 11.18 are listed under the Calculator User Interface section of the popup.

    Figure 11.20 View controller code sections

    These sections exist in the popup because of the #pragma mark directives in the ViewController.m file (for more information, see Chapter 10: The Code Editor) Given the current architecture in this project, creating separate sections in the view controller is about the best you can do to separate user-interface-specific methods from core logic.

  8. Figure 11.21 provides a high-level overview of the interaction between the numeric buttons of the Calculator in the user interface and the methods of the ViewController class.
  9. Figure 11.21 Calculator numeric-button code interaction

    1. The user touches a numeric button.

    2. The view controller’s numberTouched: action method is called, which contains user-interface-specific code.

    3. In the numberTouched: method, after executing the user-interface processing, the view controller’s processNumber: method is called.

    4. The processNumber: method executes the Calculator’s core logic for handling a new number, and returns the new Calculator value.

    5. In the numberTouched method, the return value from the processNumber: method is stored in the text property of the lblTotal label.

  10. Now let’s take a closer look at these methods. In the jump bar at the top of the Code Editor, click the section on the far right again. From the member popup list, under the Calculator User Interface section, select the numberTouched: method to view it in the Code Editor. 

  11. You should see the following code:

    - (IBAction)numberTouched:(UIButton *)sender {

    NSString *numberString;
    NSInteger tagNumber = [sender tag];
    if (tagNumber == -1) {
    numberString = @”.”;
    }
    else {
    numberString = [NSString
    stringWithFormat:@"%i", tagNumber];
    }
    [self hideOperationHighlight];

    self.lblTotal.text =
    [self processNumber:numberString];
    }

    Remember, this is the method that is immediately called when the user touches a numeric button in the Calculator. Everything that happens in this method is user-interface specific:

    • When this method is called, the button passes a reference to itself in the sender parameter—buttons are user-interface objects.

    • At the top of the method, the tag number is retrieved from the selected button, and converted to a string. If the tag number is -1, the string is set to the decimal point. Again, buttons and their tag numbers are part of the user interface.

    • Afterwards, the hideOperationHighlight method is called which hides any operation key highlight that may be visible on the user interface.

    • Next, at the bottom of the method, a call is made to the processNumber: method.

  12. If you look at Figure 11.20, you can see the processNumber: method is listed under Calculator Core Logic. That means the method should contain no user-interface logic—just pure Calculator logic. To see this method, in the Code Editor jump bar, click on the section to the far right again and select the processNumber: method from the popup list. Here are the key actions this method performs:

    • If the last action was an operation, clear the current value.

    • Make sure the user hasn’t entered the maximum number of digits.

    • If it’s a decimal point, make sure there isn’t already a decimal point in the number.

    • Append the new digit or decimal point to the current value.

    • Format the current value to include commas (if any are needed).

    Each of these actions is definitely a part of the Calculator’s core logic, and is separate from any user-interface considerations.

  13. Now let’s take a quick look at how the user interface and view controller code interact when an operation is selected by the user as outlined in Figure 11.22.

    Figure 11.22 Operation button and code interaction

    1. The user taps an operation button.

    2. The view controller’s operationTouched: action method is called, which contains user-interface-specific code.

    3. In the operationTouched: method, after executing the user-interface processing, the view controller’s performOperation: method is called.

    4. The performOperation: method executes the Calculator’s core logic for performing an operation and return’s the Calculator’s new value.

    5. In the operationTouched: method, the return value from the performOperation: method is stored in the text property of the lbTotal label.

  14. Let’s take a closer look at these methods. In the jump bar at the top of the Code Editor, click the section on the far right again. From the member popup list, under the Calculator User Interface section, select the operationTouched: method to view it in the Code Editor. You should see the following code:

    - (IBAction)operationTouched:(UIButton *)
    sender {

    NSInteger tagNumber = [sender tag];

    self.lblTotal.text =
    [self performOperation:tagNumber];
    }

    Again, this is the method that is immediately called when the user touches an operation button in the Calculator, so everything that happens in this method is user-interface specific. 

    • When this method is called, the button passes a reference to itself in the sender parameter—buttons are user-interface objects 

    • At the top of the method, the tag number is retrieved from the selected button. Again, buttons and their tag numbers are part of the user interface.

    • Next, at the bottom of the method, a call is made to the performOperation: method.

  15. If you look at Figure 11.20, you can see the performOperation: method is listed under the Calculator Core Logic section. Again, that means the method should contain no user-interface logic—just pure Calculator logic.

    To see this method, in the Code Editor jump bar, click on the section to the far right again and select the performOperation: method from the popup list. Here are the key actions this method performs:

    • The display value is converted from a string to a double value.

    • If the operation is the type that should be performed immediately (such as All Clear, Positive/Negative, or any of the memory operations), that operation is performed.

    • For all other operations, a check is performed to see if there is any previous operation, and if so, that operation is performed.

    Again, each of these actions is definitely a part of the Calculator’s core logic, and is separate from any user-interface considerations.


What’s Wrong With This Architecture?



The Calculator App seems to work well. It adds, subtracts, multiplies, divides, and performs all other operations properly. So what’s the problem?


Check out the strong link between the Calculator user interface and the view controller as shown in Figure 11.23. In object-oriented programming terms, this is known as tight coupling.

Figure 11.23 Tight coupling between the Calculator UI and core logic

 


Per Apple’s developer documentation, this is typical. A view is typically bound to a single view controller. Ultimately, the view controller is a user-interface object. It’s not the tight coupling between the view and the view controller that’s the problem—that’s perfectly fine. The problem is the core logic code that’s in the view controller.



A good App is often a victim of its own success. Let’s say you release the Calculator App with its current architecture. If it does well in the App store, you may consider creating a Scientific Calculator App. Wouldn’t it be great to reuse some of the functionality of the Calculator App, since the Scientific Calculator does everything the regular Calculator does and more?


Unfortunately, because the Calculator’s core logic is buried inside the view controller, there isn’t a clean way to reuse this logic in another App. It’s “stuck in the weeds” of the user interface.


A Better MVC Implementation

So, how do you fix this problem? You need to put the Calculator’s core logic in some other place where you can access it from multiple Apps, or from multiple view controllers in a single App. If you shouldn’t put your core logic in a view controller, where should you put it? 

Part 2 of this post provides the answer to these questions and helps you build a solid architecture for your iOS Apps!

Kevin McNeish


Twitter: @kjmcneish


 


 

4 thoughts on “Tutorial: Improving Your iOS App Architecture – Part 1 of 2”

  1. From which book is this article an excerpt ? 1, 2 or 3 ?

    I have all ready developed apps for iOS but I am looking for a good book to improve my skills in apps architecture like :
    - Why and Where to use a singleton
    - Maybe service injection for the business controllers
    - blocks
    - …

    Any suggestion ?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>