Enforcing iOS Security Settings in Third-Party Applications
A while back, I was working on an application for a client with a very specific requirement. Since it collected personal data, the application could only run on iOS devices that were protected with a passcode. This requirement, seemingly very simple from the client’s perspective, was a bit of a hassle to implement on the programming side of things. There’s no simple method on UIDevice to determine if a passcode is set, nor is there a way to force that programatically. In fact, there’s no way to force most things like that. The iOS device isn’t the programmer’s, it’s the user’s. Except when it isn’t.
One of the things that you can do is to use the iPhone Configuration Utility to make a configuration profile. These profiles can support a range of things, from requiring a passcode (or even an advanced, non-numeric passcode) to WiFi settings, VPN to CardDAV settings. Creating a configuration profile that requires a passcode, then installing that configuration profile onto the device is a no-brainer. But how do you ensure that the application will only run in that case?
Disclaimer: Before I go any further, you should know that since this was an in-house project, none of the code that I wrote made it into the App Store. Therefore, I don’t know if this is kosher in an App Store app, nor do I recommend this approach for that.
One thing that you can do is to include a self-signed certificate in a configuration profile. Those of you familiar with OpenSSL may be groaning as you realize where this horrible, horrible workaround is headed. I created a new certificate authority. With that new certificate authority, I signed a separate certificate that I had created. Verifying this certificate, then, requires that the verifying party accept the certificate authority’s certificate as valid. Well, since you can set that in the configuration profile, I did, along with the passcode requirement. Then, in the app, I bundled the certificate that I had signed with my CA.
When the app starts up, it attempts to verify the certificate. In the case where the configuration profile is installed and the CA’s certificate is in the system’s keychain as trusted, this is no problem: the certificate checks out and my app is free to go. If that validation fails, however, then I know that the certificate from the CA is in the system, so I know that the configuration profile is installed, as well.
Why this works for a passcode so well is that to install a configuration profile on a device without a passcode when the profile requires one is that you can’t install it without setting a passcode in the process. For the client, this was Good Enough, and the app shipped and worked properly. It’s worth noting, though, that the less the end-user knows about this process, the better. To circumvent the passcode restriction, all one would have to do would be to modify the configuration profile to still include the CA’s certificate, but not the pas code requirement. For that reason I can’t recommend this for anything like EMR or tax records, but for minor demographic information like we were collecting, this sufficed.
I realize this didn’t include any code, but the individual portions aren’t that hard, and I don’t have access to the original code so I’d have to re-write them all. Here they are in a nicely-formatted list for those keeping score at home:
- Create a new certificate authority with OpenSSL.
- Create a new certificate, then sign it with that certificate authority you just created.
- Create a configuration profile in the iTunes Configuration Utility with the settings you would like to enforce.
- In the “Credentials” section in the iTunes Configuration Utility, add your CA’s public-facing certificate to the configuration profile.
- Add the certificate you signed with your CA to your application’s bundle.
- In your application, verify the certificate you included.
- Distribute the configuration profile along with your application to end users.
Like I said, this is far from perfect. But when you’re working with an enterprise client who has Big Needs, this is one trick to keep in your back pocket when you’re up against a deadline.
Cocoa Touch: Circumventing UITableViewCell Redraw Issues with Multithreading
In your career as a Cocoa or Cocoa Touch developer, every now and then you’ll encounter an issue with something Apple has written. Whether it’s a full-blown bug, something that doesn’t work quite how you’d expect it to, or a minor inconvenience, it happens. When it does, naturally the first thing you do is file a bug report (right?). After that, though, you need to do something about it. This usually occurs right when a project is due, so often we can’t wait for Apple’s engineering teams to fix the problems (or tell you that you’re wrong). This post is an example of using KVO to get around the problem without worrying about it anymore.
The Problem: In iOS, if you create a UITableViewCell and return it to the table view in its data source’s -tableView:cellForRowAtIndexPath: method, but then return later (say, after doing some background processing) to add an image to the cell’s imageView, you don’t see anything! Why? Well, it looks like either the image view isn’t added to the cell’s view hierarchy if you don’t immediately add an image or there’s some other bug in the UITableViewCell implementation. I don’t think it’s a bug, I think it’s just a side effect of an optimization; if there’s no image, why add it to the cell?
So how do we fix it? Well, a simple call to -setNeedsLayout gets the cell to fix itself quite nicely. But we shouldn’t have to do that from our table view data source—that has a bit of code smell to it. Lines like that quickly get overused, with programmers calmly stating, “I don’t know why, but we always do that.” No, a better solution is to get the cell to handle this problem on its own.
We’ll create a subclass of UITableViewCell and use KVO. When we create the cell, we’ll register for KVO notifications with the on the image view whenever its image property is modified—but we’ll send the option to include the old value in the change dictionary. When we receive the notification, we’ll look at that dictionary, and if the old value was nil, then we’ll send self a -setNeedsLayout message. This avoids having to do it in other classes, and only does it when necessary. We simply set it and forget it.
#import <UIKit/UIKit.h>
@interface JKTableViewCell : UITableViewCell
@end
#import "JKTableViewCell.h"
@implementation JKTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [[self imageView] addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionOld context:NULL]; } return self;}
// The reason we’re observing changes is that if you create a table view cell, return it to the// table view, and then later add an image (perhaps after doing some background processing), you// need to call -setNeedsLayout on the cell for it to add the image view to its view hierarchy. We// asked the change dictionary to contain the old value because this only needs to happen if the// image was previously nil.- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if (object == [self imageView] && [keyPath isEqualToString:@"image"] && ([change objectForKey:NSKeyValueChangeOldKey] == nil || [change objectForKey:NSKeyValueChangeOldKey] == [NSNull null])) { [self setNeedsLayout]; }}
@end
iOS Programming for Multicore Processors
With the impending release of the iPad 2, understanding how to program multithreaded applications will quickly become paramount as applications continue to push the envelope to make immersive experiences with high-performance computation. Now, without actually having a multicore iPad in my hands, I can’t say exactly how the system will behave, but there are a few best practices we should all be aware of when writing iOS code:
- Using Core Data? You can’t share access to an
NSManagedObjectContextacross multiple threads, dispatch queues, orNSOperationqueues, so for each one you’ll need to create a new instance. Similarly, don’t pass anNSManagedObjector subclass thereof between threads. Give each of your objects a unique ID—CFUUIDworks well for this—and pass the ID around, pulling a new object out of yourNSManagedObjectContextfor each thread. It’s a pain, but that’s how to (safely) get around threading and Core Data. - Always call UIKit updates from the main thread. Whatever you’re doing to your user interface, be it updating a label, loading an image into an image view, anything that’ll be rendered to screen—and some things that won’t—should be run on the main thread. There are two main ways to do this:
- Use Grand Central Dispatch. Using
dispatch_get_main_queue(), you can get a reference to the main queue and submit blocks to it for updating your UI. This is typically a clean, easy way to refactor existing code for thread-aware programming. - Use
-performSelectorOnMainThread:withObject:waitUntilDone:and friends. This has the drawback of only working for methods that take one or zero Objective-C objects as arguments, but can be a quick and easy way to use fire-and-forget methods like-reloadDataonUITableView.
- Use Grand Central Dispatch. Using
- Think about how you declare your properties. How many people have been using atomic properties? Before now, not many. In fact, before now, it was typically useless, as the chances of something interrupting your accessor methods was pretty low. Now, though, if you’re planning on accessing an object from multiple threads, you really need to control access to your properties.
- Use locks. Locks, long the scourge of the multithreaded-code author, are simply essential for some parts of multithreaded programming. Whether you’re using
NSLockor a lower-level lock, or even something like Grand Central Dispatch’s counting semaphore type,dispatch_semaphore_t, protect critical regions of your code from multiple accessors with (carefully-thought-out) locked access.
This list is by no means exhaustive, and Apple can do a lot to make this irrelevant (such as make UIKit threadsafe, which would be a killer iOS 5.0 feature), but with the iPad 2’s arrival, developers can no longer assume safety from threading problems. Be sure also to read Apple’s Threading Programming Guide to get anything I’ve left out.
It also makes a great excuse to buy an iPad 2. I mean, you need to test this, right?
iPhone Programming: Hard Mode
After a conversation with some co-workers, I discussed how it would be technically possible to write an iPhone app using only a main.m file—no separate class files. This post is the result of that. It’s definitely doable, but not something I’d ever recommend for shipping code.
The code is explained below, but you can get the full source in my public GitHub repository.
Getting Started
By default, an iPhone application template gives you a main.m file, but it’s pretty basic:
#import <UIKit/UIKit.h>
int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
To go from that code to running your application, a few things happen, mostly in UIApplicationMain. Its last argument, typically nil, is a pointer to an NSString that specifies the class name of the application delegate. Normally, your Info.plist file specifies a nib file (the keys NSMainNibFile or NSMainNibFile~ipad), which in turn specifies the application delegate, instantiates it, etc.
So, first, delete the MainWindow.xib file, then all other class files (MyCoolAppDelegate.h and MyCoolAppDelegate.m, for instance). There should be three files left in the iPhone application: your Prefix.pch file (which we could technically do without), main.m, and your Info.plist file.
The Fun Begins
Administrivia: Creating Strings
Since, as a part of the exercise, we don’t want to use Objective-C, I also decided against using any constant NSStrings (@"Hello, World!" and the like). To create a string, then, we’ll use CFStrings. I created a macro to create one from a C string using ASCII encoding:
#define JK_EasyCFString(X) (CFStringCreateWithCString(kCFAllocatorDefault, (X), kCFStringEncodingASCII))
Don’t forget to call CFRelease() when you’re done with it, though.
Creating a Class
We need to modify the call to UIApplicationMain to include our application delegate class if we want this app to be anything more than a blank screen. What class name can we give it, though? We’ll need to create a class, and we’ll call it “HardModeAppDelegate.”
// Create a class to serve as our application delegate. Class appDelegate = objc_allocateClassPair(NSClassFromString((id)NSObjectString), "HardModeAppDelegate", 0);
The first argument is a toll-free bridged CFString that specifies NSObject as the superclass, then we pass in the name of the class as a C string and 0 for the number of extra bytes we want (which is nearly always zero). Now let’s set it up a bit:
// Conform to the UIApplciationDelegate protocol. Protocol *appDelegateProto = NSProtocolFromString((id)UIApplicationDelegateString); class_addProtocol(appDelegate, appDelegateProto); // Add methods. SEL applicationDidFinishLaunchingWithOptions = NSSelectorFromString((id)appDidFinishLaunchingOptionsString); class_addMethod(appDelegate, applicationDidFinishLaunchingWithOptions, (IMP)UIApplicationDelegate_ApplicationDidFinishLaunchingWithOptions, "v@:@@"); SEL dealloc = NSSelectorFromString((id)deallocString); class_addMethod(appDelegate, dealloc, (IMP)appDelegate_dealloc, "v@:"); // Add an instance variable for the window. class_addIvar(appDelegate, "window", sizeof(id), log2(sizeof(id)), "@"); // Now that we’ve added ivars, we can register the class. objc_registerClassPair(appDelegate);
That code is the same as writing an @interface block for a new class, setting up ivars, conforming to protocols, and defining instance methods, but done using the runtime calls instead. Methods are actually C functions, but with two arguments prepended to the argument list: id self and SEL _cmd. Those arguments are actually passed to every Objective-C method, but usually hidden.
Custom Message Sending
The rest of the application is more of this bootstrapped Objective-C in C, but there are a few wrinkles worth discussing, most notably the use of objc_msgSend with methods that return other than id and/or have additional arguments beyond self and _cmd. For instance, to call -bounds on a UIScreen object, I had to cast the return type of objc_msgSend to CGRect:
CGRect (*msgSendBounds)(id self, SEL _cmd); msgSendBounds = (CGRect(*)(id, SEL))objc_msgSend_stret;
Similarly, when calling -initWithFrame: on a UIWindow or UILabel, I casted it to take in a CGRect argument:
id (*msgSendCGRect)(id self, SEL _cmd, CGRect rect); msgSendCGRect = (id(*)(id, SEL, CGRect))objc_msgSend;
Accessing Instance Variables
In my implementation of the HardModeAppDelegate class’s -dealloc method, I need to access the window instance variable to send it a -release message. Using the Ivar type and the object_getIvar function, it becomes easy:
Ivar windowIvar = class_getInstanceVariable(self->isa, "window"); id window = object_getIvar(self, windowIvar);
So what do we have?
To be clear, this is not writing an iPhone app without using Objective-C, per se. The runtime is still being used, messages are being sent, all that. But it is an illustration of some of the heavy lifting that the runtime does for you. I would caution against adopting this practice for real, shipping apps.
Take Me Home
Take Me Home was a good “Hello, World!” iOS app, but its time has come. I’ve pulled Take Me Home from the App Store. Goodnight, sweet prince.
Autorelease is Not Your Friend
How many times have you written this line?
NSMutableArray *foo = [[[NSMutableArray alloc] init] autorelease];
At first glance, it looks fine. foo is an autoreleased NSMutableArray that you can use and, at the end of the method, it’s gone into the ether of the autorelease pool. Don’t get me wrong, most of the time, this use of -autorelease is acceptable. But, in this post, I’ll try to convince you to use autorelease differently in subtle ways. Read more
Xcode 3.2: Using GDB as a Non-Admin User
New in Xcode 3.2 is an authorization setting that looks like this:
<dict> <key>allow-root</key> <false/> <key>class</key> <string>rule</string> <key>comment</key> <string>For use by Apple. WARNING: administrators are advised not to modify this right.</string> <key>k-of-n</key> <integer>1</integer> <key>rule</key> <array> <string>is-admin</string> <string>is-developer</string> <string>authenticate-developer</string> </array> <key>shared</key> <true/> </dict>
The upshot of this is that if you aren’t in the _developer group in the local directory, you’ll have to authenticate as an administrator to use gdb or some of the performance tools. For the vast majority of developers on Mac OS X, who run as an administrator, this is fine, but if you’re running as a regular user, either for security reasons or because you’re in something like a lab setting, this can be a problem. To add a user to the _developer group, use the dscl command:
dscl . -append /Groups/_developer GroupMembership UserName
Replace UserName with the short name of your user account (or $(whoami)) and you should be all set.
If you’re administering Mac OS X in a lab setting, you can either create a LaunchAgent that handles this or a login hook. See the Apple tech note “Running At Login” for more information on login hooks. As an added touch, my login and logout scripts to handle this also remove all users from the group, like so:
dscl . -delete /Groups/_developer GroupMembership
If the GroupMembership key doesn’t exist, dscl will create it—and it doesn’t exist by default—so deleting it outright shouldn’t cause any problems.
Dealing with Special Characters in iPhone 4 Graphics Filenames with Subversion
With the iPhone 4’s high-resolution screen, designers need to create two sets of art; the guidelines are to name the files like so: SomeCoolImage.png and SomeCoolImage@2x.png. Unfortunately, if you try to add these files to an SVN repository, the @ symbol throws them off:
$ svn add Icon\@2x~iphone.png svn: warning: 'Icon' not found
The fix, thanks to the subversion_users Google Group, is to add another @ to the end of the filename, like so:
$ svn add ./Icon\@2x~iphone.png@ A (bin) Icon@2x~iphone.png
If you’d like to do this for all of your high-resolution art in a folder, here’s a tiny Bash command for the task:
for x in `ls *\@*`; do svn add $x\@; done
Take Me Home Now Free
Take Me Home is an ancient, buggy iPhone app that was my “Hello, World!” in the App Store. Its sale rate has plummeted to only a couple of buyers a week and, with the prevalence of real, honest-to-God GPS apps in the store these days, its usefulness is questionable. So from here on out, it’s free. I can’t promise to continue supporting it—especially for new devices and APIs—so the best I can do may be to remove it from the store in the event that some update breaks it.
Enjoy!
