Use Your Loaf

[[brain engage] write]

Modules and Precompiled Headers

Apple announced Modules in WWDC 2013 so this post may be a little over due. What prompted me was noticing that Xcode no longer adds a Prefix.pch file to new projects by default which has a lot to do with modules.

#Import versus #Include

To understand why modules are useful it is worth first recapping the traditional way of working with frameworks and libraries. As I am sure everybody reading this knows it involves including (via a #include) the framework or library header in your source file and then at build time linking with the library.

The #import directive used by Objective-C is almost identical to the classic #include mechanism it inherited from the C programming language. The only difference is that it makes sure the same file is never included more than once. This is a small convenience to avoid the common C technique of including a header guard for the preprocessor:

/* someframework.h */
#ifndef _SOMEFRAMEWORK
#define _SOMEFRAMEWORK
/* body of someframework.h */
#endif

Unfortunately Objective-C did nothing to address other problems with the #include preprocessor mechanism. Having the preprocessor parse each header file in each source file quickly becomes slow and inefficient as the project grows as many source files include the same header files. The simple mechanism of text inclusion also leads to fragility as names from different headers collide.

Precompiled Headers

Precompiled headers are a partial solution to the problem of slow build times. They speed up the time it takes to compile a project when all or nearly all source files need to include some common headers. For example, an iOS project is likely to include <UIKit/UIKit.h> in most if not all source files. This means parsing and compiling the UIKit.h header many times when building the project which is wasteful and slow. A precompiled header file, as the name suggests, collects the common headers into a separate file. Precompiling this file just once and then automatically including it in all source files in the project significantly speeds up the build process for many projects.

Here is a typical Prefix.pch file from an old project that imports UIKit and Foundation headers as well as checking for at least iOS 5:

#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
#endif

The ability to have something globally included by placing it in the pch file is a useful but easily abused facility. Maintaining and optimising the prefix header file also puts a lot of the work on the developer that the tools should be handling for us. For another view on the ways developers can abuse prefix headers see 4 Ways Precompiled Headers Cripple Your Code.

Modules

A module offers a better way to work with system frameworks and libraries by replacing the preprocessor text inclusion mechanism with what Clang refers to as a semantic import. To import a module you use the @import declaration instead of the #include or #import preprocessor directives (note the semicolon):

@import UIKit;

When the compiler sees a module import it loads a standalone precompiled version of the framework. It also automatically takes care of linking to the module meaning you no longer need to manually add the framework in Xcode. Since the module is compiled just once there is no longer any advantage to including the framework header in prefix.pch. As a result the Precompile Prefix Header build setting now defaults to NO in Xcode.

Using Modules

Opting into using modules is as easy as setting the Apple LLVM 6.0 - Language - Modules Xcode build settings:

Both the Enable Modules and Link Frameworks Automatically settings default to YES in new Xcode projects. In fact once modules are enabled any #import or #include directives are automatically converted to @import declarations. This means you can adopt modules without having to make source code changes. For example a header file that has old style preprocessor imports for UIKit and Core Data frameworks:

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

With modules enabled this is automatically mapped to import declarations:

@import UIKit;
@import CoreData;

As a result of this automated conversion and the new Xcode defaults you may even be using modules without realising it if you have recently created a new project. One limitation of modules is that they are not available for user frameworks but the Apple system frameworks have been available as modules since iOS 7 (and OS X 10.9).

Adding a Pre-Compiled Header to a Project

Now that modules are available there is no need to continue to list system frameworks in a precompiled prefix header. If you need to add a prefix header to an Xcode project you can still do that (at least at time of writing with Xcode 6.1) by manually modifying the Precompile Prefix Header flag to YES and specifying the path in Prefix Header:

Further Reading

Some links for further reading:

Continuous Integration With Xcode Server

Xcode Server was introduced with Xcode 5 to provide a way to perform continuous integration through the creation of bots that automate the building, analysing, testing and archiving of projects. The bots can be created in Xcode on a development Mac and then run on the server. Since this requires OS X server it is a great way to use a spare Mac you have gathering dust.

This post collects together my notes on installing and setting up the Xcode 6 service on a Yosemite OS X Server. My simple objective was to have my unit tests run each time I make a commit to a project hosted in a GitHub repository.

Installing OS X Server

To install Xcode Server you first need a Mac running OS X Server. There is nothing to stop you from turning your development Mac into a server and using it to run Xcode Server. However it is also a great way to use an old Mac as long as it is capable of running Yosemite.

If you are a member of the iOS developer program you probably know that you are entitled to use Mac OS X Server free of charge. To redeem your copy you need to login to the iOS Dev Center, find OS X Server in the downloads section and request a download code:

OS X Redeem Code

At the time of writing there are two versions of OS X Server available depending on whether you use OS X Mavericks or Yosemite. For the rest of this post I am assuming OS X Server 4.0 running on Yosemite.

If you are already on the Mac where you want to install the server you can just click on the redeem code in the iOS Dev Center to directly launch the Mac App Store, redeem the code and start the download. If you need to enter the code manually the Redeem link is listed under Quick Links in the Mac App Store. The Server application downloads to the Applications folder and needs to be run to complete the setup.

Server Icon

Click the continue button, enter your password for admin access and let the install complete. Once the Server app is displayed you should the list of services that you can now run on the server in the sidebar (none of which are currently enabled).

Services

Remotely Managing A Server (Optional)

If you have multiple Mac’s and want to administer the server remotely you can download the Server app from the Mac App Store to another machine. To allow remote management you need to check the server settings and ensure that the “Allow remote administration using Server” is enabled:

Server settings

Now when you run the Server app on the remote Mac select “Other Mac” and enter the host name or IP Address of the server together with the Administrator name and password to connect.

Server setup

Since the server is running with a self-signed certificate that was generated during the installation you will most likely get a warning. You can either just accept the certificate this time by clicking “Continue” or select “Show Certificate” to modify the trust. You can choose to always trust “com.apple.servermgrd” or only this certificate:

Verify Certificate

Turning On Caching Service (Optional)

One other tip you can take advantage of is to turn on the Caching service. This speeds up software downloads and updates by caching Mac and iOS system updates and App Store downloads on the server. To enable click on the Caching service in the Server app and switch it on. You can optionally select the disk volume and cache size. Once the service is running any clients (Mac or iOS) on the same local network will automatically use the caching service.

Server caching

Configuring the Xcode Service

With OS X Server installed and running we can turn our attention to the Xcode Service. If you have not already done so you should download and install Xcode 6 on the server from the Mac App Store (assuming you are developing with the released version of Xcode). If you have just installed Xcode on the server you will need to run it before configuring the Xcode service to accept the software license agreement and let it finish installing components.

Click on the Xcode service in the sidebar of the Server app and then use the “Choose Xcode” button to find the version of Xcode you want to use (for example: /Applications/Xcode):

Once the setup finishes you will need to slide the switch to start the service:

We now have a running Xcode service but before we look at using it from a project there are a few extra server configuration steps:

  • To allow Xcode Server to access provisioning profiles you need to add a developer team from the Apple Developer program. Use the Add… button and enter your Apple Developer credentials to confirm you want to add this server to your team.

  • Any test devices you attach to the server should show up in the devices list. If this is the first time you have used the device for development click the “Use for Development” button and then “Add to team” to add it to the Apple developer team. If the device shows as not supported check you have removed any passcode lock.

  • You can also configure Xcode server to host code repositories on the server but since I am going to be using a remote repository hosted on GitHub I will skip this step.

Configure Xcode Development Environment

The following steps are performed on the development machine. I am assuming you already have a project with unit tests.

Add Server Account

On your development Mac add a server account to Xcode so that you can check the status of the remote bots from Xcode. In the Xcode preferences Account tab use the Add button and add a server. The server should show up in the list, enter your user name and password to connect to the server.

Share The Scheme

The Xcode project scheme defines the settings to use when a project is built, run, tested, profiled, analysed or archived. To allow these settings to be accessed by the Xcode server they need to be checked into the source code repository which requires that they be shared. In Xcode on the development machine use Product > Scheme > Manage Schemes to view the available schemes. In my case I have only a single scheme to mark as shared:

This creates the xcshareddata directory containing the scheme configuration that I can then check into the Git repository for the project.

Add Remote Repository to Xcode Accounts

To allow the Xcode Server to access the remote code repository you first need to add it to the Xcode Account settings on your development Mac. In the Xcode preferences Account tab use the Add button to add the repository. In my case I want to access a repository on GitHub using SSH public/private keys so I will use the SSH URL for the repository as follows:

[email protected]:kharrison/UYLPasswordManager.git

Note that there is no way, that I can see, to enter the SSH keys for authentication at this point.

Setting Up SSH Keys For Repository Access

To allow the Xcode server to access the GitHub repository it needs to know the SSH keys to use. GitHub has detailed instructions on generating SSH keys that you can follow if you need to create keys. In brief to create a new pair of keys from the command-line:

$ ssh-keygen -t rsa -C “[email protected]

You will prompted for a file name and then a passphrase. By default this will create two files in the .ssh subdirectory of your home directory named id_rsa and id_rsa.pub but you can use some other value when prompted. You need to copy the contents of the id_rsa.pub file to the SSH keys section of your GitHub account. If in doubt refer to the detailed GitHub instructions.

A quick tip that I always forget: The pbcopy command is a quick way to copy the contents of a file to the clipboard making it easy to paste into a window:

$ pbcopy < ~/.ssh/id_rsa.pub

Create a Bot

To create a bot to run our unit tests, use Product > Create Bot and check the scheme and server name are correct:

At this point the Xcode Server will check if it can access the source code repository which in my case will fail as I have not given Xcode any credentials:

Use the Change button and select Existing SSH Keys for the type and paste in the public (id_rsa.pub) and private (id_rsa) keys into the correct fields

Clicking Next will cause Xcode to validate the credentials and if all went well you should see the options to schedule and configure the bot:

Configure the Bot

A bot can be configured to run hourly, daily, weekly or when there is a new commit to the repository. When run the bot can perform static analysis, testing and product archiving actions. You can also specify if and when the bot should clean the build products and intermediates. To fit my requirements to run the tests on each commit I selected the following options:

  • Schedule: On Commit - the bot will check for new commits every 5 minutes
  • Actions: Perform analyze action, Perform test action
  • Cleaning: Once a day

I don’t archive every build and I only clean the build products once a day to keep the builds fast. Since you can have multiple bots you could also for example schedule a bot to run once a day that did a clean build and archive.

Next you can specify which devices and simulators should run the tests. Any devices you want to specify must be connected to the server. If you select Specific Devices and Simulators you are presented with a list of attached devices and the long list of simulators for each version of the iOS SDK.

Finally you can add an email notification or shell script trigger before and after a bot runs if you need some custom actions. By default an email notification is generated when a test fails or a build has errors:

With the bot configuration complete, use the Create button.

Monitoring Bots

There are several ways to view and manage a bot:

Report Navigator

You can manage the bots you have created from the Report Navigator in Xcode on your development Mac:

Right-clicking the bot in the navigator pane allows you to edit or delete the bot or run it immediately. Selecting the bot will show a summary of the bot integrations in the editor area of Xcode. Clicking on the entry in the navigator pane for a specific integration run will show detailed results:

Web Browser

Each bot is also accessible via a web browser at the URL: <hostname>/xcode/ where <hostname> is the address of the OS X Server. This view is less useful than the Xcode reporting interface but it does provide a quick summary of the most recent integrations. You can also switch to the big screen mode either by clicking the button on the bots homepage or using the URL: <hostname>/xcode/bigscreen.

Closing Summary

If you have never used a continuous integration server to run your tests you should give Xcode Server a try. It is a little bit of work if you are not already running OS X Server but for the most part it is simple to get started. Alternatives such as Travis CI are free for open source projects but will cost you money for private repositories so it is worth trying Xcode first to see if it will meet your needs.

UIAlertController Changes in iOS 8

As part of the theme of iOS 8 to make interfaces adaptive there are some major changes to the presentation of view controllers. The new UIPresentationController does a lot of the hard work of animating view controller transitions and adapting to device size changes such as rotation. It also brings some big changes to some old UIKit favourites such as alert views, action sheets, popovers and search bar controllers. This post will be a gentle introduction to this new world by looking at the changes to alert views and action sheets.

UIAlertView - Alerting the Old Way

The last time I wrote about alert views was back in 2011 to describe the UIAlertView changes in iOS 5. The release of iOS 5 brought alert view styles but not much else has changed since then. The code snippet below is all it takes to setup and present an alert view with cancel and OK buttons:

UIAlertView *alertView = [[UIAlertView alloc]
                           initWithTitle:@"DefaultStyle" 
                           message:@"the default alert view style"
                           delegate:self 
                           cancelButtonTitle:@"Cancel" 
                           otherButtonTitles:@"OK", nil];

[alertView show];

The introduction of alert view styles in iOS 5 added a limited ability to create custom alerts by setting the alertViewStyle property. This extended the plain default button-only style to allow plain text input, secure text input or even a login and password input alert:

Basic Two Button Alert Text Input Alert Secure Text Input Alert Login and Password Alert

The UIAlertViewDelegate protocol has callback methods for the button actions and also a method (alertViewShouldEnableOtherButton:) called when a text field changes to allow buttons to be dynamically enabled/disabled.

UIAlertController - Adaptive Alerting

In the new adaptive world of iOS 8 the UIAlertController is a functionally identical, block-based replacement for both UIAlertView and UIActionSheet. Switching between an alert or action sheet is done by setting the preferred style when creating the controller.

A Simple Alert

It is interesting to compare the code required to setup a new style alert to the old UIAlertView. The creation of the basic UIAlertController is very similar to creating an UIAlertView (the alertTitle and alertMessage are both NSString’s):

UIAlertController *alertController = [UIAlertController
                              alertControllerWithTitle:alertTitle
                              message:alertMessage
                              preferredStyle:UIAlertControllerStyleAlert];

There is no delegate, nor do we initially specify the buttons. Note the third parameter which chooses between the alert and action sheet styles.

You add action buttons by creating an instance of UIAlertAction which you then add to the controller. The UIAlertAction consists of a title string, style and a block to execute when the user selects the action. The three possible choices for the UIAlertActionStyle cover default, cancel and destructive actions. To reproduce the classic cancel/ok action sheet we just need to create and add the two alert actions:

UIAlertAction *cancelAction = [UIAlertAction 
            actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel action")
                      style:UIAlertActionStyleCancel
                    handler:^(UIAlertAction *action)
                    {
                      NSLog(@"Cancel action");
                    }];

UIAlertAction *okAction = [UIAlertAction 
            actionWithTitle:NSLocalizedString(@"OK", @"OK action")
                      style:UIAlertActionStyleDefault
                    handler:^(UIAlertAction *action)
                    {
                      NSLog(@"OK action");
                    }];

[alertController addAction:cancelAction];
[alertController addAction:okAction];

Finally we can present the alert view controller as with any other view controller:

[self presentViewController:alertController animated:YES completion:nil];

The display order for the buttons depends on the order they are added to the alert controller. If you follow the iOS Human Interface Guidelines you should make the default action the right button and the cancel button the left button for a two button alert. You can only have one cancel action, if you add a second you will get a runtime exception:

*** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘UIAlertController can only have one action with a style of UIAlertActionStyleCancel’

Destructive actions

Here is a quick example of the third alert action style for destructive actions. The code is the same as before except that we add a “reset” button instead of the “ok” button:

UIAlertAction *resetAction = [UIAlertAction
             actionWithTitle:NSLocalizedString(@"Reset", @"Reset action")
                       style:UIAlertActionStyleDestructive
                     handler:^(UIAlertAction *action)
                     {
                       NSLog(@"Reset action");
                     }];

[alertController addAction:resetAction];
[alertController addAction:cancelAction];

[self presentViewController:alertController animated:YES completion:nil];

Note that this time the destructive action is added first to make it appear on the left.

Text Input Alerts

The greater flexibility of the UIAlertController means that you no longer need to be constrained by the built-in styles for plain text, secure text or login and password input alert views. We can add an arbitrary number of UITextField objects to the alert and use all of the standard UITextField configuration options. When you add the text field to the alert controller you specify a block that is used to configure the text field.

For example, to recreate the old login and password style alert we can add two text fields and configure them with the appropriate placeholder text and set the password field to use secure text entry:

UIAlertController *alertController = [UIAlertController
                    alertControllerWithTitle:alertTitle
                                     message:alertMessage
                              preferredStyle:UIAlertControllerStyleAlert];

[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
 {
   textField.placeholder = NSLocalizedString(@"LoginPlaceholder", @"Login");
 }];

[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
 {
   textField.placeholder = NSLocalizedString(@"PasswordPlaceholder", @"Password");
   textField.secureTextEntry = YES;
 }];

The values of the text field can be retrieved in the OK action handler:

UIAlertAction *okAction = [UIAlertAction
  actionWithTitle:NSLocalizedString(@"OK", @"OK action")
  style:UIAlertActionStyleDefault
  handler:^(UIAlertAction *action)
  {
    UITextField *login = alertController.textFields.firstObject;
    UITextField *password = alertController.textFields.lastObject;
    ...
  }];

Things get a little more complicated if we want to reproduce the behaviour of the old UIAlertView delegate method alertViewShouldEnableOtherButton:. Assume we only want to enable the OK button if the user has entered at least 3 characters in the login field. There is no equivalent delegate method for UIAlertController so we need to add an observer to the login text field. We can do that with the following code snippet in the configuration block:

[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
 {
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self
                            selector:@selector(alertTextFieldDidChange:)
                                name:UITextFieldTextDidChangeNotification
                              object:textField];
 }];

