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

Friday, April 30, 2010

The iPhone OS Architecture - Part 1 (The very basics)

iPhone OS is really amazing! So, I think it's a good idea for us to start studying how iPhone OS works. iPhone OS applications are mostly based on Objective-C programming, which is part of Cocoa Touch architecture. Cocoa Touch? Below, let's see an introduction overview of this Apple architecture for the iPhone.

When I started programming applications for the iPhone, I particularly found it completely different from everything I already coded within my 15 years of coding experience. At the beginning, I admit it was weird... but after studying and acquiring some experience on that, I really found it amazing.

The curious thing is that it's based on NeXTStep Objective-C, from 1982! It's now ressurrected for the iPhone.

First of all, you use Xcode and Interface Builder in parallel. Cocoa Touch is based on MVC (Model - View - Controller) architecture. I mean, you code the Controller in Objective-C and, almost at the same time, use Interface Builder to create the UIKit visual objects and link them to your visual object events into methods, instance variables (outlets) you coded for the Controller. It's a different concept and that's how it works on Cocoa/Cocoa Touch. We are used to create a visual object, click twice on it, and then code its event. Cocoa works different at this point.

Let's go through the brief overview of iPhone OS... First, let's talk about Mac OS X, which was rewritten and recoded entirely in Cocoa and Objective-C. By the way, according to Apple, the Snow Leopard version (10.6) was completely rewritten from its antecessor Leopard (10.5) and the code in 10.6 was reduced to less than a half of previous 10.5 [1]. For the iPhone OS, Apple introduced almost all same benefits they did for the Snow Leopard and this really allows the iPhone OS to run faster and support complex applications, as well. Here is iPhone OS architecture:



The iPhone OS architecture is divided into four important layers [2]:

  • Core OS: includes OS X Kernel, Power Management, Mach 3.0, Keychain, BSD, Certificates, Sockets, File System, Security and Bonjour
  • Core Services: includes Collections, Core Location, Address Book, Net Services, Networking, Threading, File Access, OS Preferences, SQLite and URL utilities
  • Media: includes Core Audio, JPG/PNG/TIFF engines, OpenAL, PDF engine, Audio Mixing, Quartz (2D), Audio Recording, Core Animation, Video Playback and OpenGL ES
  • Cocoa Touch: includes Multi-Touch Events, Alerts, Multi-Touch Controls, Web Views, Accelerometer, People Picker, View Hierarchy, Image Picker, Localization, Controllers

Based on above architecture, we will code in the Cocoa Touch layer in order to access all other layers. For example, if you want your iPhone application to detect the end user GPS coordinates, you will add the Xcode Core Location Framework into your Xcode project. So, you will have Cocoa Touch layer accessing Core Services layer. And so on.

Bibliography:

[1] APPLE, Inc. Mac OS X: Enhancements and Refinements. http://www.apple.com/macosx/refinements/enhancements-refinements.html#installation

[2] APPLE, Inc. iPhone OS Overview. http://developer.apple.com/iphone/library/referencelibrary/GettingStarted/URL_iPhone_OS_Overview