The mobile advertising buzz lately is about Apple’s iAd. But iAd isn’t yet available in all countries. If you’re like me and have about half of your users outside of the US you will still need ad services that support the other countries. Using an ad mediation layer service like AdWhirl is still a practical solution, and it fully supports iAd and many other ad networks.

Background on iOS mobile advertising

One of the benefits of using an ad mediation layer is that you can serve ads from multiple networks at the same time. Two years ago there were a few ad networks that had iPhone SDKs which developers could use to publish ads inside their apps. For example, AdMob was and is a big presence in the iPhone ad network world. So big, in fact, that Google acquired AdMob earlier this year for an estimate $750 million. AdMob has an easy-to-use self service feature that lets anyone create their own banner ads, which are then published on iPhone app screens. Only apps that have the AdMob SDK integrated into them will display ads, and the developers of those apps earn a percentage of the revenues generated.

Ad revenue is measured in eCPM which means “cost per thousand views.” This number is derived from factors such as how much the advertiser is paying per click, how many clicks, etc. Since ad networks generally provide a mix of ads from a wide variety of advertisers, you use the eCPM as a barometer to indicate how well your iPhone app is generating ad revenue. In other words, it’s not really an indicator of how well the ad network or any particular advertiser is paying. So if your app displays 10,000 ads (called “impressions”) and you earn $5.00 then your eCPM is  $0.50, or $5.00 (cost) divided by 10 (thousand views). It doesn’t matter whether the advertiser is paying per click (CPC or cost per click) or per impression (CPM or cost per 1000 impressions) because you only care about the bottom line, the effective CPM or eCPM.

Aside from a higher eCPM, app developers are eager for what’s called a high fill rate. If each time your app requests an ad from the network it gets one, that’s a 100% fill rate. That’s great but it never happens. (Not entirely true: Google AdSense may return a PSA if no ad is available, so it appears to have 100% fill rate.) It’s not uncommon for fill rate to be very low, often under 50%. Why’s that? There’s only so much demand for the iPhone ad “inventory;” that is, there are more apps requesting ads than there are paying advertisers, at any particular moment. Due to the requirements of advertisers and the need to resist user fatigue (seeing the same ads over and over again, etc.) your ad requests might go unfilled. Refreshing the ads less often can help with this, but more on that later.

At some point the ad networks started allowing custom ads (or house ads) to make up for the unfilled requests. If you wanted to promote some of your other apps this was any easy way to allocate a percentage of requests to your own ‘free’ ads (you aren’t paying for those banners). Or you could promote other developer’s apps, which I did on a large scale last Christmas. Of course some of us started to find ways to manually combine different ad networks inside a single app, and it didn’t take long for folks like TapjoyMobclixAdWhirl (née AdRollo), and Burstly to form new businesses around the idea of server-based ad network mediation. AdWhirl was possibly first, formed in April 2009 by two guys who successfully filled a niche and were later acquired by AdMob. Their system allows you to build your app with the AdWhirl SDK and deploy it, and control ad requests from a web panel after the app has already been released. All the mediation services operate the same way; they act as middlemen to optimize your ad requests based on priorities you set.

I’ve been using AdWhirl since June 2009 with a good deal of success, even though there were some trying times just before the AdMob acquisition. It’s open source and the server code is available if you want to run your own system. I’m not going to focus on the pros and cons of AdWhirl versus Mobclix, etc.; that’s been blogged about ad nauseam. My advice is simply that your results will vary from what you read about elsewhere. Experiment and find what works best for you. My $1.00 eCPM might sound good to you, but it’s not as good as somebody else’s $2.50 eCPM. However, if that $2.50 eCPM is only for 10,000 impressions I can’t expect the same amount for 100,000 impressions, can I?

The optimization

When setting up your application on the AdWhirl web panel, you can adjust the rate of refresh and transition animation, and other parameters. Typical refresh rates of 30 to 60 seconds are best practice. But you can’t set the refresh rate for a particular network, and this could be an issue. Say you get a call from your rep at VideoEgg or MdotM or iAd (ad networks) offering a higher fill or rate due to promotion this week or next. Yes, you can increase the percentage of requests you make to their networks in the web panel. But you might still suffer from poor fill rate or otherwise can’t afford to allocate all your requests to a particular ad network. Here’s how I solved that problem on AdWhirl, thought I’m sure this technique will work for most mediation layers.

I should point out that this method is primarily effective if your app displays a banner all the time. If you use interstitial ads (show up temporarily between pages or levels of a game, etc.) this won’t be as effective. First you need to modify your code slightly to allow the “ad received” callback to recognize a particularly interesting ad and “park” on that ad. Here’s what this looks like:

Notice this relies on your UIApplication delegate object having a “networkNames” property. If that property exists and is an NSArray that contains the name of the most recently received ad network, it will pause the refresh timer. Voilà, your ads will not refresh until you resume the refresh timer, usually in response to user action such as flipping to a new view, or navigating to a different screen, etc. This means that when you detect an AdSense ad that’s currently earning higher eCPM, for example, you can park it there and hopefully generate higher earnings.

But where does this “networkNames” property come from? It’s set up in my UIApplication delegate, but you can put it anywhere you like. Since I want this to be dynamic, it is fetched from a server at launch, which means it can be modified at my convenience. I’ve created a property list file and use an asynchronous NSURLConnection to load it if the network is available. If there’s no network, there’s no ads anyway, so no big deal. The content of the file is pretty trivial, just list the names of the ad networks to ‘park’ on, like this:

You can get the actual names of the ad networks from the actual AdWhirl (or other) implementation by just observing the mostRecentNetworkName as the callback is triggered. Though the plist file is a perfect candidate to load with the dictionaryWithContentsOfURL: method, that is synchronous and will block the application while loading. I suppose you could use an NSOperationQueue or separate thread, but the NSURLConnection is well suited to handle this. You can take advantage of the NSPropertyListSerialization class to convert the data into a property list, which is a dictionary. Here’s the code to get the configuration file and parse the results. Also note the retry on failure and after a fixed period even if it succeeds. This ensures that you can update the config file and users will have it without relaunching the app.

getConfig   

I’m also posting my own kConfigChangedNotification to any parts of the app that care to notice such things. I’ll most likely use this for other features, that code is not shown here.

Conclusion

So does this work? In my case, yes. When Google AdSense for Mobile Apps (AFMA) started last fall, there was a requirement to pause ad refreshing. AFMA does its own internal ad refreshing and you don’t want to mess with that, nor violate the publisher agreement you signed. However that has since been relaxed, and you can refresh the ads every 180 seconds. Plus the latest AdWhirl SDK treats AdSense like all the others and would normally refresh it at the interval you specified on the web panel. Given that the AdSense ads are one of the highest paying (again, in my limited experience, your results will vary) I want to pause every time I get an AdSense ad. Before I did this, and with the normal AdWhirl rotation, AdSense ad revenue dipped because the ads weren’t visible long enough for the user to engage. The typical clickthrough rate went from ~ 3% to ~1%. After putting this mechanism in place the clickthrough rate is back up around 3%. That in turn translates to a higher eCPM and better revenues.

This post is part of iDevBlogADay, a group of indie iOS development blogs featuring two posts per day. You can keep up with iDevBlogADay through the web siteRSS feed, or Twitter.