iOS

UITableView Swipe Actions

Overview

One of the new but little discussed APIs in iOS 11 allows the addition of swipe actions on UITableView rows via the new UISwipeActionsConfiguration class and associated UITableViewDelegate methods. Adding swipe left or swipe right actions is now pretty simple, so lets just dive right in. To try out these new APIs, a very basic list view was created with some hard coded data. This app will allow the user to swipe right to make a row “read” or “unread” and swipe left to either “flag” a row, or “delete” the row from the list. The code for this post can be found here.

 

The UITableView delegate

The keys to adding swipe actions start with new UITableView delegate methods, defined as follows:

func tableView(UITableView,
              leadingSwipeActionsConfigurationForRowAt: IndexPath)
                                    -> UISwipeActionsConfiguration

func tableView(UITableView,
               trailingSwipeActionsConfigurationForRowAt: IndexPath)
                                    -> UISwipeActionsConfiguration

As you can see in the method signatures, these both return a UISwipeActionsConfiguration class. This class is initialized with an array of UIContextualAction classes. So let’s start at the bottom and work our way up.

 

The Details

For our sample app, we’ll create a struct called an Email. It looks like this:

struct Email {
    let subject: String
    let body: String
    var isNew: Bool
    var isFlagged: Bool

    static func mockData(numberOfItems count: Int) -> [Email] {
        var emails = [Email]()
        for idx in 1...count {
            let email = Email(subject: "Email \(idx)", body: "This is my body for email \(idx)", isNew: true, isFlagged: false)
            emails.append(email)
        }
        return emails
    }

    mutating func toggleReadFlag() -> Bool {
        self.isNew = !self.isNew
        //normally make some call to toggle and return success/fail
        return true
    }

    mutating func toggleFlaggedFlag() -> Bool {
        self.isFlagged = !self.isFlagged
        //normally make some call to toggle and return success/fail
        return true
    }
}

We will use this class to load up the table view with data. This is accomplished using the typical UITableViewDataSource methods and can be seen within the ViewController class. The real area of interest is within the UITableViewDelegate. Here we introduce the 2 new methods mentioned above.

 

Setting up the UISwipeActionsConfiguration

Let’s take a deeper dive into the trailing actions method, since it will apply more than one possible swipe action.

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let deleteAction = self.contextualDeleteAction(forRowAtIndexPath: indexPath)
        let flagAction = self.contextualToggleFlagAction(forRowAtIndexPath: indexPath)
        let swipeConfig = UISwipeActionsConfiguration(actions: [deleteAction, flagAction])
        return swipeConfig
    }

Here, we are using a helper method to instantiate each of the UIContextualAction classes we will need to build our swipe actions. Then we can instantiate a UISwipeActionsConfiguration using the UIContextualAction classes to populate the actions array. The UISwipeActionsConfiguration is then returned from the delegate method.

The important thing to note here, is that the order of the items in the action array determines the order of the actions displayed when the table view row is swiped. The easiest way to remember is that the first item displays furtherest out, and subsequent items display moving towards the cell. So if we are dealing with trailing actions, the first item in the array corresponds to the rightmost action (“Delete”), the second one will be one inside of that (“Flag”), and so on.

 

Handling Contextual Actions

Next, lets take a look at the UIContextualAction. This is where the work happens. The documentation found here. The initializer takes a UIContextualAction.Style, title, and UIContextualActionHandler block. The UIContextualActionHandler gives you access to the UIContextualAction, the view that displayed the action, and a completion handler that you pass a bool indicating whether or not the action was successful. An example can be found in the contextualToggleFlagAction method shown below:

func contextualToggleFlagAction(forRowAtIndexPath indexPath: IndexPath) -> UIContextualAction {
        // 1
        var email = data[indexPath.row]
        // 2
        let action = UIContextualAction(style: .normal,
                                        title: "Flag") { (contextAction: UIContextualAction, sourceView: UIView, completionHandler: (Bool) -> Void) in
            // 3
            if email.toggleFlaggedFlag() {
                // 4
                self.data[indexPath.row] = email
                self.tableView.reloadRows(at: [indexPath], with: .none)
                // 5
                completionHandler(true)
            } else {
                // 6
                completionHandler(false)
            }
        }
        // 7
        action.image = UIImage(named: "flag")
        action.backgroundColor = email.isFlagged ? UIColor.gray : UIColor.orange
        return action
    }

