1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SwiftUI】個人開発アプリにAdMobを導入する全手順(ATT対応・バナー・安全なID管理 + 他広告の収益感まとめ)

1
Posted at

はじめに

先日、KPT(Keep, Problem, Try)フレームワークを活用した習慣化アプリHabitSparkをリリースしました!
個人開発アプリの収益化としてGoogle AdMobを導入しましたが、

  • ATT(App Tracking Transparency)の対応順序
  • 広告の種類ごとの違い
  • 本番IDの安全な管理
  • 「実際どれがどれくらい稼げるのか」

など、最初はかなり分かりづらいポイントが多かったです。
この記事では、

  • 実際にバナー広告を導入した経験
  • 他広告フォーマットの特徴や収益感

を交えながら、SwiftUIでのAdMob実装手順をまとめて解説します。

🚀 アプリはこちら
👉 HabitSpark - 振り返りで続くシンプル習慣化アプリ


📊 現在の収益(リアル)

実装の解説に入る前に、まずは一番気になるであろう「収益」を公開します。

  • 収益: ¥1
  • 表示回数: 166
  • eCPM: ¥8

👉 正直に言うと、ほぼゼロです(笑)

ただし、「広告が動いてお金が発生する状態」まで持っていくのが最初の壁であり、今回はそこをちゃんと越えられたのでOKとしています!

🛠 開発環境

  • SwiftUI
  • Swift 5.x
  • Google-Mobile-Ads-SDK

1. AdMobの広告種類まとめ(重要)

AdMobの広告種類まとめ

① バナー広告(今回実装)

  • 特徴: 画面下などに常時表示
  • 収益感: かなり低い(eCPM 数円〜数十円)
  • メリット: UXを壊しにくく、とりあえず入れやすい。
  • デメリット: 稼げない。
  • 👉 今回の自分の結果: eCPM ¥8 / 収益 ¥1。正直「とりあえず導入する用」です。

② インタースティシャル広告(今回は未実装だけど重要)

  • 特徴: 画面遷移時に全画面表示
  • 収益感: 中〜高(バナーの数倍)
  • メリット: しっかり収益が出る。
  • デメリット: UXを壊しやすい。
  • 👉 使いどころ: 完了画面や区切りタイミング。個人開発ならこれが一番バランス良いです。

③ リワード広告(Rewarded)

  • 特徴: 広告を見ると報酬がもらえる
  • 収益感: 高い(かなり強い)
  • メリット: ユーザーが納得して見るため、UXを壊さない。
  • デメリット: アプリ設計段階からの組み込みが必要。
  • 👉 例: 「動画を見ると分析機能解放」「履歴を1週間分表示」。設計できるなら最強です。

④ ネイティブ広告

  • 特徴: UIに自然に溶け込む広告
  • 収益感: 中くらい
  • メリット: UXを壊しにくい。
  • デメリット: 実装が少し面倒。

2. 結論:どれを使うべき?

個人開発なら以下の優先度がおすすめです👇

広告フォーマット おすすめ度 理由
インタースティシャル ★★★ 収益と実装手間のバランスが一番良い
リワード ★★★ UXを壊さず高収益(ただし事前設計必須)
バナー ★☆☆ UXは守れるが、単体だと正直ほぼ稼げない

3. Info.plist設定

AdMob SDKを導入後、Info.plist に以下の必須項目を追加します。

  1. GADApplicationIdentifier: アプリID
  2. SKAdNetworkItems: 広告ネットワークのIDリスト
  3. NSUserTrackingUsageDescription: ATTダイアログの文言

⚠️ ATTの文言について
👉 「ユーザーのメリットを書く」 ことが審査通過の鍵です。
(例:「お客様の興味関心に合わせた最適な広告を表示するために使用します」)


4. 広告IDの安全管理

本番用IDをそのままGitHub等のパブリックリポジトリにプッシュしてしまうと事故ります。
必ず Config.swift などの設定ファイルを作成し、#if DEBUG を用いて環境ごとにテストIDと本番IDが切り替わるようにしましょう。また、本番IDを記載したファイルは .gitignore に追加することを強く推奨します。

import Foundation

