IB Free in Practice 1: Managing a Modal

IB Free In Practice Logo

This series of blog posts will provide examples of how we develop Swift applications without IB and demonstrate some general strategies for app architecture. For this first post, I’m going to cover presenting and dismissing a modal view controller over a home view controller. Even though the example is relatively simple, I am going to approach building it the same way that I would approach building a professional app. This means that in addition to building the app with reuse and compartmentalization in mind, I will also try to deal with many of the ‘little things’ that often crop up over the course of development. For example, in this post, I’ll cover how to manage custom colors, fonts, a blur effect, and the color of the status bar.

Some Thoughts on Code Patterns and Style

Because I will be addressing a number of common decisions that we face in developing an app, I want to first stress that often when it comes to architectural decisions there’s not a single right way to do things. At Raizlabs, we don’t enforce a single way of developing apps. Each tech lead and their team are free to agree upon architectural decisions and code patterns.

What is important, however, is that the decisions remain well thought-out and consistent throughout the project and that these decisions are well documented. Inconsistency can make managing the project difficult across team members, and it has an even more severe impact on future generations. We frequently have teams roll on and off projects, and we also frequently hand code off to our clients after completing a project, so readable future-tolerant code is a must.

Having said this, I will sacrifice some consistency for the sake of code readability when the code is simple. Given our architectural emphasis on small components and reuse, we often end up with very small code files that are only made more complex by using extensions or breaking out constants into an enumeration. One technique that I have found useful is to allow for different code styles depending upon if the code is of high or low complexity. If the code is of high complexity, then a style that does not allow for magic literals and makes heavy use of extensions to organize code is used. If the code is of low complexity and everything can be accomplished in a small space, then magic literals are allowed and things like styling and layout can all be done in the initializer as opposed to extensions. The style that is adopted should be consistent within the code file, and there should be a low threshold for moving from the low complexity style to the high complexity style. For example, if a constant is reused, then it should be placed into a Constants enumeration and the high complexity style should be adopted for that file.

Getting Started

For this app, the home screen and the modal screen will look like the following:

The main and modal screens

IBFree In Practice 1. The main and modal screens.

On the home screen there’s a Raizlabs logo, and below it, a pill-shaped button. When the button is tapped, the modal is displayed with the default modal transition (e.g. slide up from the bottom). The modal also blurs the background of the home screen when it is presented. Tapping the ‘X’ in the upper-right corner of the screen will dismiss the modal. The only wrinkle here is that the status bar is white for the modal and dark for the home screen.

Setting Up the Project

This project uses Anchorage, which is a set of intuitive helpers for coding Auto Layout. Please refer to my original IBFree Post to learn how to set up a IBFree project. You can also download the completed project.

Planning the approach

First, build the things that will be used throughout the app: in this case, the style helpers for fonts and colors. Next, rather than trying to implement the UI of these screens all at once, start by building separable components of each of the screens (e.g. the logo image view and the pill button). The point is to make each of these components as simple and self-contained as possible. In general, the layout of each of these components should be so straightforward that you’ll have little need to run and test them via visual inspection.

While it is not going to happen in this sample project, this approach has an added advantage in that many of these components, such as the pill button and cancel button, will be reusable across screens that share the same style.

Building Style Helpers

Font and style helpers will be used to avoid having to specify fonts and colors repeatedly and to organize all of the fonts and colors for the app. I prefer to use enumerations for these style helpers, as opposed to extending UIColor or UIFont because I want to strictly control what fonts or colors are used throughout the app.

App Fonts

Create a new Swift file called AppFont. Then create a private static function that will take a font name and size and return a UIFont. You can then create a static function for each of the fonts that will be used in the app:

 

It may be tempting to have different functions for different font sizes (e.g. ‘largeHelvetica’) or have different enumerations for font sizes and fonts. In my experience, so many different font sizes are used throughout a professional app that this approach quickly leads to lookup frustration and embarrassing names like ‘extraExtraLarge.’

Also, note that in the size function, rather than force unwrapping the UIfont, I log an error and use a system font instead. In almost all cases it is best to have the app report an error and gracefully recover rather than crash. If an error means that your app will get into some dangerously undefined state, then the code should be refactored so the app can handle that state. Having an app that reports errors and fails gracefully has proved immensely useful when large-scale problems occur (for example, an API you do not control drastically changes without your knowledge).

