With the iOs SDK 4 now public and the advent of iAds just a few days away, I thought we’d celebrate with a tutorial on how to integrate iAd into your iPhone app!
In this tutorial, not only will we show you how to get started with iAd, but we’ll also show you how to deal with some complex issues you may run into along the way such as:
- Supporting both Portrait and Landscape ads in the same app
- Integrating into a Universal app
- Maintaining backwards compatibility with iOs 3.0
- What to do if you are using a UITableViewController!
We’re actually going to start with where we left off in the How To Port an iPhone Application to the iPad and use the universal app we developed in that tutorial in the starting point.
So grab a copy if you haven’t already, and let’s get to adding some iAds!
Base SDK vs. Deployment Target
The first step to use iAd is to make sure our project has the right Base SDK and iPhone OS Deployment Target selected.
For those of you confused about the difference between the Base SDK and Deployment Target (like I was for quite some time!), here’s what they mean:
- The Base SDK is the version of the SDK you are linking against. Your app can use any classes or functions available in the version of the SDK you choose here – as long as they are available on the actual device the code runs on.
- The Deployment Target is the earliest possible version of the SDK your code can run on. This can be an earlier version than the Base SDK – in fact you often want to set it to be earlier to ensure that as many different versions of the OS can run your code as possible!
The tricky bit is what happens when you want to use a class, function, or framework available in one version of the OS if it’s available, but still work on the old version of the OS if it isn’t. We already did some of this in How To Port an iPhone Application to the iPad, and we’ll do even more in this tutorial!
For this tutorial, we want to set things up so that our code can use stuff available in iOS 4.0 (such as iAd), but still run on as many devices as reasonable (3.0+).
So first let’s set iOs 4.0 as the base SDK. To do this, expand the Targets directory, right click on PortMe, and choose “Get Info”. Click the Build tab, make sure “All Configurations” is selected, navigate to Architectures\Base SDK, and change the value to iPhone Device 4.0.
Then, let’s set iPhone OS 3.0 as the iPhone OS Deployment Target. To do this, still in the Target Build tab, navigate to Deployment\iPhone OS Deployment Target, and change the value to iPhone OS 3.0.
You should now be able to compile and run your app (use the iPhone simulator), and try it out on an iPhone 4 simulator. Once you run your code, in the simulator choose Hardware\Device\iPhone OS 4 and re-run your app. The simulator window will look a little different, and say iPhone 4 in the toolbar, so you’ll know it’s working!
Linking Against the iAd Framework
The next thing we need to do is add the iAd framework to the project. You can do this by right clicking on Frameworks, choosing “Add\Existing Frameworks…”, and choosing “iAd.framework”.
The problem is, if that is all we do our code will break on older devices that don’t have the iAd framework.
You can verify this by trying to run your code in the iPad Simulator 3.2 – boom! The app will crash on startup and you’ll see the following error log:
dyld: Library not loaded: /System/Library/Frameworks/iAd.framework/iAd Referenced from: /Users/rwenderlich/Library/Application Support/ iPhone Simulator/3.2/Applications/ 3ACB1BDA-26F6-43A6-84EA-9FB637B8CDCD/PortMe.app/PortMe Reason: image not found
To fix this, we need to weak link against the iAd framework. Expand the Targets directory, right click on PortMe, and choose “Get Info”. Click the Build tab, make sure “All Configurations” is selected, and navigate to Linking\Other Linker Flags. Double click on that entry, click the “+” button, and type “-weak_framework iAd”.
Click OK, and then try your app on the iPad simulator again and viola – it should work!
Preparing our XIB
In this tutorial, we’re going to integrate iAd into both the PortMeGameListController and the PortMeGameDetailsController. However, the integration is a bit easier in the PortMeGameDetailsController because it is a subclass of UIViewController, so we’re going to start there first.
Open up PortMeGameDetailsController.xib. You’ll see that all of the controls are children of a single view:
What we’re going to need to do with iAd is scroll an ad view onto the screen when an ad is available, and shrink the rest of the content to fill the remaining space. As currently designed, this isn’t that easy because all of the controls are direct children of the root view. But there’s an easy way to fix it – we’ll simply move the controls into a subview instead!
The easiest way to do this is to drag another view from the library into the XIB, and change its size to be the same as the existing view’s size (320×416). Then drag the existing view as a subview of the new view. When you’re done, it should look like the following:
Then, control-drag from the File’s Owner to the new view (which is now the root view) to connect it to the view outlet. Save your XIB, and run the project and verify that everything still works OK with the details view (in particularly that orientation resizing works correctly). If all works well, we’re one step closer to integrating iAd!
Simple iAd Integration
Ok, now let’s get to the fun part – integrating iAd!
First, make the following changes to PortMeGameDetailsController:
// In the import section #import "iAd/ADBannerView.h" // Modify the PortMeGameDetailsController interface @interface PortMeGameDetailsController : UIViewController |
We first include the iAd headers and mark the view controller as implementing the ADBannerViewDelegate. This way, we can receive events as ads become available or not.
We then declare a property to keep track of the content view that contains all of the controls (basically the inner UIView). We also declare a variable to keep track of our iAd banner view, and whether or not it’s currently visible.
Note that we declare the iAd banner view as an id variable rather than as a ADBannerView. This is because we want to ensure backwards compatibility all the way to OS 3.0, and the ADBannerView class is only available on 4.0+, so we need to weak link against it.
Before we forget, let’s hook up our content view to the new outlet we just made. Make sure you save PortMeGameDetailsController.h, go back to PortMeGameDetailsController.xib, control-drag from the File’s Owner to the inner (second) UIView, and connect it to the contentView outlet.
Then switch over to PortMeGameDetailsController.m and make the following changes:
// In the synthesize section @synthesize contentView = _contentView; @synthesize adBannerView = _adBannerView; @synthesize adBannerViewIsVisible = _adBannerViewIsVisible; // In the dealloc section self.contentView = nil; self.adBannerView = nil; |
Next, we’re going to add the meat of the code. But there’s a lot of it – so let’s break it down into 6 steps.
1) Add helper functions to get height of iAd banner
- (int)getBannerHeight:(UIDeviceOrientation)orientation { if (UIInterfaceOrientationIsLandscape(orientation)) { return 32; } else { return 50; } } - (int)getBannerHeight { return [self getBannerHeight:[UIDevice currentDevice].orientation]; } |
There are several places in the rest of the code where we’re going to want to know how large the banner view should be given a particular orientation. Currently iAds have two possible sizes: 320×50 for landscape, or 480×32 for portrait. So we simply retrieve the proper height based on the passed in orientation.
2) Add helper function to create the iAd view
- (void)createAdBannerView { Class classAdBannerView = NSClassFromString(@"ADBannerView"); if (classAdBannerView != nil) { self.adBannerView = [[[classAdBannerView alloc] initWithFrame:CGRectZero] autorelease]; [_adBannerView setRequiredContentSizeIdentifiers:[NSSet setWithObjects: ADBannerContentSizeIdentifier320x50, ADBannerContentSizeIdentifier480x32, nil]]; if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) { [_adBannerView setCurrentContentSizeIdentifier: ADBannerContentSizeIdentifier480x32]; } else { [_adBannerView setCurrentContentSizeIdentifier: ADBannerContentSizeIdentifier320x50]; } [_adBannerView setFrame:CGRectOffset([_adBannerView frame], 0, -[self getBannerHeight])]; [_adBannerView setDelegate:self]; [self.view addSubview:_adBannerView]; } } |