Skip to content

Posts tagged ‘objective-c’

3
Apr

How and Why to Implement Keyboard Shortcuts in iOS 7

Support for keyboard shortcuts is new in iOS 7. The new UIKeyCommand class and the -[UIResponder keyCommands] method allow app developers to add keyboard shortcuts to their iOS apps without using ugly hacks.

Two Reasons

There are two reasons to add keyboard shortcuts to your iOS app:

  1. People often use wireless keyboads with their iPads, especially for text entry. Keyboard shortcuts for an external keyboard are helpful additions that can make typing on an iOS device more convenient.
  2. They work in the iOS Simulator! During development and testing, keyboard shortcuts are so much nicer than using the mouse to simulate gestures on the simulator.

I admit that second reason is a benefit only during development. However, keyboard shortcuts make using the simulator so much nicer that, ten minutes after adding them to my app, I was kicking myself for not adding them sooner.

Key Commands

The first thing to do is to implement the keyCommands method in one of your classes in the responder chain. On iOS, the responder chain includes the currently focused control, its super views, and their associated view controllers.

@implementation MyViewController

- (NSArray *)keyCommands {
    return @[
        [UIKeyCommand keyCommandWithInput:@"f" modifierFlags:0 action:@selector(keyPressF)]
    ];
}

@end

Starting at the first responder and working up the responder chain, iOS will ask each object if it responds to the given action. The first object it finds that implements the method is allowed to handle the key press. This means that you can implement the keyCommands method in a different class than you define the action handler methods. However, to avoid compiler warnings about private methods, it’s often easier to do both in the same class.

The keyCommands method appears to be called three times for each key press, so you may want to cache the array if you have a lot commands.

First Responders

By default, iOS will only generate key press events when a control, such as a text field, is the first responder. You can fix that by overriding the canBecomeFirstResponder method in your root view controller so that it can become the first responder. You can implement the keyCommands method and your action methods there too.

@implementation MyRootViewController

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (NSArray *)keyCommands {
    return @[
        [UIKeyCommand keyCommandWithInput:@"f" modifierFlags:0 action:@selector(keyPressF)]
    ];
}

- (void)keyPressF {
    // Do something awesome here.
}

@end

This lets you avoid having to create and manage a hidden text field or something similar.

Handling Dialogs

For simple apps, that’s all you need to do. However, if your app presents a modal dialog with a text field, then your root view controller will not regain first responder status when that dialog is dismissed.

You could manually tell your root view controller to become the first responder each time a dialog is dismissed. But there is an easier way.

UIApplication is the top-level object in the responder chain. If you subclass UIApplication and override the canBecomeFirstResponder method, then it will become the first responder when a dialog is dismissed. Then it will tell your root view controller to become the first responder again.

@implementation MyApplication

- (BOOL)canBecomeFirstResponder {
    return YES;
}

@end

To tell iOS to use your new UIApplication subclass, you need to modify the main method in main.m:

int main(int argc, char *argv[]) {
    @autoreleasepool {
        // The third parameter is nil by default.
        return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class], NSStringFromClass([AppDelegate class]));
    }
}

Now a dialog with a text field will no longer mess up your keyboard shortcuts.

Simplifying Things

Things are working, but you can simplify a bit more if you want. Instead of having an app delegate separate from your UIApplication subclass, you can let your subclass be its own delegate.

Then you can move everything from your app delegate into your new MyApplication class, including the properties and app lifecycle methods, and delete your app delegate class.

@interface MyApplication : UIApplication <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

Then adjust main.m one more time to assign the same class as both the application and its delegate:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSString *app = NSStringFromClass([MyApplication class]);
        return UIApplicationMain(argc, argv, app, app);
    }
}

Let me know if I’ve missed something or there are better ways to do this. :)

Future Hopes

As of iOS 7, the UIApplicationDelegate is in the responder chain, and I think it would be better to do this stuff there than subclassing UIApplication. But right now the UIApplicationDelegate does not appear to forward first responder status to the root view controller. So this only works with a UIApplication subclass.

11
Apr

Introducing FastSocket