We need to remove the observer when the view controller is dismissed by adding the appropriate code to the handler block for each of the actions (and anywhere else we may dismiss the alert controller). For example in the okAction block we saw earlier:

UIAlertAction *okAction = [UIAlertAction 
  actionWithTitle:NSLocalizedString(@"OK", @"OK action")
  style:UIAlertActionStyleDefault
  handler:^(UIAlertAction *action)
  {
    ...
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                 name:UITextFieldTextDidChangeNotification
                               object:nil];
  }];

Before presenting the alert controller we can disable the OK action:

okAction.enabled = NO;

Then in the notification observer we can check the login text field for content before changing the state back to enabled:

- (void)alertTextFieldDidChange:(NSNotification *)notification
{
  UIAlertController *alertController = (UIAlertController *)self.presentedViewController;
  if (alertController)
  {
    UITextField *login = alertController.textFields.firstObject;
    UIAlertAction *okAction = alertController.actions.lastObject;
    okAction.enabled = login.text.length > 2;
  }
}

The alert view is now presented with the OK button disabled unless there are at least three characters in the login text field:

Action Sheet

The action sheet is used when you need to present the user with a set of choices. Unlike the alert view which is always presented as a modal view the presentation of the action sheet depends on the device size. On an iPhone (compact width) the action sheet rises from the bottom of the screen. On an iPad (regular width) an action sheet is always shown in a popover.