In this method:

  1. We are simply retrieving the data element from our data array
  2. Instantiating the UIContextualAction class, providing a style, title and handler
  3. Mutating the object. Of course our email struct could have been a class to ease the mutation handling.
  4. Saving the mutated object back to our data and reloading the appropriate rows.
  5. Call the provided completion handler, indicating success.
  6. Had the update to the object failed, we can indicate that to the completion handler by passing false.
  7. Setting the image and color attributes on action. By specifying an image, the title in the initializer is not displayed. This certainly makes localization a bit more work, since the images would need to contain any text we wanted to display for an action, assuming we wanted both text and images.

One more thing of note is that on the UISwipeActionsConfiguration class, you can specify whether the first action in the collection should be performed with a full swipe. This defaults to true. Here is a video of the working app with both leading and trailing swipe actions.

Changing your iOS App Icon programatically

When iOS 10.3 was released, Apple opened up an API to allow developers to change the app icon for their app. Of course this doesn’t mean you can change it every second like the Clock app, or even every day like the Calendar app. What you can do is change the app icon when the app is running in the foreground, and it will notify the user with a pretty ugly system alert dialog that the icon is changing. This post will walk through the configuration and API calls to allow an app to change the app icon.

The first step is to obtain your app icon image assets. These must be .png files and you will need 2x and 3x resolutions. An important thing to consider is that you want all the proper icon sizes available so that when the icon changes it is consistent across settings, spotlight, notifications, etc.

Unfortunately, these cannot be placed in a xcassets file, however, they must be packaged in the main bundle. The source code for this post can be found here.

The next step is to configure the Info.plist file. Review the documentation for the plist file carefully. In the provided code, the plist file contains the CFBundleIcons dictionary configured as show below:

Screen Shot 2017-07-13 at 9.19.42 AM

In the CFBundleAlternateIcons dictionary, the key represents the string that will be used when making the API call to change the app icon.  You will see these used in the code samples later in this post.  The value is an array of CFBundleIconFiles.  In our case, its an array of one item, with the relative path to the filename (without extension and size modifier).  If you like to keep your files organized in both Xcode and the filesystem like me, you’ll want to store your icons in a sub folder within your project, so it’s important to specify the relative path, otherwise you’ll get a runtime error when trying to change icons.

Now on to the code.  The API docs can be found here: https://developer.apple.com/documentation/uikit/uiapplication/2806818-setalternateiconname.  The APIs are pretty basic.  Ideally you want to check if the app can change the app icon with something like the following:

if UIApplication.shared.supportsAlternateIcons {
    print("I can do it")
}

Then to change the icon, do something like this:

//note that the name corresponds to the key in the 
//  Info.plist CFBundleAlternateIcons dictionary
UIApplication.shared.setAlternateIconName("hockey") { (error: Error?) in
    guard let error = error else {
        print("success")
        return
    }
    print("switching icon to hockey encountered and error: \(error)")
}

Finally, to reset the icon back to the primary one by setting the name to nil:

UIApplication.shared.setAlternateIconName(nil) { (error: Error?) in
    guard let error = error else {
        print("success")
        return
    }
    print("switching icon to primary encountered and error: \(error)")
}

Simple as that. Check out the sample app and switch icons between your favorite Philadelphia sports teams 😃

Using Node.js and Charles Proxy to Mock a Server API

While working on an iPhone project for a client, I came across a situation where I needed to test some error conditions returned from a server API which I have no control over.  I ended up using Charles Proxy to remote map the server API calls to a locally running Node.js server.  Of course the proper way to do this is to write functional tests and mock the networking classes to return the appropriate responses, but that is a blog post for another day.

