Swift で AdMob ネイティブ広告を 0 秒で表示できるようにした方法
こんにちは、株式会社 Zaim で iOS エンジニアをしている TEM です。iOS版の Zaim アプリでは今年になってから、AdMob のネイティブ広告を使って広告を表示しています。この記事では、その実装の際に得られた知見を紹介させていただきたいと思います。
AdMob ネイティブ広告
AdMob ネイティブ広告とは、アプリに表示する広告のデザインをカスタマイズできる広告のことです。コンテンツに自然に溶け込む形でレイアウト可能なため、通常のバナー広告よりストレスになりにくいと言われています。
ネイティブ広告には、AdMob 側でテンプレートのレイアウトが用意されており、今回はそちらを利用して広告を表示しました。
広告の表示に時間がかかる
アプリへのネイティブ広告の導入にあたり、AdMob のドキュメントにしたがって実装を進めました。表示自体はかなり簡単でしたが、広告を表示するまでに時間がかかるという問題が見つかりました。体感で、画面に遷移してから広告が表示するまで 1〜 2 秒程度かかっていたように思います。表示までに時間がかかると、滞在時間の短い画面では広告の表示が完了する前にユーザーが画面を遷移してしまい、結果的に広告が出せなくなることが課題になりそうでした。
どうすれば広告の表示速度を改善できるか
前述の問題に対して、どうにか広告を早く表示できないかと思い、ドキュメントを読み直してみました。その結果、ネイティブ広告の導入方法のページに以下のような記述を見つけました。
https://developers.google.com/admob/ios/native/start?hl=ja#when_to_request_ads
> ネイティブ広告を表示するアプリでは、実際に表示する広告を前もってリクエストすることもでき、通常はこの方法がおすすめです。 たとえば、ネイティブ広告を含むアイテムリストを表示するアプリでは、ユーザーがビューをスクロールしなければ表示されない広告や、まったく表示されない可能性がある広告が含まれている場合でも、そのリストに含まれるネイティブ広告を事前にすべて読み込むことができます。
ドキュメントによると、ネイティブ広告では事前に広告を読み込めるとのことでした。事前に読み込んでおく、つまりプリロードしておくことで遷移時に即時で広告の表示が可能になるので、表示速度の改善が見込めそうです。
広告をプリロードして、表示速度を改善する
ドキュメントには特にプリロードの実装例などは記載がなかったので、今回、独自で実装してみました。
① 広告のプリロード
// 広告のプリロード処理
func preloadNativeAd(numberOfAds: Int = 1) {
if let loader = adLoader, loader.isLoading {
return
}
let multipleAdsOptions = GADMultipleAdsAdLoaderOptions()
multipleAdsOptions.numberOfAds = numberOfAds
adLoader = GADAdLoader(adUnitID: "広告のユニットID",
rootViewController: nil,
adTypes: [.native],
options: [multipleAdsOptions])
adLoader?.delegate = self
adLoader?.load(GADRequest())
}
// 広告の取得完了後に呼ばれ、広告データの追加を行う
func adLoader(_: GADAdLoader, didReceive nativeAd: GADNativeAd) {
preloadedNativeAds.append(PreloadedNativeAd(ad: nativeAd, date: Date()))
}
// 広告データ格納用の構造体
struct PreloadedNativeAd {
var ad: GADNativeAd
var date: Date
}
広告のプリロード処理になります。こちらはドキュメントの実装とほとんど同じです。preloadNativeAd メソッドを実行することで任意の個数の広告オブジェクトを取得、保存できます。
② 広告のPop
func popNativeAd() -> GADNativeAd? {
let adLimitDate = Calendar.current.date(byAdding: .hour, value: -1, to: Date())
preloadedNativeAds = preloadedNativeAds.filter { $0.date > adLimitDate }
if let first = preloadedNativeAds.first {
preloadedNativeAds.removeFirst()
preloadNativeAd(numberOfAds: 1)
return first.ad
} else {
return nil
}
}
こちらは広告の Pop 処理になります。前述のプリロード処理で取得した広告オブジェクトを払い出します。ドキュメントによると取得してから 1 時間以上が経過した広告の表示は非推奨とのことなので、必要に応じて破棄するようにしています。
https://developers.google.com/admob/ios/native/start?hl=ja
広告のプリフェッチは効果的な手法ですが、表示されないままになっている古い広告はいつまでも保持しないことが重要です。1 時間以上たっても表示されずに保持されているネイティブ広告のオブジェクトは、破棄して新しいリクエストの広告に置き換えてください。
コード全体
final class AdMobNativeAdPreloader: NSObject {
static let shared = AdMobNativeAdPreloader()
override private init() {}
private var preloadedNativeAds = [PreloadedNativeAd]()
private var adLoader: GADAdLoader?
func preloadNativeAd(numberOfAds: Int = 1) {
if let loader = adLoader, loader.isLoading {
return
}
let multipleAdsOptions = GADMultipleAdsAdLoaderOptions()
multipleAdsOptions.numberOfAds = numberOfAds
adLoader = GADAdLoader(adUnitID: GlobalConstants.googleAdNativeUnitId,
rootViewController: nil,
adTypes: [.native],
options: [multipleAdsOptions])
adLoader?.delegate = self
adLoader?.load(GADRequest())
}
func popNativeAd() -> GADNativeAd? {
let adLimitDate = Calendar.current.date(byAdding: .hour, value: -1, to: Date())
preloadedNativeAds = preloadedNativeAds.filter { $0.date > adLimitDate }
if let first = preloadedNativeAds.first {
preloadedNativeAds.removeFirst()
preloadNativeAd(numberOfAds: 1)
return first.ad
} else {
return nil
}
}
}
extension AdMobNativeAdPreloader: GADNativeAdLoaderDelegate {
func adLoader(_: GADAdLoader, didReceive nativeAd: GADNativeAd) {
preloadedNativeAds.append(PreloadedNativeAd(ad: nativeAd, date: Date()))
}
func adLoader(_: GADAdLoader, didFailToReceiveAdWithError _: Error) {}
}
extension AdMobNativeAdPreloader {
struct PreloadedNativeAd {
var ad: GADNativeAd
var date: Date
}
}
結果
実装したプリロード処理をトップページで実行することで、広告の表示ページに遷移した際に即時で広告を表示できるようになりました!
広告の表示速度に関してお悩みの方は、ぜひ参考にしてみてください。
さいごに
Zaim では、エンジニアを募集しています。ぜひ話を聞きに来てください!