Nick Harris

Posts Tagged ‘Objective-C

Core Data and Enterprise iPhone Applications – Protecting Your Data

Introduction

NewsGator’s primary software offering is a product called Social Sites which basically adds social networking aspects to Microsoft SharePoint. My role in this product is to create the iOS device apps that interact with the server. My app is pulling in a lot of data that an enterprise customer considers *very* sensitive so data protection is the first thing many CIOs ask about when considering Social Sites. This post is to layout both how iOS protects data on the device so our prospective clients have a better idea about how it works, along with some additional steps developers can take to further protect their data.

Much of what I’ll discuss was covered in Session 209 at WWDC, but since that information is under NDA, I’ll stick to information that’s available in the public domain (including this post about iPhone 3GS and this post about iOS4). But if you have access to the WWDC videos, Session 209 is a great resource.

Data Encryption Starting with iPhone 3GS

Social Sites uses Core Data with a SQLite store type for storage and persistence. When the app first runs, it creates a Social_Sites.sqlite database file in its sand boxed documents directory on the device file system.

With the 3GS, any data written to the filesystem is encrypted using hardware encryption. By simply creating the Social_Sites.sqlite file on the file system, the data stored in it is already encrypted. This encryption also allows for the Social Sites data to be instantaneously unavailable when you use Remote Wipe. I say unavailable because the remote wipe doesn’t actually overwrite the encrypted data on the filesystem, but instead overwrites the hardware key used to encrypt/decrypt it. But still, the important thing is that the data on disk is encrypted.

This type of data encryption is also used on the current iPads.

Data Encryption on iOS 4

The way hardware encryption works on iOS4 has been greatly improved. I can’t find any public document from Apple that explains the improvements in detail, but their iOS 4: Understanding data protection support page gives a hint with “Data protection enhances the built-in hardware encryption by protecting the hardware encryption keys with your passcode.” I’ll just say that hardware data encryption on the 3GS and iPad use a single hardware encryption key. The WWDC session goes into great detail about the under the hood improvements. The main takeaway for a CIO though is that the Social Sites sqlite datafile on iOS4 has stronger encryption than it did in the past.

Further Protection – NSFileProtectionComplete

In iOS4, Apple introduced data protection. It gives you the ability to encrypt the hardware keys used to encrypt your files and to erase those keys when the system is locked – leaving your file unreadable until the phone is unlocked and the keys are regenerated.

This level of protection is not built in by default though. The user has to enable it (or an IT department can enable it and force users to have it on) and your code must set an attribute on your Core Data sqlite store as well.

Steps for the user to enable data protection are outlined on the iOS 4: Understanding data protection support page. You need to have the passcode turned on since this is what the system will use to generate the encryption for the keys.

As a developer, you need to set the NSFileProtection level of your sqlite store to NSFileProtectionComplete (by default it will be NSFileProtectionNone). You can do this right after you instantiate your persistent store coordinator:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"Social_Sites.sqlite"];
    NSURL *storeUrl = [NSURL fileURLWithPath:storePath ];

    NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
        // Handle error
    }

    if(RSRunningOnOS4OrBetter())
    {
        NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
        if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:storePath error:&error]) {
            // Handle error
        }
    }

    return persistentStoreCoordinator;
}

BOOL RSRunningOnOS4OrBetter(void) {
    static BOOL didCheckIfOnOS4 = NO;
    static BOOL runningOnOS4OrBetter = NO;

    if (!didCheckIfOnOS4) {
        NSString *systemVersion = [UIDevice currentDevice].systemVersion;
        NSInteger majorSystemVersion = 3;

        if (systemVersion != nil && [systemVersion length] > 0) { //Can't imagine it would be empty, but.
            NSString *firstCharacter = [systemVersion substringToIndex:1];
            majorSystemVersion = [firstCharacter integerValue];			
        }

        runningOnOS4OrBetter = (majorSystemVersion >= 4);
        didCheckIfOnOS4 = YES;
    }
    return runningOnOS4OrBetter;
}

Considerations

If your application needs your Core Data store in any background processing, then you cannot use data protection. Any attempt to access files that are NSFileProtectionComplete will cause an exception.

There are application delegates applicationProtectedDataWillBecomeUnavailable and applicationProtectedDataDidBecomeAvailable as well as notifications UIApplicationProtectedDataWillBecomeUnavailable and UIApplicationProtectedDataWillBecomeAvailable that you can use to determine what state your protected data is in.

You could then in theory keep unprotected data in memory in your background processing then write it into your Core Data store when protected data becomes available again.

You can find more information in the iPhone Application Programming Guide.

Conclusions

