Skip to content

Posts tagged ‘Cocoa Touch’

1
Dec

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:

  1. Create a new certificate authority with OpenSSL.
  2. Create a new certificate, then sign it with that certificate authority you just created.
  3. Create a configuration profile in the iTunes Configuration Utility with the settings you would like to enforce.
  4. In the “Credentials” section in the iTunes Configuration Utility, add your CA’s public-facing certificate to the configuration profile.
  5. Add the certificate you signed with your CA to your application’s bundle.
  6. In your application, verify the certificate you included.
  7. 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.

14
Aug

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

12
Aug
GCDExample Screenshot

GCD Example Updated (Now With More Speed!)

Due to popular demand, I’ve updated my GCD example from previous talks to include a few things to make the example not only do something on a background queue, but also snappy. It should scroll much better now. A quick rundown of what changed:

  • Images are now resized. Since the example uses wallpaper-sized images, there’s no sense in not resizing them to go on a 44-pixel-tall table view cell. I’m using the popular image-resizing routines from Trevor’s Bike Shed to do the resizing with a nice interpolation quailty.
  • Those resized images are now cached. I use an NSCache to store the images. If the app receives a memory warning, it’ll jettison all of the cached images, but if you’re just scrolling up and down this is a quick and dirty way to cache the images. I had never really used NSCache before, so this was a good excuse to try it.
  • I’m at CocoaConf in that state down to the South today, so this post has been brought to you by late-night hotel room caffeine. I made some other changes to the project to deal with a weird table view cell bug that I’ve submitted to Apple; a post on that is coming up next!

3
Mar

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 NSManagedObjectContext across multiple threads, dispatch queues, or NSOperation queues, so for each one you’ll need to create a new instance. Similarly, don’t pass an NSManagedObject or subclass thereof between threads. Give each of your objects a unique ID—CFUUID works well for this—and pass the ID around, pulling a new object out of your NSManagedObjectContext for 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:
    1. 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.
    2. 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 -reloadData on UITableView.
  • 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 NSLock or 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?

28
Feb

Cocoa Touch Tutorial: Using Grand Central Dispatch for Asynchronous Table View Cells

One of the problems that an iOS developer will often face is the performance of table view cells. Table view cells are loaded on-demand by the UITableView that they’re a part of; the system calls ‑cellForRowAtIndexPath: on the table view’s dataSource property to fetch a new cell in order to display it. Since this method is called (several times) while scrolling a table view, it needs to be very performant. You don’t have very much time to provide the system with a table view cell; take too long, and the application will appear to stutter to your users. This kills the immersion of your application and is an instant sign to users that the application is poorly-written. I guess what I’m saying is that this code needs to be fast. But what if something you need to do to display the table view cell takes a long time—say, loading an image?

In my MobiDevDay presentation a couple of weeks ago, I illustrated a solution to this problem: Grand Central Dispatch. GCD, Apple’s new multiprocessing API in Mac OS X Snow Leopard and iOS 4, is the perfect solution for this problem. Let’s take a look at how it works.

Grand Central Dispatch operates using queues. Queues are a C typedef: dispatch_queue_t. To get a new global queue, we call dispatch_get_global_queue(), which takes two arguments: a long for priority and an unsigned long for options, which is unused, so we’ll pass 0ul. Here’s how we get a high-priority queue:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

It’s pretty straightforward. To use this queue, we add blocks of code onto it. Typically this is done with blocks (Apple’s new code encapsulation extension to the C language), though it can be done with C functions. To submit a block onto a queue for execution, use the functions dispatch_sync and dispatch_async. They both take a queue and a block as parameters. dispatch_async returns immediately, running the block asynchronously, while dispatch_sync blocks execution until the provided block returns (though you cannot use its return value). Here’s how we schedule some code onto a queue (we’ll assume this code runs after our previous example, so queue is already defined):

dispatch_async(queue, ^{
    NSLog(@"Hello, World!");
});

It’s very easy to forget the ); at the end of that line, so be careful.

How does this apply to table view cells? Let’s take a look at a typical scenario for loading images from disk:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ExampleCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:CellIdentifier] autorelease];
    }

    // Get the filename to load.
    NSString *imageFilename = [imageArray objectAtIndex:[indexPath row]];
    NSString *imagePath = [imageFolder stringByAppendingPathComponent:imageFilename];

    [[cell textLabel] setText:imageFilename];
    UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    [[cell imageView] setImage:image];

    return cell;
}

The problem with that code is that creating image blocks until ‑imageWithContentsOfFile: returns. If the images are especially large, this is catastrophic. Modifying this code to use Grand Central Dispatch is simple:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:CellIdentifier] autorelease];
    }

    // Get the filename to load.
    NSString *imageFilename = [imageArray objectAtIndex:[indexPath row]];
    NSString *imagePath = [imageFolder stringByAppendingPathComponent:imageFilename];

    [[cell textLabel] setText:imageFilename];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

    dispatch_async(queue, ^{
        UIImage *image = [UIImage imageWithContentsOfFile:imagePath];

        dispatch_sync(dispatch_get_main_queue(), ^{
            [[cell imageView] setImage:image];
            [cell setNeedsLayout];
        });
    });

    return cell;
}

First, we create our image asynchronously by using dispatch_async(). Once we have it, however, we have to come back to the main thread in order to update our table view cell’s UI (all UI updates should be on the main thread, unless you like reading crash reports). GCD has a function to get the main queue—analogous to the main thread—called dispatch_get_main_queue(). We can dispatch a block to that thread to update the UI.