FastSocket is a fast, synchronous Objective-C wrapper around BSD sockets for iOS and OS X. Use it to send and receive files or raw bytes over a socket very quickly. This is the class to use if fast network communication is what you need. An asynchronous API might be better if you want to do something else while your network operations finish.

UPDATE: Now available as a CocoaPod

I wrote FastSocket a couple years ago, and put it up on GitHub, but don’t think I ever mentioned it. There are classes for both clients and servers. It’s available under the MIT license. Please feel free to let me know if you have any problems, questions or suggestions.

To create and connect a client socket:

FastSocket *client = [[FastSocket alloc] initWithHost:@"localhost" andPort:@"34567"];
[client connect];

Send a file:

long sent = [client sendFile:@"/tmp/filetosend.txt"];

Receive a file of a given length:

long received = [client receiveFile:@"/tmp/newlyreceivedfile.txt" length:1024];

Send raw bytes:

char data[] = {42};
long sent = [client sendBytes:data count:1];

Receive raw bytes:

char data[42];
long received = [client receiveBytes:data limit:42];

Close the connection:

[client close];

Please check out the unit tests for more examples of how to use these classes.

17
Oct

Automatic Build Numbers for iOS Apps Using Xcode

UPDATE Sep 2014: Added another idea from Jared Sinclair, who has example code for Git.
UPDATE Jan 2013: Now also updating the build version in the dSYM file.

There are many articles that discuss how to automate build numbers in Xcode. However, some are misleading for iOS apps. Many are quite long. I wanted to find a short, simple way to do this.

Background

On the target Summary tab your project settings, Xcode lets you set a Version and a Build. The version is what we are all familiar with, such as 5.0 and 5.1.1. The build is what often appears in parentheses after the version, as in 5.0 (134) and 5.1.1 (147).

Apple doesn’t seem to care what you use for Version (aka CFBundleShortVersionString), but Build (aka CFBundleVersion) must be a monotonically increasing string, comprised of one or more period-separated integers. Thus Apple will reject your update if you go from 1.1 (10) to 1.2 (0). The version is ignored and, alas, zero is clearly less than ten.

The part about integers is important too because Apple will also reject your update if you go from 1.01 (1.01) to 1.1 (1.1). The period is not a decimal place. Both (1.01) and (1.1) are interpreted as “the integer one followed by the integer one”. We saw this logic in action when OS X went from version 10.4.9 to 10.4.10.

Problem

If you do this wrong, you’ll see this error when you upload your app update:

This bundle is invalid. The key CFBundleVersion in the Info.plist file must contain a higher version than that of the previously uploaded version.

To get your monotonically increasing period-separated integer build number, you could just use the app version, like 5.1.1 (5.1.1). But I think it’s better to use a build number that can help identify the code from which that version of your app was built. Both Git and Mercurial include the ability to count commits, which is perfect—always increasing and helpful in identifying the code.

If you modify the Info.plist file in your project folder during the build process, you’ll probably need to commit the change to your code repository. This extra commit, while not harmful, is unnecessary. Instead, you can modify the Info.plist file in the app package after the build process is finished. The file will be in a binary format, but the PListBuddy tool can handle it.

Solution

Here is the script you need for Mercurial. This script also adds the bundle version to the dSYM file, which is necessary for correctly symbolicating your crash logs. It’s also required for several distribution mechanisms including TestFlight and HockeyApp.

ver = `/usr/local/bin/hg id -n`.strip
puts "Build number is #{ver}"
filepath = "#{ENV['BUILT_PRODUCTS_DIR']}/#{ENV['INFOPLIST_PATH']}"
puts "Updating #{filepath}"
`/usr/libexec/PlistBuddy -c "Set :CFBundleVersion #{ver}" "#{filepath}"`
filepath = "#{ENV['DWARF_DSYM_FOLDER_PATH']}/#{ENV['DWARF_DSYM_FILE_NAME']}/Contents/Info.plist"
puts "Updating dSYM at #{filepath}"
`/usr/libexec/PlistBuddy -c "Set :CFBundleVersion #{ver}" "#{filepath}"`

The first line counts the number of commits in your local repository. It’s safe to use as long as your builds are done on the same machine. Repositories on other computers may have different commit counts. For Git, you can count your commits using a similar command.