App Colors

App colors can be specified as static constants in an AppColor enumeration. It is fairly common for our designers to specify a color in hex code, so I often include a hex transformation function:

Several things are worth noting here. First, even if you are using UIColor constants, (e.g. UIColor.lightGray), you should place them within the app color enumeration so if any app colors are modified, then they can easily be changed throughout the app.

Second, you’ll notice that  color(fromHex:) is verbosely identified as being adapted from a Stack Overflow answer. If you’ve never incorporated code from Stack Overflow or other reputable sources, good for you. But for those of us who don’t know everything, there is no shame in doing this. However, when you do incorporate code from another source, you should reference it in a comment and attribute the source correctly based upon the license under which the code is published. This can be immensely useful for future developers: if for some reason the code stops functioning properly, the developer can go back to the source and see if anyone else has noticed and solved the problem, and if not, then the developer can give something back by reporting the problem and/or suggesting a solution. Besides, the Stack Overflow terms of use require attribution of the code found on the site (this post provides a concise description of what Stack Overflow requires when attributing code).

Finally, note how the list of colors is alphabetized. This is always best practice whenever possible; it makes looking up values that much easier, and also makes it easier to see if a value you are looking for exists in the list or doesn’t.

Building Components

With the helpers in place, the next thing is to look for easily self-contained parts of the screens that we are going to be building: in this case, the buttons and image view.

The Logo Image View

The logo image view is a simple UIImageView that has a constant size and image:

Since our subclass has its own designated initializer, it has to call a designated initializer of its superclass, in this case,  super.init(frame: CGRect.zero) is called. After the image is set, the size of the image is set using Anchorage.

Note the use of #imageLiteral for setting the image. In Xcode, you can just start typing the name of an image in your .xcassets folder and you will be able to preview and select the appropriate image. You will then see a thumbnail of the image in the assignment (this doesn’t translate to the code snippet above). Or, if you type #imageLiteral this will bring up a menu that allows you to view your images and select the correct one. It is convenient to have a preview of the image being used, and using image literals here also prevents you from making any mistakes when typing an image name.

The last function is necessary because init(coder:) is declared as required in UIView and any subclass that has its own designated initializer must then also implement the required initializer. Even though I have to implement it, I don’t want it to be used, so it is marked as unavailable.

Finally, this file is an example of the simplified coding style that I described in the introduction to this post. There’s not a separate configure() function in a separate extension, and I’m also allowing ‘magic values’ for the size values. The simplicity of this class means that when I also adopt a simple coding style, it is easy for a developer to glance at the class and know everything about it.

The Dismiss Button

The dismiss button is a simple UIButton subclass with an image, and otherwise follows the same pattern as the LogoImage described above:

The Pill Button

The pill button is not as simple as the previous two elements and requires a shift into the complex coding style with the use of extensions and constants. The first section covers the button’s initialization:

There is little that’s new here, except that the button is initialized with a title and then the button’s style is configured with the configure(withTitle:) Next is the button configuration extension:

 

The configure(withTitle:) function handles all styling of the button based on the values specified in the Constant enumeration.

Finally, because this is a custom button, we need to set the style of the disabled and highlighted states. This is accomplished via UIButton overrides and values in the Constant enumeration:

 

Building the Modal

The final modal is going to be composed of two classes. The SimpleMessageModalViewController  is a subclass of ModalViewController that is in turn a UIViewController subclass.

Since we may have different types of modals shown in the app (and assuming that the modals are all going to have the blurred background with a dismiss button), the ModalViewController‘s view will contain the blur and dismiss button and manage the status bar. SimpleMessageModalViewController will be a subclass of ModalViewController and contain only the unique content to be shown (i.e. the single large text message).

ModalViewController

ModalViewController declares the blurView and dismissButton:

Note how blurView and dismissButton are declared. First, they are declared as fileprivate (as opposed to private) so that they can be accessed by other extensions in the same file.

Second, the blurView is a computed constant: because it requires additional styling that does not make any reference to the view controller, it can be fully styled as it is declared. This approach is different from that demonstrated in the introductory IBFree Post where all styling was done in a configure function. This is another coding style choice, and it can be very useful to have the style of each control be self-contained: either by using a block in the declaration (as in the case of blurView), or by having all the style be contained in the class itself (as in the case of dismissButton).

