bekkou68 の日記

Gogengo! や IT 技術など。

iOS で mopub SDK を組み込んでバナー広告・全画面広告を表示する手順【Google AdMob】【Millennial Media】【iOS5 iOS6】【ARC 対応】

はじめに

mopub SDK を組み込む機会があったのでエントリにまとめます。公式以外のドキュメントが少なくてだいぶ苦労しました。

In English is here.

mopub とは

mopub とは広告を総合管理してくれるシステムです。一度 SDK を埋め込めば、管理画面から広告媒体・広告を出す頻度などを変えられます。また、CPM 等を分析できます。mopub SDK を組み込むことで、複数の広告主からの広告を配信できます。組み込むメリットの一つが SDK の差異を吸収してくれることです。広告の SDK は各社でバラバラなので、比例した労力がかかります。ただし、Google AdMob や Millennial Media など、別途 SDK を組み込む必要があるアドネットワークもあります。

ゴール

Objective-C のアプリが以下の状態になっていることをこのエントリのゴールとします。

  • 標準の mopub SDK が組み込まれ、バナー広告・全画面広告が表示される
  • Google AdMob SDK が組み込まれ、バナー広告が表示される
  • Millennial Media SDK が組み込まれ、全画面広告が表示される

なお、ゴールを満たしているプロジェクトを用意しました。ご参考ください。

前提

以下の条件を前提とします。

  • mopub の管理画面からバナー広告と全画面広告それぞれが登録済み
  • プロジェクトが Single View Application で作成済み
  • 初期表示およびバナー広告を表示するための画面の Controller名は TopViewController
  • 全画面広告を表示するための画面の Controller名は InterstitialViewController

方針

  • Deployment Target は 5.1
  • 極力 cocoapods をつかって作業を進める
  • ARC 対応のアプリに組み込む

下準備: mopub SDK をインストール

最新版の mopub SDK 1.12.1.0 を組み込みます。 cocoapods の使い方はこちらのエントリをご参考ください。

Podfile に以下の内容で記述します。【注意】このエントリを公開した時点では pod spec ファイルは pull request の状態です。前述した URL から適宜とり込んでください。マージされました(2013/5/25 追記)。
なお現在は MoPubClient が active な spec のようです(2014/1/8 追記)。

platform :ios
pod 'MoPubSDK', '1.12.1.0'

インストールします。

$ pod install

また、テスト用のプロジェクトが mopub より用意されており、バナー広告と全画面広告をそれぞれ確認できます。テスト用の広告ID も同梱されています。clone して動作を確認すると雰囲気がつかめると思います。また、のちほどこのプロジェクトを使うのでこのタイミングで clone しておきましょう。

$ git clone git://github.com/mopub/mopub-client.git

mopub-client/MoPubiOS/SimpleAdsDemo/SimpleAds.xcodeproj を XCode で開けば OK です。

バナー広告を表示する

公式ドキュメントのとおりに TopViewController.m を実装します。

GitHub の diff はこちら

diff --git a/mopub_spike/TopViewController.h b/mopub_spike/TopViewController.h
index f51d4a3..9d1b8b5 100644
--- a/mopub_spike/TopViewController.h
+++ b/mopub_spike/TopViewController.h
@@ -7,8 +7,8 @@
 //

 #import <UIKit/UIKit.h>
+#import "MPAdView.h"

-@interface TopViewController : UIViewController
+@interface TopViewController : UIViewController <MPAdViewDelegate>

 @end
-
\ No newline at end of file
diff --git a/mopub_spike/TopViewController.m b/mopub_spike/TopViewController.m
index 2d19850..554de97 100644
--- a/mopub_spike/TopViewController.m
+++ b/mopub_spike/TopViewController.m
@@ -8,8 +8,13 @@

 #import "TopViewController.h"