In this post, I’ll walk through the steps to create a simple iPhone app that interacts with a public API, using Node and Charles to simulate an error response.  This post assumes some experience writing iOS code, but all the source code will be available at https://github.com/stevenpsmith/charles-node-blog.

Create an iOS App

For the iOS app, I’ll write a simple weather app that uses the OpenWeatherMap API.  We all know the world needs yet another weather app :).

Let’s start by creating a new single view project in Xcode.  In my iOS projects I almost always use CocoaPods for dependency management.  CocoaPods makes it easy to add/remove 3rd party libraries to your iOS project.  In this app, we’ll use AFNetworking to access the API, and I’ll add MBProgressHUD in case the response time is slow and the user needs to wait for UI updates. The following assumes that you already have CocoaPods installed.

After creating the Xcode project, quit Xcode (or close the project) and create a text file named “Podfile” at the root of the project.  The contents should include the following:

platform :ios, '7.0'

pod 'AFNetworking', '~> 2.0.3'
pod 'MBProgressHUD', '~> 0.8'

With that file in place, drop to a command line and execute pod install

That will create a workspace and configure the libraries. From this point forward, we use the workspace instead of the project. The final version of the application will look like this:

Screenshot_1_14_14__12_03_PM

The code base can be found at https://github.com/stevenpsmith/charles-node-blog, but the key portion is the API call, which is below (without all the details)

[[CSMServerAPI sharedInstance] weatherForLongitude:-75.1914 
                                       andLatitude:40.1386 
    successBlock:^(NSDictionary *weatherDict) {
        // code to map API response to UI elements here
        //...
    } failureBlock:^(NSError *error) {
        //access error message parts from response here
        //....
        NSString *msg = ...
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Houston - We have a Problem" 
                                                        message:msg 
                                                       delegate:nil 
                                              cancelButtonTitle:@"OK" 
                                              otherButtonTitles:nil];
        [alert show];
    }];

If everything is successful, the UI gets populated with the weather data. But, if there is a failure, we throw an alert up to the user containing the error messages (I know, alerts are not user friendly, but this is an example).

Mocking an error with Node

Now that we have the API calls in place, we need to set up our node server to fake an error response. Sticking with the theme of keeping things simple, I used the express framework for node. With node installed, I created a directory for my server app and added a ‘package.json’ file inside. The ‘package.json’ looks like the following:

{
  "name": "mock-api",
  "description": "for use with charles proxy to mock error conditions for OpenWeatherMap API",
  "version": "0.0.1",
  "dependencies": {
    "express": "~3.4.7"
  }
}

Dropping to a command line and executing npm install will set up the node server with the express module. Now we create an ‘app.js’ file and add our mock API, as follows

var express = require('express');
var app = express();

var errorJson = {error:"API Error",error_msg:"You have encountered an error thanks to Charles and Node....have a nice day!"};

app.get('/weather', function(req, res) {
  	res.json(400, errorJson);
});

app.listen(process.env.PORT || 4730);

Since our call to the OpenWeatherMap API is a get request to the /weather URI, we simply handle that request route in our code and return the response we want, which is an error containing a JSON error message. To start the server, execute node app.js at the command line and the server will be listening on port 4730.

Configure Charles Proxy for Remote Mapping

If you code against server APIs and don’t use Charles Proxy, or something like it, you really should start. It is immensely valuable for troubleshooting API calls, and the more I use it, the more nice features I find. I’ve used it for bandwidth throttling, request/response inspection, breakpoints (to change requests or responses), and now URL mapping. In order to map the OpenWeatherMap API to our local node server, access the Map Remote settings via the Tools -> Map Remote... menu. Once there, add a new remote mapping, filling out the fields as shown below. The configuration was also exported and is available at https://github.com/stevenpsmith/charles-node-blog.

Edit_Mapping_and_Map_Remote_Settings_and_Charles_3_8_3_-_Session_1

With that in place, enable remote mapping, run the iPhone app and get the following error:

Screenshot_1_14_14__11_51_AM

If you used Charles to record your network requests, you can even see the mapping occurred in the request overview notes. That’s all there is too it. You can certainly do a lot more than just simulate errors with this set up. I’ve used it to simulate server application errors as well, where server returns an http 200, but the response includes some application specific errors. Timeouts can be tested as well using the connect-timeout module. There is an example in the source code for that too.

So this is one way to intercept your API calls and modify them for testing. This is certainly no replacement for good functional tests, but simply another thing to add to your developer toolbox.

Using Custom File Types to import data into your iOS Apps

Disclaimer: This is a repost of my entry on the Chariot Solutions blog.

Introduction

I recently came across a situation where I needed the ability to share data between users of the same iOS application. Unfortunately, these users can not share an iCloud account and there is no server side components to this application. It is a standalone app.  Services like DropBox were not really an option either, as the devices are owned by an enterprise and will not have any service like that deployed onto them.

One solution is importing data via custom file types. Files can be sent to a device numerous ways (e.g. email, drop box, etc.), but for this post we will assume email. iOS makes it very simple to define a custom file type that your app can handle. This way, when the device receives an email with that type as an attachment, the user can select our app, giving it access to the underlying file. This post will walk through creating a simple app to access the contents of a text file received via email.

Create the Project

Lets start by creating a new project in XCode. I created a single view iPad application and called it “CustomUTIHandler”. I always use ARC. You can choose to use storyboards or not, as the UI in this app will do nothing more than display the contents of our file. The resulting project for this post can be found at:https://github.com/stevenpsmith/CustomFileHandler.

Define the File Type

The next thing is to define a file type extension that our application will support. This is also known as a Uniform Type Identifier (UTI). For our purpose, we will use a filetype of ‘csm’ (for Chariot Solutions Mobile). The contents of this file will be some string. It could really be almost anything, but for simplicity we will use a string. For a list of Apple defined UTIs, look here: http://developer.apple.com/library/ios/#documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html#//apple_ref/doc/uid/TP40009259-SW1.

Once we know what our custom file type extension is, we can add support for it to the project by modifying the Info.plist file for the project. We are going to be the owner/exporter of the new file type, so we add the “Exported Type UTIs” key to the Info.plist file. The default definition that gets created looks like this:



Based on the bundle key definitions found here: https://developer.apple.com/library/mac/#documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html, we can fill in the required values. Since our example only has one file type that it is defining and the underlying data is text, we can add the system defined identifier for text to the “Conforms to UITs” item:

public.text

Next we need to add the identifier for our custom UTI. Apple recommends using the reverse DNS format in order to ensure our new UTI is unique. I will be using the following:

com.chariotsolutions.customUTIHandler.csm

Finally, we want to identify the file extension that our app will be handling. This is defined under the “Equivalent Types” node. Add a node under the equivalent types with a name of “public.filename-extension” and a value of “csm”. You can optionally add a “Description” element under the Item for the exported type. My Info.plist now looks like this:

Support our new File Type

In order for our app to be recognized by the OS as supporting the newly defined file type, we need to add a “Document types” declaration to our Info.plist file. Doing so gives us the following in our Info.plist file:

The possible keys for the CFBundleDocumentTypes property can be found here: http://developer.apple.com/library/ios/#documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-101685-TPXREF107. We can add anything for the “Document Type Name” value. The handler rank will affect the order that Launch Services uses to present apps that handle this file type. Since our app is really the owner of this file type, we will use that as the handler rank. Now we need to add the “Document Content Type UITs” that our app will handle. In this case, we are only handling one type (as defined in the export key above):

com.chariotsolutions.customUTIHandler.csm

Just for fun, we will also give our file type an image icon (included in the project). The specs for the image are defined in the keys document referenced above (64×64 in this case). So our final Info.plist looks like the following:

Now all we have to do is create a text file with a “.csm” extension and email it to our device. Once there, long press the resulting icon and we should see our app listed as one of the “Open in” apps.