Steps

  1. Select your project in the Project Navigator
  2. Select your target
  3. Select the Build Phases tab
  4. Choose “Add Build Phase”
  5. Select “Add Run Script”
  6. Change the Shell to “/usr/bin/ruby”
  7. Copy and paste the script

Within your app, you can grab the version and build number with this code:

NSDictionary *appInfo = [[NSBundle mainBundle] infoDictionary];
NSString *appVersion = [NSString stringWithFormat:@"%@ (%@)", appInfo[@"CFBundleShortVersionString"], appInfo[@"CFBundleVersion"]];

This will give you a string like 2.2 (134) that you can display in your app. I’m using the new object subscripting syntax in Objective-C, but it’s not too hard to switch back to using objectForKey:.

Follow Up

Jared Sinclair has a nice example of how to do this with Git. I really like the idea of using the branch name as a build suffix for Debug builds. I’m going to update my code to match.

3
Aug

3 Tips on Auto Synthesized Properties

Ha ha! You fool! You fell victim to one of the classic blunders, the most famous of which is “never get involved in a land war in Asia.” But only slightly less well-known is this: “Never have an auto-synthesized property and an instance variable with the same name when death is on the line!”

—Vizzini

At work, my team decided to drop support for iOS 4. Most of our customers have upgraded to iOS 5.1. Surprisingly, very few are still using iOS 5.0. I guess over-the-air upgrades are working really well for people.

Our app is old. In some places we still checked if the user had upgraded to iOS 3 yet. :) My team spent the week on cleanup and paying down technical debt. My job was to:

  1. Convert to using object literals for arrays, dictionaries and numbers.
  2. Remove @synthesize calls.
  3. Switch all of our instance variables to properties.

I love shortening and removing code without losing functionality. The first two tasks went fairly quickly. But I…uh…learned a lot while tackling number three.

You can declare an instance variable in an @interface block — either the public interface in the .h file or the private interface in the .m file. You can also declare one in the @implementation block in the .m file. Or all three at once.

You can mix instance variables, properties and auto-synthesized properties. But auto-synthesized properties are so much nicer: less code, less maintenance, less boilerplate typing. Here are 3 tips to help you use properties more effectively.

1. Deal With Read-Only Properties

An auto-synthesized property generates an instance variable with the same name as the property except prefixed with an underscore. This is great because it’s clear whether you’re using the property (self.myprop) or the instance variable (_myprop). Using the bare name (myprop) doesn’t even compile, so you limit the confusion and bugs caused by accidental mistypes.

Auto-synthesized properties create their own getter and setter methods (avoiding some unnecessary typing). You can implement your own getter and/or setter, in which case those methods are not automatically generated. The instance variable (_myprop) is still synthesized.

However, if your property is readonly AND you implement your own getter method, then an instance variable is not synthesized. I suspect this is to allow for a named property that is dynamically calculated. In any case, if you would like an instance variable, you can force it to be synthesized by either:

  1. Using @synthesize myprop = _myprop; in your @implementation, or
  2. Redeclaring the property as readwrite in your private @interface

The second option works like inheritance in that you can redeclare a property as less-restrictive but not the other way. Redeclaring a public readwrite property as privately readonly isn’t possible, though why you’d want to do that remains a mystery.

2. Avoid Instance Variables

It is a Bad Thing™ to have both an auto-synthesized property and an instance variable declared with the same name.

  1. self.myprop = 1 uses the auto-synthesized instance variable
  2. myprop = 1 uses the declared instance variable
  3. _myprop = 1 uses the auto-synthesized instance variable

Since you end up with two instance variables, when you probably intended to have only one, doing this can cause lots of unexpected bugs and problems. Better to avoid declaring instance variables for your synthesized properties.

It is a Worse Thing™ to have two auto-synthesized properties of the same name with one prefixed with an underscore.

    @property myprop;
    @property _myprop;

This causes problems because self._myprop is different than _myprop when you really should have used __myprop, making you start to wonder about your own sanity. This doesn’t seem like it would happen much, but it can if you are importing several .h files, one of which uses underscores in its property names.

So don’t do that. :)

3. Use Instance Variables Correctly