The creation of an action sheet is almost identical to an alert, the only difference being the style:

UIAlertController *alertController = [UIAlertController
               alertControllerWithTitle:alertTitle
                                message:alertMessage
                         preferredStyle:UIAlertControllerStyleActionSheet];

You add actions the same way as you do for alerts so I will abbreviate the code to add three actions:

UIAlertAction *cancelAction = ...;  // UIAlertActionStyleCancel
UIAlertAction *deleteAction = ...;  // UIAlertActionStyleDestructive
UIAlertAction *archiveAction = ...; // UIAlertActionStyleDefault

[alertController addAction:cancelAction];
[alertController addAction:deleteAction];
[alertController addAction:archiveAction];

You cannot add text fields to action sheets, if you try it you will get a runtime exception:

*** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Text fields can only be added to an alert controller of style UIAlertControllerStyleAlert’

If we do nothing more and present this on an iPhone/compact width device it works as expected:

[self presentViewController:alertController animated:YES completion:nil];

The cancel button, if present, is always shown as the bottom of the view regardless of the order it was added to the alert controller. The other actions are shown top to bottom in the order they were added. The iOS Human Interface Guidelines recommend that any destructive action is shown first.

There is a problem with this code when used on an iPad or regular width device it creates a runtime exception:

Terminating app due to uncaught exception ‘NSGenericException’, reason: ‘UIPopoverPresentationController (<_UIAlertControllerActionSheetRegularPresentationController: 0x7fc619588110>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.’

At the time of writing the Apple UICatalog sample code crashes for the same reason when run on an iPad.

As I mentioned before for regular width presentations the action sheet is displayed in a popover. A popover always requires an anchor point which can be a source view or a bar button item. In this case I am using a standard UIButton to trigger the action sheet so I will use it as the anchor point.

A big difference in iOS 8 is that we no longer need to write code to test for the interface idiom. The UIAlertController takes care of adapting to the display environment so we can simply ask it for a popover controller. On an iPhone/compact width device this returns nil. The extra code we need to configure the popover is below:

UIPopoverPresentationController *popover = alertController.popoverPresentationController;
if (popover)
{
    popover.sourceView = sender;
    popover.sourceRect = sender.bounds;
    popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
}

The UIPopoverPresentationController class is also new in iOS 8 and replaces UIPopoverController and for our purposes is functionally equivalent. The action sheet now displays as a popover anchored to the source button:

Note that the UIAlertController is also smart enough to remove the cancel button when using a popover. A user cancels a popover by touching outside of the popover so it is not required.

Dismissing Alert Controllers

Typically the alert controller is dismissed automatically when the user selects an action. It can also be dismissed programmatically, if required, like any other view controller. One common reason can be to remove the alert or action sheet when the app moves to the background. Assuming we are listening for the UIApplicationDidEnterBackgroundNotification notification we can dismiss any presented view controller in the observer (see the example code for the setup of the observer in viewDidLoad):

- (void)didEnterBackground:(NSNotification *)notification
{
  [[NSNotificationCenter defaultCenter] removeObserver:self
                         name:UITextFieldTextDidChangeNotification
                        object:nil];
  [self.presentedViewController dismissViewControllerAnimated:NO completion:nil];
}

Note that to be safe we also make sure to remove any text field observers we may have added to the alert controller.

In Summary

A long post but hopefully it will be another three years before I need to write about alert and action sheets again. The old UIAlertView and UIActionSheet classes still work fine in iOS 8 so if you need to target iOS 7 there is no rush to migrate immediately. The AlertController sample Xcode project for this post can be found in my GitHub CodeExamples repository.