Home > cocoa, iphone > Importing UIKit vs. Cocoa

Importing UIKit vs. Cocoa

August 20th, 2009 Leave a comment Go to comments

I work on a lot of projects that share significant code between iPhone and Mac versions. This is the beauty of Cocoa. While working on these projects, I’ve bumped into this idiom many times:

#ifdef TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#import <Cocoa/Cocoa.h>
#endif

This is almost never correct, and almost always means that someone imported Cocoa.h into a model class. Model classes should never rely on UIKit or Cocoa. They should just import Foundation.h.

There is one interesting exception that we’ve run into: NSImage versus UIImage. These are really model classes, but they’re part of AppKit and UIKit. They have very similar interfaces, so in most code you should be able to interchange them and keep everything portable. What to do? XXImage.h

#ifdef TARGET_OS_IPHONE
#import <UIKit/UIImage.h>
typedef UIImage XXImage;
#else
#import <AppKit/NSImage.h>
typedef NSImage XXImage;
#endif

You can now import XXimage.h into your model classes and use XXImage anywhere you would normally use NSImage or UIImage (as long as you’re using them in a portable way). In your UI (platform) code, you can still use NSImage or UIImage normally; no need to use XXImage everywhere, since the types are compatible.

Or you could go to CGImages exclusively, but what a pain if you don’t need them already.

EDIT Sep 24, 2009: In light of a Radar I’ve recently opened (radar://7224053), I need to amend the above. It’s better to use #define rather than typedef. I strongly prefer typedef, but there’s a bizarre bug that it bumps into. Typedef confuses valueForKey: for some reason. You can use the following code to determine if the bug has been fixed yet:


import

// Swapping #define for typedef makes this work typedef NSString MYString; //#define MYString NSString @interface MYObject : NSObject {} - (MYString *)string; @end @implementation MYObject - (MYString *)string { return @"a string"; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; MYObject *object = [[[MYObject alloc] init] autorelease]; MYString *string = [object valueForKey:@"string"]; // Fails w/ "not KVC-compliant for key string" // MYString *string = [object string]; // Works NSLog(@"image = %@", string); [pool drain]; return 0; }
  • Share/Bookmark
Categories: cocoa, iphone Tags:
  1. September 24th, 2009 at 13:17 | #1

    Good tidbit. FYI, there’s a typo of “typdef UIImage” instead of “typedef UIImage” in the second code block.

  2. September 24th, 2009 at 14:01 | #2

    Thanks. Fixed.

  3. September 25th, 2009 at 01:07 | #3

    Also, to note: I sometimes run into issues with using #ifdef instead just a plain #if. In this case TARGET_IPHONE_OS is defined as 0 in TargetConditionals.h which means depending on a couple things the wrong compiler directive might be activated. Now, off to C4[3].