In most cases, it’s best to stick with using the property (self.myprop) in your code. However, it’s important to use the instance variable (_myprop) in two cases:

  1. In your init: methods to avoid possible side-effects if you implement your own setter later, and
  2. In your custom getter and setter methods to avoid infinite recursion (always nice).

Auto-synthesized properties are great. Not quite as awesome as ARC, but close.

21
Oct

How to Launch a Privileged Process on OS X

For security reasons, Apple recommends that GUI applications should never run with the privileges of the root user. GUI applications normally load several types of plugins and input managers automatically. If a malicious plugin was installed then it could cause security problems when the privileged application was launched.

In addition, system services that run with the privileges of the root user (such as launch daemons) need to avoid using certain technology frameworks provided by Apple. These frameworks are not safe to use in a service that runs with the privileges of the root user.

If you need your GUI application to do something that requires root privileges, Apple recommends you split your application into two parts. First, create a GUI that runs as a normal user. Then when you need to do something with root privileges, you launch a separate helper process or tool. Splitting your application avoids security holes while keeping things very easy for your users.

Two Steps

Launching a privileged process is done in two steps:

1) Request authorization. The operating system will ask the user for permission to run a privileged process. The user will need to enter an administrator’s username and password.

#import <Security/Security.h>

OSStatus PreauthorizePrivilegedProcess(AuthorizationRef *authRef) {
    AuthorizationItem item = { kAuthorizationRightExecute, 0, NULL, 0 };
    AuthorizationRights rights = { 1, &item };
    AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize;
    return AuthorizationCreate(&rights, kAuthorizationEmptyEnvironment, flags, authRef);
}

2) Launch the process. If the user authenticates correctly, you can use the authorization reference created above to launch the helper process.

OSStatus LaunchPreauthorizedProcess(AuthorizationRef *authRef, NSString *path) {
    OSStatus status = AuthorizationExecuteWithPrivileges(*authRef, [path UTF8String], kAuthorizationFlagDefaults, NULL, NULL);
    AuthorizationFree(*authRef, kAuthorizationFlagDestroyRights);
    return status;
}

Please note that the authorization reference created in the first function is released in the second function. If you want to launch several privileged processes in a short amount of time, you can comment out this line and and release the reference on your own afterwards.

Some of Apple’s sample code (and other examples) has an extra step where they copy the authorization reference. This is only necessary if you have a previously created authorization reference that you want to add elevated privileges to.

Just One Step

If you don’t need to update your GUI between these two steps, then both actions can be combined into one step.

#import <Security/Security.h>

OSStatus LaunchPrivilegedProcess(NSString *path) {
    AuthorizationRef authRef;
    OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef);
    if (status == errAuthorizationSuccess) {
        status = AuthorizationExecuteWithPrivileges(authRef, [path UTF8String], kAuthorizationFlagDefaults, NULL, NULL);
        AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
    }
    return status;
}

I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.

18
May

How to Print a PDF File Using Cocoa

UPDATE 2014-03-13: @robwithhair created a command-line tool for printing PDF files based on this code which shows some additional options.

Mac OS X is well known for its great support for PDF files. You can create a PDF file from anything you can print. I thought that using Apple’s PDFKit framework would make it easy to program a way to print an existing PDF file. That turned out not to be the case.

Sending a file to a printer using the lp command is easy. However, this approach does not work for PDF files formatted for landscape printing. You can specify landscape orientation, but I wanted a way to detect the orientation automatically.

PDFKit has a PDFView object that has a printWithInfo:autoRotate: method. However, adding a PDFDocument to a PDFView and telling it to print doesn’t work. I eventually stumbled onto the fact that PDFDocumenthas a secret method that makes printing easy. So here is the code:

#import <Quartz/Quartz.h>

