How to #define based on iOS version?

Undo picture Undo · Jul 25, 2013 · Viewed 12.4k times · Source

I have a Constants.h file in my app, where I #define app-wide things for easy access later. I'm having a hard time, though, #defineing based on iOS version. Here's what I've tried:

#ifdef __IPHONE_7_0

#define kHamburgerImage [UIImage imageNamed:@"reveal_menu_icon_portrait_ios7.png"];

#else

#define kHamburgerImage [UIImage imageNamed:@"reveal_menu_icon_portrait.png"];

#endif

Just because it says iOS 7 in there doesn't mean this is under NDA, O closers!

Which works fine - for iOS 7. When I run my app on iOS 6, however, the #define is still the iOS 7 one - it seems as though the #ifdef is never taken into account.

What can I do to fix this?

Answer

rmaddy picture rmaddy · Jul 25, 2013

Instead of using compile-time checks, you need runtime checks. This means you can't use #define. I suggest using a static variable that is initialized at runtime based on the version of iOS. Below is an example if you only need the value in a single file.

Some .m file:

static UIImage *kHamburgerImage = nil;

+ (void)initialize {
    // This assumes you only support iOS 6 and later - adjust as needed
    if ([[UIDevice currentDevice].systemVersion hasPrefix:@"6"]) {
        kHamburgerImage = [UIImage imageNamed:@"reveal_menu_icon_portrait.png"];
    } else {
        kHamburgerImage = [UIImage imageNamed:@"reveal_menu_icon_portrait_ios7.png"];
    }
}

Edit: Since these need to be globals, you should do this:

Constants.h:

extern UIImage *kHamburgerImage;

@interface Constants
@end

Constants.m:

UIImage *kHamburgerImage = nil;

@implementation Constants

+ (void)initialize {
    // This assumes you only support iOS 6 and later - adjust as needed
    if ([[UIDevice currentDevice].systemVersion hasPrefix:@"6"]) {
        kHamburgerImage = [UIImage imageNamed:@"reveal_menu_icon_portrait.png"];
    } else {
        kHamburgerImage = [UIImage imageNamed:@"reveal_menu_icon_portrait_ios7.png"];
    }
}

@end

But this suffers from a problem. Unless you take specific steps, accessing these globals could result in nil pointers. They only get initialized if the class is actually referenced. I suggest that as the first line of your application:didFinishLaunchingWithOptions: you do:

[Constants class];

This ensures the initializer is called and the constants are setup before you use them anywhere else in your code.