+// The below is test ad unit ID. Fill in by your's.
+#define BANNER_AD_UNIT_ID @"agltb3B1Yi1pbmNyDAsSBFNpdGUYkaoMDA"
+
 @interface TopViewController ()

+@property (strong, nonatomic) MPAdView *adView;
+
 @end

 @implementation TopViewController
@@ -17,7 +22,14 @@
 - (void)viewDidLoad
 {
     [super viewDidLoad];
- // Do any additional setup after loading the view, typically from a nib.
+
+    MPAdView *adView = [[MPAdView alloc] initWithAdUnitId:BANNER_AD_UNIT_ID size:MOPUB_BANNER_SIZE];
+    adView.frame = CGRectMake(0, VIEW_HEIGHT - 50, 320, 50);
+    adView.delegate = self;
+    [adView loadAd];
+    _adView = adView;
+
+    [self.view addSubview:adView];
 }

 - (void)didReceiveMemoryWarning
@@ -26,4 +38,9 @@
     // Dispose of any resources that can be recreated.
 }

+// REQUIRED: Some of ad networks call this method.
+- (UIViewController *)viewControllerForPresentingModalView {
+    return self;
+}
+
 @end
diff --git a/mopub_spike/mopub_spike-Prefix.pch b/mopub_spike/mopub_spike-Prefix.pch
index 64731a3..0d9f26e 100644
--- a/mopub_spike/mopub_spike-Prefix.pch
+++ b/mopub_spike/mopub_spike-Prefix.pch
@@ -12,3 +12,5 @@
     #import <UIKit/UIKit.h>
     #import <Foundation/Foundation.h>
 #endif
+
+#define VIEW_HEIGHT [[UIScreen mainScreen] applicationFrame].size.height

シミュレータを起動してバナー広告が出ることが確認できると思います。

f:id:bekkou68:20130523233238p:plain

広告をタップするとアプリ内で WebView が立ち上がり広告が表示されます。

f:id:bekkou68:20130523233233p:plain

WebView で広告を表示して OpenURL を行う方法だと Safari を起動します。そのわずらわしさがないのも mopub SDK の魅力の一つだと思います。

全面広告

TopViewController から InterstitialViewController へ遷移した後に全面広告を表示させます。TopViewController に適当にボタンを配置して遷移するようにします。サンプルプロジェクトのコミットログをてきぎ参照してください。

それが終わったら全画面広告の実装を以下のようにおこないます。

以下、こちらのコミットより抜粋しました。

diff --git a/mopub_spike/InterstitialViewController.h b/mopub_spike/InterstitialViewController.h
index 9eec816..83ef891 100644
--- a/mopub_spike/InterstitialViewController.h
+++ b/mopub_spike/InterstitialViewController.h
@@ -7,7 +7,8 @@
 //

 #import <UIKit/UIKit.h>
+#import "MPInterstitialAdController.h"

-@interface InterstitialViewController : UIViewController
+@interface InterstitialViewController : UIViewController <MPInterstitialAdControllerDelegate>

 @end
diff --git a/mopub_spike/InterstitialViewController.m b/mopub_spike/InterstitialViewController.m
index 7f62ebd..44e457b 100644
--- a/mopub_spike/InterstitialViewController.m
+++ b/mopub_spike/InterstitialViewController.m
@@ -8,8 +8,13 @@

 #import "InterstitialViewController.h"

+// The below is test ad unit ID. Fill in by your's.
+#define INTERSTITIAL_AD_UNIT_ID @"agltb3B1Yi1pbmNyDAsSBFNpdGUYsckMDA"
+
 @interface InterstitialViewController ()

+@property (nonatomic, retain) MPInterstitialAdController *interstitialAdController;
+
 @end


 @implementation InterstitialViewController
@@ -27,6 +32,10 @@
 {
     [super viewDidLoad];
     // Do any additional setup after loading the view from its nib.
+
+    _interstitialAdController = [MPInterstitialAdController interstitialAdControllerForAdUnitId:INTERSTITIAL_AD_UNIT_ID];
+    _interstitialAdController.delegate = self;
+    [_interstitialAdController loadAd];
 }

 - (void)didReceiveMemoryWarning