Once we select our app, it will launch and pass the file URL to this app delegate method:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
So, adding a simple NSLog statement to the method and we can display the contents of our text file:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
   if (url){
      NSString *str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
      NSLog(@"The file contained: %@",str);
   }
   return YES;
}

Important Safety Tip

The files that are being opened by the application are actually copied into a directory called “Inbox” within the apps Documents directory.  My file was called “This is a test.csm” (great name right, what am I a windows user?).  As shown in the image below, there are copies for each time I imported it on my device.

These files should be cleaned up as required by your app.

Summary

So by adding some configuration to our apps Info.plist file and implementing a delegate method, we can handle any uniform types within our app.  The source code for this project can be found at https://github.com/stevenpsmith/CustomFileHandler.  I added some code so that it will display the contents of the file on the screen….very exciting.

Added Bonus

Since we are declaring to launch services that our app handles “csm” files, drop your custom file type into drop box.  Assuming you have the drop box app on you device, find the document in the app and try to open it.  You should get “Unable to view file”.  But if you tap the icon in the upper right of the screen, you should get a list of apps that can open that file….including our app.

It’s like magic 🙂  You see those other apps listed because we defined our UTI as text type, and those apps will handle text documents.

Using Cocoapods to Manage Private Dependencies

This is a re-post of my original blog entry, which can be found at: http://blog.chariotsolutions.com/2012/07/using-cocoapods-to-manage-private.html

Introduction

