Wednesday, March 23, 2011

Writing conditional code for different iOS versions

If you are going to use a function that is available only in later iOS versions, you will need to include conditionals. There are two types of conditionals - compile time and runtime conditionals. The purpose of conditionals is two-fold:

1. You need to use compile-time conditionals to prevent compile problems when running in older versions of the simulator.
2. You need the runtime check if you are going to build on a later version of simulator but are going to install on a device with an older version of iOS.

Before I dive into the details, let me give you an instance where this came in handy for me.

I needed to do an animation for resizing a view. I considered using the [UIView beginAnimation] and [UIView commitAnimation] functions. But when I consulted the docs, it said that the use of these functions are not recommended in iOS versions 4.0 and above.

Instead, they recommended the use of block animation functions (eg:- [UIView animateWithDuration]). So I wrote code for animating using the animateWithDuration function. I was able to successfully compile the program because I am using the 4.3 version simulator. The problem occurred when I tried to run it on an iOS 3.2 iPad. The app crashed when the animation call fired. So then I tried using a compilation conditional for the same as follows:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
      // code with animateWithDuration
#else
     // code with beginAnimation
#endif

I still got the crash because, since I am building on iOS v4.3, the animateWithDuration function still got compiled into the code. So that's when I finally used the run time check macros.

Here are the steps:

Step 1: Define a macro

#define IF_IOS4_OR_GREATER(...) \
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
{ \
__VA_ARGS__ \
}

I called my macro IF_IOS4_OR_GREATER because it checks if the runtime version is greater than 4.0 but you can call it anything you like.

Step 2: Call the macro where the runtime check is required. Instead of calling the macro directly in the source code, I decided to confine it to a specific function. I decided to call it isCompatibleOSVersion.

+ (BOOL)isCompatibleOSVersion{
    IF_IOS4_OR_GREATER
    (
     return YES;
     )
   
    return NO;
}

Step 3: Next all that is remaining is to do your processing based on this function call.

if ([self isCompatibleOSVersion]){
        // code with animateWithDuration 
}
else{
    // code with beginAnimation
}

Point to note:
If we want to include something only in OS versions prior to a a specific version, then we don't need the conditional compilation (since we still want the code to appear when compiled in a later version). In this case, only a runtime check is required.

Moral of the story:
Use conditionals wisely.




1 comment:

  1. why not just check for the existence of the new method using respondsToSelector: ?

    ReplyDelete