My answer to any CIO asking would be to ensure any device using Social Sites be a 3GS, an iPad or an iPhone 4. I would also encourage enforcing passcode and data protection for any device running iOS4. These steps ensure that the SQLite data store for Social Sites is hardware encrypted with remote wipe capabilities and further protected with iOS4 and data protection.

UPDATE:
Here’s the link to the iPhone Configuration Utility 3.0 which can be used to “create, maintain, encrypt, and install configuration profiles, track and install provisioning profiles and authorized applications, and capture device information including console logs.”

Written by Nick Harris

July 14, 2010 at 8:23 pm

Posted in Code, Core Data

Tagged with ,

Fast UITableViewCell with a UIWebView

(skip to the code)

The Problem

If you use UITableView’s you know that scrolling through your cells has to be fast.  If you’ve done much research, you’ve probably found the Tweetie approach using one custom view and doing the drawing yourself.  This works great for static text and image cells.  It gets more difficult when you need to add hyperlinking to the text in the cell.  The problem becomes how you add hyperlinking to a UITableViewCell but keep the scroll speed as fast as possible.

First Solution

I have an app that requires this type of functionality.  Its an enterprise social computing app – you can think of it like Facebook or Yammer.  Users can post microblog status messages that other users can “like” and comment on.

The first attempt I made was to subclass ABTableViewCell and in my drawing I would set aside certain regions that were “linkable”.  In this screenshot, the light blue in the cells denotes text that is linkable – usernames in this case.

ActivityStream.png

My tableview cell then waits for touchesBegan and looks to see if a touch is in one of these areas.  If it is, it sets a flag denoting what area is touched and redraws the cell with a highlight behind the text.  When it gets touchesEnded it tells its delegate what function needs to happen.  So for the screenshot above, touching the username would navigate to a view showing that users details.

Its a pretty easy approach but it has its limits such as a status that has multiple links.  I was left with either hand parsing the html and measuring where all the text should go that’s linkable, draw that text in a different color, and remember those regions.  Or I could just strip out any HTML and draw flat text for the status…  I took the easy route.  Worked great for a 1.0 release but of course status linking was the number one feature request from users.

Second Solution

The idea came to me that I could add my own UIWebView to the cell and become its delegate so that I could react to any touches on links.  As you probably know though, adding subviews – especially non-opaque subviews – can cause scrolling to be slow and to skip frames in the animations.  To work around this I decided two things…

1. Only use the UIWebView when the text actually contains a link, otherwise just draw flat text
2. Only show the UIWebView when the tableview isn’t scrolling so that we don’t get in the way

This approach seems to work really well – but there are a few issues that you need to work around.  Most of them I’ve figured out but there are two that I’m still stumped on which I will detail in a bit.

Issues and Workarounds

Issue one – loading an HTML string into a UIWebView takes a second

It takes a little bit for the webview to display your HTML.  My workaround is to draw the HTML stripped version of the status in flat text and keep the webview hidden until you receive webViewDidFinishLoad. At that point you can remove the flat text or draw it in clear color and set hidden = NO on the webview.

Issue two – UIWebViews flash when first loading content

I honestly can’t tell you why this happens, but it does.  And its a bit jarring to the user experience.  The workaround for this is to simply move the code to redraw the cell and display the webview into its own method then use performSelector:withObject:afterDelay to call it.  0.35 second duration seems to work nice.

Issue three – UIWebViews will crash your app if you don’t nil its delegate

This was a weird bug.  Because your popping webviews on and off cells at a pretty quick pace, you’ll run into situations where the webview hasn’t completed loading your HTML before you want to pop the webview off and release it during prepareForReuse (and you always want to pop the webview off and release it when preparing for reuse!) or dealloc.  The problem is that the webview continues to live until its done loading whatever it is it was loading.  When its done it will tell its delegate (your cell) that its done.  If your cell was dealloced… CRASH.  So make sure you set its delegate to nil.

Other Issues I Haven’t Solved

Issue one – UIWebView eats touches

If your app responds to tableView:didSelectRowAtIndexPathto show a details view, you’ll notice that if you touch within the bounds of the webview, this method will never fire.  So the user is forced to touch a part of the cell thats outside the webview.  If you designed your cell well, they’ll have no idea where this boundry is.  Its a very frustrating user experience – one that I would love to get solved before shipping.

Issue two – status bar touches no longer scroll to top

If any of your displayed cells have a webview on them – hidden or not – the scroll to top by touching the status feature of the tableview quits working.  My guess is that the scrollView child of the UIWebView is eating these as well and not passing them up the responder chain.  Its another issue I haven’t resolved yet.

Code

OK – enough talk.  Here’s a sample project that shows how I do this…  FastTableViewCellWithWebView (or you can look at the full Mercurial repository here).  Download it, run it, let me know what you think.  And more important… let me know if you can resolved my two issues!

Written by Nick Harris

June 17, 2010 at 9:53 pm