Cocoapods (http://cocoapods.org/) is a dependency management framework for XCode. It allows you to declaratively define project dependencies and have them included in the build of your project. It’s like Apache Maven or Ruby Gems for XCode. There are many tutorials on getting started with Cocoapods, so in this blog post I will present a simple case where we have a project that depends upon two internally developed projects or private libraries.
We will start by creating two projects, each with a simple UI screen and some 3rd party dependency. Each of these projects will contain a file known as a Podspec that defines what gets included when another project depends upon it. Next, we will create a third project that depends upon these internally developed libraries and see how everything gets tied together.
In my contrived example, I’ll create two projects, each with a dependency on AFNetworking (a nice network abstraction layer). AFNetworking is already available via the public cocoapods spec library. Cocoapods will be used to add this dependency. Each project will also use YQL to query a weather service (Yahoo versus Wunderground) and present the forecast for a given zip code. The master project, which I like to call the Weather Comparator, will pull in the Yahoo and Wunderground projects and use their code to present a comparison of the services.

The Private Libraries

We start by creating the first project, Yahoo Weather. Once we have Cocoapods installed on our system, we create an XCode project for our first framework. I started using the Empty iOS application template. Once the XCode project is created, exit XCode. Create a text file called “Podfile” at the root directory of the project. The contents of the Podfile looks like the following:

platform :ios, '5.1'

xcodeproj 'YahooWeather.xcodeproj'

pod 'AFNetworking'

The first line defines the platform and SDK version, the next line defines the name of the XCode project file, and subsequent lines define the dependencies for this project. Podfiles can be much more complex, but this post will keep things simple. Check out the Cocoapods site for more information. Once the Podfile is complete, drop to a terminal and execute pod install at the root of the project directory. You’ll see some messages about the dependencies being downloaded and configured and when the process is complete, there will be a message indicating NOT to use the YahooWeather.xcodeproj anymore, but use the YahooWeather.xcworkspace file now. This is a workspace that includes your project and the Pods project.

Under the Pods project, there is a Pods directory, which contains the libraries specified in the Podfile (AFNetworking in this case). Besides creating the workspace, Cocoapods configures your project to include a static library called libPods. This is the output of the Pods project build. Essentially it includes the compiled version of the pods for which you asked to be included in the project. We can now use the AFNetworking classes within our code. The source code for the YahooWeather project can be found on my github account: https://github.com/stevenpsmith/YahooWeatherService. The finished app can actually be run and looks like this (I know, gorgeous, right):

Following the same steps, we create a second project called WunderWeather. Much of the code is similar, except now we are using the Wunderground weather API. There are some other small changes, but for simplicity sake I reused much of the same code, just using different names for the classes (notice the class name prefix). Again, remember this is a contrived example, so reusing the code was merely for time efficiency. The source code for WunderWeather can be also be found on my github at https://github.com/stevenpsmith/WunderWeatherService. And for your viewing pleasure, here is what it looks like:

One important thing to note here, is that each of our frameworks uses a slightly different class prefix. This ensures that when both of these projects get used in a third project (the WeatherComparator), there are no name collisions. Wouldn’t namespaces be nice?
When we are done coding each of these projects, we can commit them to our “private” git repository. In this case I am using github public repositories, but these projects can reside anywhere that is accessible from the projects that will depend upon them. That could a private repo, file system, etc. Cocoapods also support SVN and Mercurial, but git is what all the cool kids are using, so that is what this example will use.

Creating the Podspec

The next step is to configure a podspec for each of our frameworks. The podspec defines the source files, resources, etc. that our libraries/frameworks expose for use by other projects. In our case, we need to expose all the classes in the API, Model, and ViewController directories within our projects. We also want to expose our XIB files. Those are resources which need to get compiled and added to our final “.app” file. In a similar way, resources might include images that will get added into the “.app” file during the final build process. To create a podspec, execute pod spec create projectname at the root of the project directory. In our case, it would be pod spec create YahooWeather or pod spec create WunderWeather
The file that gets created has comments and examples of all the properties that can be set. For our projects, the podpsec looks like this (with all the extraneous comments and unused attributes removed):

Pod::Spec.new do |s|
  s.name         = "YahooWeather"
  s.version      = "0.0.1"
  s.summary      = "Provides a Yahoo weather forecast, with basic UI, for a given zip code."
  s.homepage     = "https://github.com/stevenpsmith/YahooWeatherService"
  s.license      = 'MIT'
  s.author       = { "stevenpsmith" => "ssmith@chariotsolutions.com" }
  s.source       = { :git => "https://github.com/stevenpsmith/YahooWeatherService.git", :tag => 'v0.0.3' }
  s.platform     = :ios, '5.1'
  s.source_files = 'YahooWeather/API/*.{h,m}', 'YahooWeather/Model/*.{h,m}', 'YahooWeather/ViewController/*.{h,m}'
  s.resources = "YahooWeather/ViewController/*.{xib}"
  s.requires_arc = true
  s.dependency 'AFNetworking'
end

Notice the source property. This points to our “private” repo. You can also access local repositories (instead of server based repos) by using a URI like this: '/Users/steve/projects/foo', which points to the root directory of your version controlled code. Also note the use of the tag key (which could also be a :commit). If consumers of this library do not specify a tag or commit, this specifies the snapshot of the code that will be provided. Without specifying anything, the latest commit of the master branch will be used. The source_files and resources specify the assets within this project that will be provided to the consuming projects. The dependency property specifies any pods that this pods would depend upon (in our case, AFNetworking). This file needs to be committed to the repository, and if a tag or commit is specified, that must exist within the repo as well. It’s a good practice to validate the podspec by executing pod spec lint <podspec_name>
This will save a lot of grief with typos, incorrect source directories, etc. Once your podspec is clean, commit it to the repository and tag if needed. The podspec for WunderWeather looks eerily similar.

Using the private libraries

The next step is to use these libraries in a new project. This is the WeatherComparator project, and it’s source can be found at https://github.com/stevenpsmith/WeatherComparator. We get started like we did before, creating the project within XCode, then exiting and creating a Podfile that references the dependencies. Except this Podfile will look a little different, as shown below:

platform :ios, '5.1'

xcodeproj 'WeatherComparator.xcodeproj'

pod "YahooWeather", :git =&gt; 'https://github.com/stevenpsmith/YahooWeatherService.git', :tag =&gt; 'v0.0.3'
pod "WunderWeather", :git =&gt; 'https://github.com/stevenpsmith/WunderWeatherService.git', :tag =&gt; 'v0.0.2'

Here we are adding the :git key to the pod declaration. Again, this could be subversion, mercurial, and even a filesystem reference. Notice there are no dependencies on AFNetworking. Our new project doesn’t directly depend upon those, but since the pods do, it will get pulled into our new workspace as it did in the other projects. Execute pod install in a terminal at the root of the new project directory, open the workspace as we did before, and we are good to go. Here is a screen shot of our weather comparator:

If you take a look at the code in the WeatherComparator project, it is very simple. One interesting thing to look at is the Pods-resources.sh script located in the TargetsSupportFiles/Pods directory within the Pods project. Cocoapods generates this script file based on the contents of the podspec for the libraries. This script actually compiles the XIB files and adds them to the projects final build. As you can see, pods can use storyboards as well. This provides a nice way to create modules that include UIs and then combine them into applications.
Even though we used github public repositories, this post shows how to use Cocoapods to manage your project’s public and private dependencies. To try out the project, install Cocoapods, download the WeatherComparator project, execute pod install at the root, open the workspace, run the project, and see how different Yahoo and Wunderground weather forecasts can be. Pretty surprising actually :).