enum AdMobConfig {
    #if DEBUG
    static let bannerUnitID = "テストID"
    static let interstitialUnitID = "テストID"
    #else
    static let bannerUnitID = "本番ID"
    static let interstitialUnitID = "本番ID"
    #endif
}

5. ATT → AdMob初期化の順番

👉 ATTダイアログが閉じられた「後」にAdMobを初期化する のが鉄則です。
SwiftUIの @main 構造体などで、トラッキング許可のステータスに関わらず、ダイアログの処理が終わったタイミングで GADMobileAds.sharedInstance().start() を呼び出します。

import SwiftUI
import AppTrackingTransparency
import GoogleMobileAds

@main
struct HabitSparkApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
                    requestTrackingAuthorization()
                }
        }
    }
    
    private func requestTrackingAuthorization() {
        if #available(iOS 14, *) {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                ATTrackingManager.requestTrackingAuthorization { _ in
                    // 許可・拒否に関わらず、ダイアログが閉じたら初期化
                    GADMobileAds.sharedInstance().start(completionHandler: nil)
                }
            }
        } else {
            GADMobileAds.sharedInstance().start(completionHandler: nil)
        }
    }
}

6. 広告の実装コード

バナー広告(今回実装)

SwiftUIでバナーを表示するためには、UIKitのビューを UIViewRepresentable でラップして使用します。

import SwiftUI
import GoogleMobileAds

struct BannerView: UIViewRepresentable {
    func makeUIView(context: Context) -> GADBannerView {
        let bannerView = GADBannerView(adSize: GADAdSizeBanner)
        bannerView.adUnitID = AdMobConfig.bannerUnitID
        
        if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let rootVC = windowScene.windows.first?.rootViewController {
            bannerView.rootViewController = rootVC
        }
        
        bannerView.load(GADRequest())
        return bannerView
    }

    func updateUIView(_ uiView: GADBannerView, context: Context) {}
}

// 使い方:Viewの中で呼び出すだけ
// BannerView().frame(width: 320, height: 50)

インタースティシャル広告(おまけ・おすすめ)

個人開発で一番おすすめのインタースティシャル広告の実装例です。こちらは全画面表示のため、事前に裏側でロード(読み込み)しておき、画面遷移時などの任意のタイミングで表示する仕組みを作ります。

import Foundation
import GoogleMobileAds

class InterstitialAdManager: NSObject, ObservableObject {
    private var interstitialAd: GADInterstitialAd?
    
    func loadAd() {
        GADInterstitialAd.load(withAdUnitID: AdMobConfig.interstitialUnitID, request: GADRequest()) { [weak self] ad, error in
            if error == nil { self?.interstitialAd = ad }
        }
    }
    
    func showAd() {
        if let ad = interstitialAd,
           let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let rootVC = windowScene.windows.first?.rootViewController {
            ad.present(fromRootViewController: rootVC)
        }
        loadAd() // 次回用に再ロード
    }
}

// 使い方:画面表示時(.onAppear)に loadAd() し、ボタン等のアクションで showAd() を呼ぶ

💡 実際にやって分かったこと

① バナーだけだと本当に稼げない

今回:166インプレッションで収益 ¥1。
👉 ほぼ誤差レベル です。

② 収益より先にやるべきこと

  • ユーザー増やす
  • 継続率上げる
    👉 これが大前提。まずはアプリを使ってもらうことが最優先 です。

③ 広告はUXとの戦い

  • 出しすぎ → 即アンインストール
  • 出さなすぎ → 収益ゼロ
    👉 「自然なタイミング」が全て

まとめ

AdMob導入のポイントは以下の4つです。

  1. 広告の種類を理解する
  2. ATTの順序を守る
  3. IDは安全に管理
  4. UXを壊さない

おわりに

今回の収益は1円ですが、
👉 0 → 1 はめちゃくちゃ価値があります!

ここからは、

  • インタースティシャルの追加
  • リワードの設計

で少しずつ収益を伸ばしていく予定です。
もし同じように個人開発している人の参考になれば嬉しいです!


※ 記事内の図解イラストは、画像生成AIを使用して作成しています。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?