@@ -35,4 +44,24 @@
     // Dispose of any resources that can be recreated.
 }

+- (void)interstitialDidLoadAd:(MPInterstitialAdController *)interstitial {
+    NSLog(@"Interstitial Ad was loaded.");
+
+    [_interstitialAdController showFromViewController:self];
+
+    // If you'd like to pause app, then code in here.
+}
+
+- (void)interstitialDidDisappear:(MPInterstitialAdController *)interstitial {
+    NSLog(@"Interstitial Ad was disappeared.");
+
+    // If you'd like to resume app, code in here.
+}
+
+// ** CAUTION: MUST IMPLEMENT **
+// If you don't implement this method, your app will be crushed when loading ad failed..
+- (void)interstitialDidFailToLoadAd:(MPInterstitialAdController *)interstitial {
+    NSLog(@"** FAIL TO LOAD: Interstitial ad **");
+}
+
 @end

ここで一点注意があります。interstitialDidFailToLoadAd は必ず実装しましょう。これは広告をロード失敗したときに呼ばれるコールバックです。正しい AD UNIT ID を指定しててもたまに失敗します。おそらく配信元でうまく配信されないケースがあるのでしょう。
失敗したときにこのコールバックを定義しておかないとアプリが落ちて(!!)しまいます。ちなみに、バナー広告では fail用のコールバックを実装していなくても落ちませんでした。
なお、ロードに失敗した際は全画面広告は表示されません。

シミュレータで動作を確認します。

f:id:bekkou68:20130523233234p:plain

Google AdMob のバナー広告を表示

さて、デフォルトで組み込まれているバナー広告と全画面広告の組み込みは終了しました。これよりプラスアルファで、Google AdMob のバナー広告と Millennial Media の全画面広告を表示します。

管理画面から Google AdMob のみ配信するように設定しておくと確認しやすいです。Edit Network Settings などから設定できます。

管理画面の Test Ad ボタンを押した時に以下のように表示されれば準備OKです。

====================================== CAMPAIGN FILTER RESULTS ======================================
  No adgroups for level 0.
  No adgroups for level 1.
  ... (snip) ...
  No adgroups for level 8.
  No adgroups for level 9.
  No adgroups for level 10.
  Filtered adgroups are: AdMob key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  Mpx creatives: [], filtered creatives: [Creative(name: admob native dummy, source: network, width: 320, height: 50)]
  Calculating eCPMs for adunit creatives:
  admob native dummy: 0.05
  ##############################
  ##############################
  Winner found, rendering: admob native dummy

なお、SDK が正常に組み込んでいない状態だと以下のようなエラーログが表示されます。

...
2013-05-23 20:07:15.176 mopub_spike[17486:12b03] MOPUB: Could not find custom event class named MPGoogleAdMobBannerCustomEvent
2013-05-23 20:07:15.322 mopub_spike[17486:12b03] MOPUB: Banner view (xxxxxxxxxxxxxxxxxxxxxx) failed. Error: Error Domain=com.mopub.iossdk Code=0 "The operation couldn’t be completed. (com.mopub.iossdk error 0.)"

公式ドキュメントはこちら。プロジェクトから必要なクラスを取り込みます。

以下を XCode のプロジェクトに追加します。

  • AdNetworkSupport/GoogleAdMob/MPGoogleAdMobBannerCustomEvent.h
  • AdNetworkSupport/GoogleAdMob/MPGoogleAdMobBannerCustomEvent.m
  • AdNetworkSupport/GoogleAdMob/MPGoogleAdMobInterstitialCustomEvent.h
  • AdNetworkSupport/GoogleAdMob/MPGoogleAdMobInterstitialCustomEvent.m

f:id:bekkou68:20130523233237p:plain