By making this simple modification, we can very easily improve the performance of our table view. There are a few steps remaining, however, and this method has one serious shortcoming: if the cell is re-used by the time the image loads, it can load the wrong image into the cell. To get around this, it would be better to cache the images in an array or a dictionary (just be sure to release it in your view controller’s ‑didReceiveMemoryWarning: method). That said, this is an example of something you can do quite easily to improve the performance of your application. The better it performs, the more your users will like it, and that’s the ultimate goal.

The code used in this post is available as a GitHub repository.

5
Feb

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.

16
Nov

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 moreRead more

9
Mar

New iPhone Project: uWarranty

So, I created a new iPhone application called uWarranty. It used an unpublished Apple API for warranty status (from selfsolve.apple.com), and so I got this after submitting it:

Thank you for submitting your application to the App Store. Unfortunately, your application, uWarranty, cannot be added to the App Store because it violates section 3.3.7 of the iPhone Developer Program License Agreement:

“Applications may not perform any functions or link to any content or use any robot, spider, site search or other retrieval application or device to scrape, retrieve or index services provided by Apple or its licensors, or to collect, disseminate or use information about users for any unauthorized purpose.”

OK, I get it. That’s fine and is Apple’s prerogative. But now I have this program and all the development time that went into it. So what do I do? I guess the answer is to open-source it, just like AppSales Mobile. Watch this space for more details as I clean up the code and throw it up on a public repository somewhere.

26
Feb

Cocoa Touch Tutorial: Stripping Non-Alphanumeric Characters on Entry in a UITextField

In a previous post, I showed you how to trim non-alphanumeric characters from a string. Here I’ll go more in-depth and show a method that I wrote to restrict text entry in a UITextField to alphanumeric characters. Since I also wanted the characters to be uppercase, I’ll also ensure that only uppercase characters are allowed.

This should all happen in the - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string method of your UITextField’s delegate (which, of course, must implement the UITextFieldDelegate protocol). I’ve implemented it as follows:

- ( BOOL )textField:( UITextField * )textField
shouldChangeCharactersInRange:( NSRange )range
  replacementString:( NSString * )string
{
    /*
     * We only want uppercase letters and numbers in this text field, so if
     * this method is adding something else, we don't want it. But we also
     * want to support copy-and-paste, so it's not always going to be one
     * character added.
     */
    BOOL shouldAllowChange = YES;

The shouldAllowChange variable is set to YES initially because we want to allow this change when possible. The method will test the string to see if it meets criteria for rejection as we move forward.

    NSMutableString *newReplacement =
    [[ NSMutableString alloc ] initWithString:[ string uppercaseString ]];

    if ( ! [ string isEqualToString:newReplacement ]) {
        shouldAllowChange = NO;
    }

First, we define newReplacement. It’s an NSMutableString so that if we discover non-alphanumeric characters in it, we can remove them on-the-fly. It also serves as a convenient string against which we can test to see if string is already uppercase.

    NSCharacterSet *desiredCharacters =
    [ NSCharacterSet alphanumericCharacterSet ];

    for ( NSUInteger i = 0; i < [ newReplacement length ]; i++ ) {
        unichar currentCharacter = [ newReplacement characterAtIndex:i ];

        if ( ! [ desiredCharacters characterIsMember:currentCharacter ]) {
            shouldAllowChange = NO;
            [ newReplacement deleteCharactersInRange:NSMakeRange( i, 1 )];
            i--;
        }
    }

In this section, we define the NSCharacterSet that we want to work with - in this case, the alphanumeric character set. We go through one character by a time and if the current character isn’t alphanumeric, we remove it from the NSMutableString (decrementing i so that we don’t inadvertently skip a character) and set our shouldAllowChange flag accordingly.

    if ( shouldAllowChange ) {
        [ newReplacement release ];
        return YES;
    } else {
        [ textField setText:[[ textField text ]
                             stringByReplacingCharactersInRange:range
                             withString:newReplacement ]];
        [ newReplacement release ];
        return NO;
    }
}

To finish, if shouldAllowChange is still true, we return YES and allow the replacement characters to be added. Otherwise, we return NO, but not before using our replacement replacement string (say that ten times fast) to manually edit the text field’s text. The end result is a text field that will consist only of uppercase letters and numbers.

1
Nov

Cocoa Tutorial: Strip Non-Alphanumeric Characters from an NSString

Let’s say you have an NSString that contains both alphanumeric and non-alphanumeric characters and you want to strip the non-alphanumeric characters out of it. The hard way is to manually go through, character-by-character, and put the character in a new string if it matches certain criteria. But why do it the hard way?

Apple provides a class that we can use for this to great effect: NSCharacterSet. We want alphanumeric characters, so we can create a character set of the characters we want using this method:

NSCharacterSet *alphanumericSet = [ NSCharacterSet alphanumericCharacterSet ];

Now we have a character set like we want. We just need a way to turn our string into a string that contains only those characters. Unfortunately, the closest thing in NSString’s implementation is the -stringByTrimmingCharactersInSet: method. But that seems to do the opposite of what we want. Fortunately NSCharacterSet has our back here. We can use the -invertedSet method. So here is our final code:

NSString *beginningString = @"Some string with non-alphanumeric characters. !@#$%^&*()";
NSCharacterSet *nonalphanumericSet = [[ NSCharacterSet alphanumericCharacterSet ] invertedSet ];
NSString *endingString = [ beginningString stringByTrimmingCharactersInSet:nonalphanumericSet ];

In this example, endingString will be equal to “Somestringwithnonalphanumericcharacters”.

UPDATE: As it turns out, this only works if the non-alphanumeric characters are at the beginning or end of the NSString. Whoops.