- (void)printPDF:(NSURL *)fileURL {

    // Create the print settings.
    NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
    [printInfo setTopMargin:0.0];
    [printInfo setBottomMargin:0.0];
    [printInfo setLeftMargin:0.0];
    [printInfo setRightMargin:0.0];
    [printInfo setHorizontalPagination:NSFitPagination];
    [printInfo setVerticalPagination:NSFitPagination];

    // Create the document reference.
    PDFDocument *pdfDocument = [[[PDFDocument alloc] initWithURL:fileURL] autorelease];

    // Invoke private method.
    // NOTE: Use NSInvocation because one argument is a BOOL type. Alternately, you could declare the method in a category and just call it.
    BOOL autoRotate = YES;
    NSMethodSignature *signature = [PDFDocument instanceMethodSignatureForSelector:@selector(getPrintOperationForPrintInfo:autoRotate:)];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    [invocation setSelector:@selector(getPrintOperationForPrintInfo:autoRotate:)];
    [invocation setArgument:&printInfo atIndex:2];
    [invocation setArgument:&autoRotate atIndex:3];
    [invocation invokeWithTarget:pdfDocument];

    // Grab the returned print operation.
    NSPrintOperation *op = nil;
    [invocation getReturnValue:&op];

    // Run the print operation without showing any dialogs.
    [op setShowsPrintPanel:NO];
    [op setShowsProgressPanel:NO];
    [op runOperation];
}

I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.

27
Aug

How to Create an Alias Programmatically

First, a disclaimer. Apple will warn you not to do this. The only supported way of creating an alias is to use the Finder. If you must do it programmatically, you will be told to use AppleScript. But if AppleScript won’t work for you, and a simple Cocoa method is what you want, read on.

Mozy’s Mac client doesn’t create aliases, but our customers do. We want to make sure our software backs them up correctly. So we added some unit tests to our build process that create aliases and check to see that Mozy handles them correctly.

We first used AppleScript, but ran quickly into two issues:

  1. Our build server runs as the root user, which doesn’t have a UI context. AppleScript doesn’t work without a UI context.

  2. Even running as a normal user, AppleScript cannot access the system temporary files location (/tmp) which is where we wanted to create our aliases.

That’s when the fun began.

I spent quite a bit of time failing to find the right bit of magic to create an alias that functioned properly in Finder. It turns out that an alias is a data structure inside another data structure stored in the resource fork of an empty file. Those structures need to have the correct record types for everything to work.

Having gone to the trouble of figuring this out, I thought I’d share. This code creates an alias for a folder, but it should serve as a good template if you need to create another type.

- (void)makeAliasToFolder:(NSString *)destFolder inFolder:(NSString *)parentFolder withName:(NSString *)name
{
    // Create a resource file for the alias.
    FSRef parentRef;
    CFURLGetFSRef((CFURLRef)[NSURL fileURLWithPath:parentFolder], &parentRef);
    HFSUniStr255 aliasName;
    FSGetHFSUniStrFromString((CFStringRef)name, &aliasName);
    FSRef aliasRef;
    FSCreateResFile(&parentRef, aliasName.length, aliasName.unicode, 0, NULL, &aliasRef, NULL);

    // Construct alias data to write to resource fork.
    FSRef targetRef;
    CFURLGetFSRef((CFURLRef)[NSURL fileURLWithPath:destFolder], &targetRef);
    AliasHandle aliasHandle = NULL;
    FSNewAlias(NULL, &targetRef, &aliasHandle);

    // Add the alias data to the resource fork and close it.
    ResFileRefNum fileReference = FSOpenResFile(&aliasRef, fsRdWrPerm);
    UseResFile(fileReference);
    AddResource((Handle)aliasHandle, 'alis', 0, NULL);
    CloseResFile(fileReference);

    // Update finder info.
    FSCatalogInfo catalogInfo;
    FSGetCatalogInfo(&aliasRef, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL);
    FileInfo *theFileInfo = (FileInfo*)(&catalogInfo.finderInfo);
    theFileInfo->finderFlags |= kIsAlias; // Set the alias bit.
    theFileInfo->finderFlags &= ~kHasBeenInited; // Clear the inited bit to tell Finder to recheck the file.
    theFileInfo->fileType = kContainerFolderAliasType;
    FSSetCatalogInfo(&aliasRef, kFSCatInfoFinderInfo, &catalogInfo);
}

I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.

If you need a complete solution, Nathan Day wrote a nice set of classes called NDAlias. We didn’t want to import 9 classes for just a handful of unit tests.

I later found some of Apple’s sample code from 1999 demonstrating a similar approach. I think our Objective-C example is much easier to use.