Sunday, December 12, 2010

Resolving runtime issues with AirPrint and iOS backward compatibility with weak linking

Hi,

Maybe you are running into the same issue. So, I considered to write this topic. All began when I was testing the new AirPrint iOS 4.2 feature with my existing applications. In my case, Implementing AirPrint feature into your application is pretty simple. You just instantiate the Airprint UIKit objects, based on Apple's sample [1] and you are all set. For instance, to print a webview content (that was my case), just add this method into your View Controller .m code and let iOS 4.2 do the rest for you, I mean, show you the window to choose number os copies, select printer etc:

- (void)printWebPage {

if ([UIPrintInteractionController class]) {

UIPrintInteractionController *controller = [UIPrintInteractionController sharedPrintController];

if(!controller){

NSLog(@"Couldn't get shared UIPrintInteractionController!");

return;

}

void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) =

^(UIPrintInteractionController *printController, BOOL completed, NSError *error) {

if(!completed && error){

NSLog(@"FAILED! due to error in domain %@ with error code %u",

error.domain, error.code);

}

};

if ([UIPrintInfo class]) {

UIPrintInfo *printInfo = [UIPrintInfo printInfo];

printInfo.outputType = UIPrintInfoOutputGeneral;

printInfo.jobName = [Singleton sharedInstance].globalBlogTitle;

printInfo.duplex = UIPrintInfoDuplexLongEdge;

controller.printInfo = printInfo;

controller.showsPageRange = YES;

}

if ([UIViewPrintFormatter class]) {

UIViewPrintFormatter *viewFormatter = [self.blogDetailsWebView viewPrintFormatter];

viewFormatter.startPage = 0;

controller.printFormatter = viewFormatter;

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {

[controller presentFromBarButtonItem:self.navigationItem.rightBarButtonItem animated:YES completionHandler:completionHandler];

} else

[controller presentAnimated:YES completionHandler:completionHandler];

}

}

}



Now, take a look at above code. I need to preview if user has an iOS version smaller than 4.2 and avoid crashes or bogus references in my application. iOS 4.1 and previous don't know what UIPrintInteractionController, UIViewPrintFormatter and UIPrintInfo mean. So, I need to test for these classes existence and instruct my code where to go in this case [2].

When you compile the application, compilation is successful and runs fine in iOS 4.2 as well. But try running it in a previous iOS version device or simulator (e.g: iPad 3.2) and your application simply crashes on start. Look at Run - Console Xcode menu and you will face this unpleasant error:

[Session started at 2010-12-12 08:53:27 -0200.]

dyld: Symbol not found: _OBJC_CLASS_$_UIPrintInfo

Referenced from: /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/3.2/Applications/3ABCAD78-5788-4FFA-8F12-B5AD805E1AC5/MyApp.app/MyApp

Expected in: /Developer/Platforms/iPhoneSimulator.platform/Developer/

SDKs/iPhoneSimulator3.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit

in /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/3.2/Applications/3ABCAD78-5788-4FFA-8F12-B5AD805E1AC5/MyApp.app/MyApp


Something is missing... a Weak Linking to your application is missing. iOS 4.2 introduces the weak linking technique in order to preserve compatibility to older iOS versions [3]. That means, if you introduce Airprint feature into your application, that means old iOS users can continue using the application and won't take advantage of Airprint only. So, that's exactly what we need here.

In a straight-forward way, here's what to do to get rid of above error, according to Apple's document [4]:

  • The base SDK for your Xcode project must be iOS 4.2 or newer. The name for this setting in the build settings editor is SDKROOT (Base SDK).
  • The deployment target for your project must be iOS 3.1 or newer. The name for this setting is MACOSX_DEPLOYMENT_TARGET (Mac OS X Deployment Target).
  • The compiler for your project must be the LLVM-GCC 4.2 compiler or newer, or the LLVM compiler (Clang) 1.5 or newer. The name for this setting is GCC_VERSION (C/C++ Compiler Version).
  • You must ensure that any frameworks not available in your project’s deployment target are weakly linked, rather than required
The latest item above is achieved this way:

- Within your Xcode treevieew, expand Targets > , right-click it and choose Get Info
- In the General tab, you will see something like this:

- Right-click the "Required" text under Type column for each framework and change it all to "Weak"
- Recompile your application and test it on iPhone/iPad devices or simulators with previous iOS versions than 4.2 - now it will all work fine.

Have a nice day!

Reference:

[1] APPLE, Inc. Drawing and Printing Guide for iOS.
[2] APPLE, Inc. SDK Compatibility Guide
[3] APPLE, Inc. What's new in iOS
[4] APPLE, Inc. SDK Compatibility Guide

6 comments:

  1. Thank you SO MUCH! I couldn't figure out how to fix this and you're the only person on the internet who has posted about this error! Thanks again!

    ReplyDelete
  2. Hi guys, you're welcome. Glad it helped you.

    Whenever I find these glitches in iOS programming and find a solution, I will publish them :-)

    Marcelo

    ReplyDelete
  3. Thank you for taking the time to write this up.

    ReplyDelete
  4. Thanks for the writeup. Just used this to add printing from ChildBrowser in PhoneGap.

    ReplyDelete
  5. can u tell me how to incorporate the printer plugin... I have printer plugin .. Tell me the steps followed.
    Thanks in advance

    ReplyDelete