Finally, in viewDidLoad(), the controller configures its actions and layout through separate functions. Having a separate extension for a view’s actions can help you quickly narrow your search for bugs that are related to user actions. It is also helpful to have a separate configureActions function in this extension so you can immediately see how all the actions have been wired up:

The @objc in front of dismissButtonTapped() allows us to place this selector in a private extension (private methods are not exposed to Objective-C, and selectors must have this exposure for use with UIControl‘s target/action mechanism).

ModalViewController also needs to manage its presentation style in order to control the blur and manage the appearance of the status bar:

If the modalPresentationStyle is not set to .overFullScreen or .overCurrentContext, then the screen that the modal is presented over will not be preserved upon presentation and the blur effect won’t work. Additionally, since I can’t override a var and make it read-only, I make sure to note that setting modalPresentationStyle to anything else is going to be ignored, and log a warning if another developer tries to change the value.

In order to control the style of the status bar, the view controller needs to capture the status bar appearance, so modalPresentationCapturesStatusBarAppearance is set to true (with a similar description of how any attempt to change this value will be ignored and logging an error if another developer tries to change it).

Note the comments for the modalPresentationCapturesStatusBarAppearance stating that the implementation here is not consistent with Apple’s documentation. If you ever implement code that is contrary to Apple’s documentation, you should first 1) note that you are doing so and reference the documentation, 2) file a bug with Apple’s Bug Reporter, 3) also file the same bug with open radar so that it can be publicly tracked.

Finally, ModalViewController‘s View Configuration extension lays out the blur view and dismiss button:

Note how the naming of the margin constants is set up so that they can be more easily alphabetically organized and so that in a longer list, all of the margin values for each control would be in the same place.

Finally, when you are specifying left or right anchor points, you should prefer to use ‘trailing’ and ‘leading’ respectively unless there is an explicit reason not to do so. This is so that your layout will be more likely to appropriately adapt to languages that are not left-to-right (LTR).

The Simple Message Modal View Controller

Given our previous setup, SimpleMessageModalViewController is straightforward:

In configureView(), the message label’s horizontal anchors are set to the view’s anchors with a small amount of padding. This ensures that the text in the label will wrap if needed.

The Main View Controller

At this point, building MainViewController is also straightforward:

Up Next: Navigation & Navigation Controllers

There are many different ways that this could have been architected (and I would love to hear alternatives in the comments), but hopefully this gives a sense of how a set of screens can be approached without using IB while also trying to maximize compartmentalization, reuse, and readability.

You can find the completed project here, as well as any other IBFree related posts that we will be making in the future. Over the next several posts, I plan to cover using navigation controllers, routing, and integrating data with view controllers. Please let me know if there are other IBFree topics that you would like to see covered.

 

 

 

3 thoughts on “IB Free in Practice 1: Managing a Modal”

  1. I really enjoyed the article and emphasis on clean code—I’m eager to incorporate component based UI in my next feature.

    I am curious as why you’re using enums with static let constants and that are void of enumeration cases. I think a struct would be more befitting data structure.

    Also what is the advantage of putting the configuration of the UI components in a private extension? Wouldn’t it be more appropriate to group it next to the initializer for better readability?

    • Thanks for the feedback! Regarding the use of enum rather than struct, while I agree it is confusing because it is not actually enumerating something. However, it is something that is meant to 1) list things and 2) not be instantiated itself (unlike a struct, which can be initialized). In my previous IB Free post, I was using struct for constants, so I think its a close call between using either.

      Regarding putting the configuration of UI Components in a private extension, it is again another close-call architectural decision. For the most part, a lot of the layout of a view shouldn’t be changing that often or be the source of bugs, so I prefer having the layout code put towards the bottom of a class and in between would be data logic, actions etc.

  2. Really nice post,

    I personally code without storyboards. You got a lot of flexibility with that practice.

    But one thing I especially don’t like and I see often is putting your Views inside your ViewControllers, I think it does not respect the Single Responsibility principle and make the VC really long.

    Generally, I do create a class for my views, for instance SimpleMessageModalView and put all my view related code (subviews, layout, etc…) there, then I reference it in loadView of my SimpleMessageModalViewController. That way you don’t end-up with several views in your VCs and you can use RxSwift or another communication pattern for communication between your views and VCs for event handling.

    That was my two cents 😉

Leave a Comment