Monday, August 18, 2014

[iOS] Solving Backwards Compatibility Issues

Solving Backwards Compatibility Issues

Once you have identified the backwards compatibility issues in your app, the next step is to figure out how to fix them. Each new version of iOS introduces new frameworks, classes, methods, constants and enumeration values — and there’s a specific strategy to deal with each of these.
Unsupported frameworks
Linking against a framework that doesn’t exist in your deployment target will just cause the dynamic linker to crash your app. To solve this, you mark unsupported frameworks as “Optional” in your project settings.
To do this, select your project under the “Targets” section, and open up Build Phases -> Link Binary With Libraries. Next to each framework you can specify either Required or Optional. Selecting Optional will weak link the framework and solve your compatibility issue.
Weakly linking a library in Xcode
Note: You can read more about weak linking in Apple’s Framework Programming Guide.
Unsupported classes
Sometimes you want to use a class that exists in your base SDK, but not in your deployment target. To do this you need to check the availability of this class at runtime to avoid crashing your app. It crashes because this is what the Objetive-C runtime will do if you try to use a class that doesn’t exist. As of iOS 4.2, classes are weakly linked so you can use the +class method to perform the runtime check. For example:
if ([SLComposeViewController class]) {
    //Safe to use SLComposeViewController 
} else {
    //Fail gracefully
}
Unsupported methods
Similarly, if you’re using a method in your base SDK that doesn’t exist in your deployment target, you can avoid nasty crashes by using a little introspection.
The methods -respondsToSelector: and +instancesRespondToSelector: will both do the trick, as shown in the code examples below:
if ([self.image respondsToSelector:@selector(resizableImageWithCapInsets:resizingMode:)]) {
    //Safe to use this way of creating resizable images
} else {
    //Fail gracefully
}
The same goes for verifying the existence of class methods, except you call respondsToSelector: on the class itself, like so:
if ([UIView respondsToSelector:@selector(requiresConstraintBasedLayout)]) {
    //Safe to use this method
} else {
    //Fail gracefully
}
Note: If you want to check for the presence of a certain property on a class, then you can do so by testing that instances respond to the property getter or setter.
For example, to check if UILabel can take an attributed string via its attributedText property (new in iOS 6), perform introspection against the implicit setter method @selector(setAttributedText).
Unsupported constants/C functions
Sometimes a constant value is the missing piece of the deployment target; it usually takes the form of an extern NSString * or a C function. In this case, you can perform a runtime check against NULL to determine if it exists.
For example, the C function ABAddressBookCreateWithOptions(...) was introduced in iOS 6 but can still live safely in your iOS 5 app like so:
if (ABAddressBookCreateWithOptions != NULL) {
    //Safe to use
}
else {
    //Fail gracefully
}
The same approach applies to constants. For example, iOS 4.0 introduced multitasking support. If you wanted to check for the existence of UIApplicationWillEnterForegroundNotification, you would simply validate it as so:
if (&UIApplicationWillEnterForegroundNotification) {
    //Safe to assume multitasking support
}
else {
    //Fail gracefully
}
For further proof, take a look at UIApplication.h in Xcode. You’ll see that UIApplicationWillEnterForegroundNotification is simply an extern NSString * declared at the bottom of the document.
When your application is loaded into memory, these strings are initialized and stay resident in memory. The & operator gets the string’s memory address. If the memory address is not equal to nil, then the NSString is available, otherwise it’s not available and your code will have to work around that fact.
Note: The mechanism that makes this work is weak linking, which was described earlier. When a binary is loaded the dynamic linker replaces in the app binary any addresses of things (functions, constants, etc) in dynamically loaded libraries. If it’s weakly linked then if the symbol in the library is not found then the address is set to NULL.
Unsupported enumeration values
Checking for the existence of enumeration or bit mask values — the kind that you would find inside an NS_ENUM or NS_OPTIONS declaration — is incredibly difficult to check at runtime. Why?
Under the hood, an enumeration is just a method of giving names to numeric constants. An enum is replaced with an int when compiled, which always exists!
If you’re faced with the situation of needing to see if a certain enumeration exists, all you can do is either explicitly check the OS version (which isn’t recommended) or alternatively check against another API element that was introduced at the same time as the new enumeration value.
Note: Whichever way you do this, be sure to add adequate comments to any such code and consider wrapping the code in a dedicated compatibility helper.
Explicit iOS version checking
You should generally stay away from checking explicit iOS versions but there are specific situations where it’s unavoidable. For example, if you need to account for a bugfix in a previously available method, use the following line of code to return the OS version:
NSString *osVersion = [[UIDevice currentDevice] systemVersion];
You can use NSString’s compare:options: method, passing NSNumericSearch as the options to compare OS versions. This takes into account the fact that 6.1.1 is greater than 6.1.0. If you converted them to floats first and then used standard arithmetic things would go wrong because both would parse as the number 6.1.
Note: You can find out more about this topic in Apple’s SDK Compatibility Guide.

Tuesday, August 5, 2014

[iOS]Ojbective C async call sample

1. Define block method:
typedef void (^MyTestBlock)(void);

MyTestBlock myBlock = ^void() {
    // your implementation
}];

2. Consumer
[[NSNotificationCenter defaultCenter] addObserverForName:@"key" object:nil queue:nil usingBlock:^(NSNotification *note) {
            myBlock();
        }];

3. Trigger
[[NSNotificationCenter defaultCenter] postNotificationName:@"key" object:nil userInfo:nil];

Friday, August 1, 2014

[iOS] Error: Synthesis of weak property only allowed in arc or gc mode

Go to Targets -> Build Phases -> Compile Sources, find the file

double click it to add: -fobjc-arc

and rebuild again.