そして、次のクラスに対して XCode から -fno-objc-arc オプションをつけます(プロジェクトを選択>TARGETS >Build Phases>Compile Sources)。-fno-objc-arc の最終行に改行をつけたら反映されなくてハマりました・・

  • AdNetworkSupport/GoogleAdMob/MPGoogleAdMobBannerCustomEvent.m
  • AdNetworkSupport/GoogleAdMob/MPGoogleAdMobInterstitialCustomEvent.m

Podfile に以下を追加します。

+ pod 'AdMob', '6.4.1'

インストール。

$ pod install

シミュレータより、Google AdMob の表示が確認できると思います。

f:id:bekkou68:20130523233236p:plain

Millennial Media の全面広告を出す

いよいよ最後です。Millennial Media の全面広告を出しましょう。Google AdMob と同じように、管理画面から Millennial Media のみ表示されるようにします。

以下のクラスを追加します。

  • AdNetworkSupport/Millennial/MPMillennialBannerCustomEvent.h
  • AdNetworkSupport/Millennial/MPMillennialBannerCustomEvent.m
  • AdNetworkSupport/Millennial/MPMillennialInterstitialCustomEvent.h
  • AdNetworkSupport/Millennial/MPMillennialInterstitialCustomEvent.m

そして -fno-objc-arc オプションを以下のクラスに追加します。

  • MPMillennialBannerCustomEvent.m
  • MPMillennialInterstitialCustomEvent.m

Millennial Media のサイトから SDK をダウンロードします。ダウンロードにはアカウントが必要です。ログインが必要な SDK だからおそらく cocoapods の spec に登録されていないのでしょう。

ダウンロードした SDK における MMSDK 配下を XCode の任意の箇所に追加します。

f:id:bekkou68:20130523233235p:plain

Millennial Media は音楽や動画などリッチな広告が多いので、新規で framework をいくつか追加する必要があります。ガイドラインに従って framework を追加してください。また、位置情報によって広告を出し分けているのでしょうか。CoreLocation.framework が必要です。組み込んだあとは、アプリの初回起動時に現在位置情報のアクセス許可を求めるダイアログが表示されるようになります。

位置情報を取得できるようにするため、次のようにコードを編集します。
AppDelegate.h

+ #import "MMSDK.h"
+ #import <CoreLocation/CoreLocation.h>

+ @property (strong, nonatomic) CLLocationManager *locationManager;

AppDelegate.m

  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  {
+     [MMSDK initialize]; //Initialize a Millennial Media session
 
+     //Create a location manager for passing location data for conversion tracking and ad requests
+     self.locationManager = [[CLLocationManager alloc] init];
+     [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
+     [self.locationManager startUpdatingLocation];
 
      return YES;
  } 

シミュレータで確認すると以下のようなログがでて表示されませんでした。

2013-05-23 21:39:44.875 mopub_spike[20330:1a303] MOPUB: Ad view is fetching ad network type: clear
2013-05-23 21:39:44.875 mopub_spike[20330:1a303] MOPUB: Ad server response indicated no ad available.
2013-05-23 21:39:44.876 mopub_spike[20330:1a303] ** FAIL TO LOAD: Interstitial ad **

広告を登録した直後は表示できる広告がないらしく、しばらく表示されませんでした。数日後、こんなメールが送られてきました。

Your Application, Xxx, has been approved! Your application will now serve live campaigns.
Log in to mMedia to view your reports and manage your apps. If you have any questions,
please visit the Resource Center or contact us.

SDK を組み込んで何日かすると承認をもらえるようです。その後は表示されるようになりました。また、問い合わせすると承認が早くなるかもしれません。

しあげに、Deployment Target を 5.1 に設定(TARGETS からプロジェクトを選択 > Summary > Deployment Target)し、5.1 と 6.1 それぞれのシミュレータで動作を確認することができました。

おわりに

快適な mopub ライフをおすごしください!