Securing Data in iOS Apps

There are numerous ways to secure data that you are storing on an iOS device.

The Simple/Built-in Way

The simplest way is to take advantage of iOS Data Protection (iOS 4+). This is accomplished by setting an attribute on a file like this:

    [[NSFileManager defaultManager] createFileAtPath:[self filePath]
                    contents:[@"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding]
                    attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                           forKey:NSFileProtectionKey]];

There are several different levels of file protection. The following is taken from the NSFileManager class reference from Apple:

  • NSFileProtectionNone – no file protection
  • NSFileProtectionComplete – file is encrypted when the device is locked or booting
  • NSFileProtectionCompleteUnlessOpen – file is encrypted and can only be opened when the device is unlocked. Once open, the file can continue to accessed even if the user locks the device.
  • NSFileProtectionCompleteUntilFirstUserAuthentication – The file is stored in an encrypted format on disk and cannot be accessed until after the device has booted. After the user unlocks the device for the first time, your application can access the file and continue to access it even if the user subsequently locks the device.

A Core Data sqlite store can also be encrypted by setting the NSFileProtectionKey file attribute to one of the above values (after you create your persistent store coordinator).

However, an important thing to realize is that this type of data protection requires the device to have a passcode set on it (http://support.apple.com/kb/HT4175).

CommonCrypto

What if you really need to insure that your data is protected regardless of whether the device has a passcode set? One way is to use the CommonCrypto libraries from Apple. This can be fairly complex. There is a great write up here from Rob Napier on what’s involved. Fortunately, he has also provided a wrapper that greatly simplifies this process here. Using this library (or writing your own), you can encrypt your data and store it wherever you need. If you are using Core Data, you could write an NSValueTransformer for the Core Data entity attributes that require encryption using CommonCrypto or the RNCrypto library to encrypt/decrypt the attribute values when accessing or updating them.

SQLCipher

One more way to protect your data, specifically data that you want to store in a SQLite database, would be to use SQLCipher. SQLCipher encrypts/decrypts data at the page level and is transparent to your application code. You still use the standard SQLite APIs, with one additional method call when accessing the database (passing your key to sqlite). There are excellent instructions on setting it up for use in an iOS project here.

You can build SQLCipher as a set of static libraries by following the same iOS instructions with some modifications and additions. Here are the high level steps:

  1. Instead of creating a view based iOS project, create a static library project
  2. Follow the balance of the steps for including SQLCipher in your project
  3. Add a new “Aggregate” build target
  4. Add a “Run Script” build phase and paste the following script into it. This will build both the simulator and iOS based targets for the libcrypto, libsqlcipher, and libssl libraries.
  5. xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator
    
    xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos
    
  6. Add another “Run Script” build phase and paste the following script into it. This will merge the simulator and iOS builds into one for each library. These three libraries are what would be added to a project using SQLCipher.
  7. CRYPTO_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libcrypto.a" &&
    SQLCIPHER_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libsqlcipher.a" &&
    SSL_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libssl.a" &&
    
    DEVICE_CRYPTO_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libcrypto.a" &&
    DEVICE_SQLCIPHER_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libsqlcipher.a" &&
    DEVICE_SSL_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libssl.a" &&
    
    UNIVERSAL_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal" &&
    
    
    # Create framework directory structure.
    rm -rf "${UNIVERSAL_LIBRARY_DIR}" &&
    mkdir -p "${UNIVERSAL_LIBRARY_DIR}" &&
    
    # Generate universal binary for the device and simulator.
    lipo "${CRYPTO_LIB}" "${DEVICE_CRYPTO_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libcrypto" &&
    lipo "${SQLCIPHER_LIB}" "${DEVICE_SQLCIPHER_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libsqlcipher" &&
    lipo "${SSL_LIB}" "${DEVICE_SSL_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libssl"
    

Once completed, you can build your new aggregate target and there should be 3 libraries located in a subdirectory within your project build directory. Just add these to your XCode project as you would any other library and you are good to go.

I have also posted this article on my companies blog, located here.

NSFetchedResultsController and Core Data Managed Object Updates

In the process of working on an iPhone project for a client, I came across an interesting problem. I was using Core Data to store domain related information. This data is being pulled from a server, parsed, and persisted on a background thread. The main thread of the application presents a list of information based on what is persisted. In this application, I chose to use an NSFetchedResultsController to manage querying the underlying Core Data store and implemented the standard UITableView delegate methods. I won’t go into detail on how to do that here, but there are many good write-ups out there, including this very good one: How to use NSFetchedResultsController.

I was updating the underlying data store in a background thread with its own managed object context, as is the standard practice. As a result, the NSFetchedResultsController’s managed object context was unaware of the changes to the data store. This was easily fixed by subscribing to the NSManagedObjectContextDidSaveNotification notification in my table view controller class.

[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(refreshData:) 
                                             name:NSManagedObjectContextDidSaveNotification 
                                           object:nil];

The notification that gets fired includes all the information about the changes to the underlying data store (Inserts, Updates, and Deletes). The nice thing here is that there is a convenience method on the managed object context for merging these changes in, as shown in the snippet below:

- (void) refreshData:(NSNotification *)notif {
    [[[self fetchedResultsController] managedObjectContext] mergeChangesFromContextDidSaveNotification:notif];
}

On the surface, this worked beautifully. Once the changes are merged in, the NSFetchedResultsController delegate methods start firing. This is where my struggle occurred. All the proper delegate methods fired correctly, but the underlying result set for the NSFetchedResultsController still had the old data. I combed through the documentation, code samples, etc. and it seemed that I was doing everything correct. I came up with some hack arounds, but they were too ugly…and just wrong. I knew there had to be something I was missing.

I could see that the object that I updated in my main thread’s managed object context was a faulted object (see Apple’s description of a Faulted object here if you aren’t familiar with this concept). What I didn’t realize is that when accessing attributes of a faulted object, the store will retrieve the underlying data from the previously fetched data first, then from the underlying data store. How long the previously fetched data is used is based off of the staleness interval attribute of the managed object context. BTW, this defaults to infinity 🙂

So, the addition of 1 line of code when creating my NSFetchedResultsController to set the staleness time interval to 0 fixed everything.

[context setStalenessInterval:0];

Now my NSFetchedResultsController delegate methods were able to update the table view with the updated data as expected.

For those of you following the tutorial referenced above, one change is necessary if you use a sectioned table view by setting the sectionNameKeyPath when initializing the NSFetchedResultsController.

In the didChangeObject delegate method, the NSFetchedResultsChangeMove case should look like this:

case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
                             withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
                             withRowAnimation:UITableViewRowAnimationFade];
            break;

Otherwise you will get an error under certain use cases (e.g. changing/removing the last item in a section so the section gets deleted).