筆記#3 — 使用 AdMob 插入廣告並設置內購(IAP)去廣告

Pei-chi
21 min readJul 9, 2024

--

其實這篇好幾個月前就已經把各種操作截圖跟大綱都列好了,只是拖延症發作沒把說明文字打完🙈 剛好這個暑假回來上彼得潘的 Flutter 課程,忽然發現居然還有這篇文章一直存在草稿裡!趕快補上文字與大家分享這段篳路藍縷的 Admob 及 IAP設置路程 🤣🤣🤣

1. 申請 AdMob 帳號

→ AdMob 首頁:

i. 使用 google 帳號登入後同意使用條款:

ii. 設置 Payment 資訊:

* 如果點擊 “Add payments account” 但是一直沒有動靜的話,可以去確認一下網頁左上角的網頁許可設置,如果有設置任何 block 會導致彈跳視窗跑不出來(會顯示 “payments.google.com refused to connect.”)!

2. 建立新的 App

因為 App 還未在商店中上架,可以先選擇 “Unpublished”,等 App 上架後再回來連結 App 上架網址。

就會得到一組 App Id 啦!

3. Add ad unit

i. 選擇要使用的廣告類型:(我自己目前只有使用到 Banner 橫幅廣告 跟 Interstitial 插頁式廣告)

ii. 輸入廣告名稱:

iii. 就會得到一組 Ad unit ID 啦~ 不過這組 ID 先不要用!AdMob 有提供測試用的 Ad unit ID,讓我們在建置 App 時使用。如果自己使用到自己的 Ad ID 會被偵測為 Invalid activity,最嚴重可能會關閉帳號哦。

*Google 提供測試用的 Ad ID:

4. 在 Xcode 中匯入 Google Mobile Ads SDK

i. 打開 File > Add Packages Dependencies

ii. 右上角搜尋 “https://github.com/googleads/swift-package-manager-google-mobile-ads.git”,選擇 “add package”

5. 更新 Info.plist

i. 複製 Google AdMob Mobile Ads SDK 說明上的這段程式碼

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-*********</string> //這裡要更換為自己的 App ID
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
...... //完整程式碼太長啦!!請直接見網頁~
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qcr597p9d.skadnetwork</string>
</dict>
</array>

ii. 在 xCode 中右擊 Info > 選擇 Open As > Souce Code

iii. 把剛剛複製的程式碼貼進 Info.plist 中:
(貼入</dict> 前即可)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
......

//插入開始
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-*********</string> //自己的 App ID
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
......
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qcr597p9d.skadnetwork</string>
</dict>
</array>
//插入結束

</dict>
</plist>

6. 更新 App Delegate

在 App Delegate 中的 “didFinishLaunchingWithOptions” 內插入 GADMobileAds 的程式碼:

import UIKit
import GoogleMobileAds //插入這行

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

GADMobileAds.sharedInstance().start(completionHandler: nil) //插入這行

return true
}

}

7. 在 App 中嵌入廣告

我自己喜歡另開一個 File ,將 AdModb 的讀取與顯示寫成獨立的 struct 類別,這樣在不同的 view controller 都可以直接呼叫,後續想要修改也比較方便:

import GoogleMobileAds

struct Admob {
//因為後續會設置內購去廣告,所以設置了一個 key value 存在 UserDefaults 來確認是否要在裝置上顯示廣告。如果 App 沒有要設置內購功能,可以直接忽略所有跟 adRemoved 相關的程式碼~
static let adRemoved = UserDefaults.standard.bool(forKey: "adRemoved")

static func addBannerView() {
//如果已購買去廣告(adRemoved == true)則不會運行下面的程式碼
guard adRemoved else {
//詳細程式碼在下方~
}
}

static func addInterstitialAd() {
guard adRemoved else {
//詳細程式碼在下方~
}
}

}

> Banner (橫幅廣告)

i. 設置 struct Admob 內的程式碼:

static func addBannerView(_ bannerView: GADBannerView, view: UIView, self: UIViewController) {

guard adRemoved else {
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716" //Demo ad unit ID

bannerView.rootViewController = self
bannerView.load(GADRequest())
view.addSubview(bannerView)

bannerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
bannerView.bottomAnchor.constraint(equalTo: view.bottomAnchor), //我這裡將 banner 設置在螢幕最底端
bannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
bannerView.widthAnchor.constraint(equalTo: view.widthAnchor),
bannerView.heightAnchor.constraint(equalToConstant: 50) // default ad banner 尺寸: 320*50。這邊特別設置成 constraint 避免 autoLayout 時被壓縮,導致 banner 不可見。
])
return
}
}

ii. 在要使用 Banner 廣告的頁面:

var bannerView = GADBannerView(adSize: GADAdSizeBanner) // 預設的 ad banner 尺寸: 320*50

override func viewDidLoad() {
//如果使用 navigation controller:
Admob.addBannerViewToView(bannerView, view: self.navigationController!.view, self: self) //在首頁將 banner 設置在 navigationController.view 就好,比較方便

//如果沒用 navigation controller:
Admob.addBannerViewToView(bannerView, view: view, self: self) //在每一個要顯示廣告的 controller 都要放。
}

*GADAdSizeBanner:

*這時候使用 demo Ad unit ID 不管是在 simulator 或是實體裝置上廣告都可以正常顯示啦!但如果是自己的 unit ID,則只有在 simulator 上可以看到廣告(也會顯示為 Test Mode),但在實體裝置/TestFlight上則無法。查了 Google Admob Help 是上架後才會顯示:

果然 App 通過 App review 跟 AdMob review 後廣告就出現啦~

> Interstitial(插頁式廣告)

i. 設置 struct Admob 內的程式碼:

//interstitial Ad 我把程式拆分成兩段,分別為讀取(load)及顯示(show):

//讀取廣告並使用 escaping closure 導出讀取到的廣告(型別為 GADInterstitialAd)
static func loadInterStitialAd(completion: @escaping(GADInterstitialAd) -> Void) {

guard adRemoved else {

var interStitialAd = GADInterstitialAd()

//讀取廣告時會使用到的參數:
let adUnitID = "ca-app-pub-3940256099942544/4411468910" // demo adUnit ID
let request = GADRequest()

//送出 load 請求後,會收到回傳 ad 或 error
GADInterstitialAd.load(withAdUnitID: adUnitID, request: request) { ad, error in
//如果失敗收到 error
if let error {
print("Failed to load interstitial ad with error: \(error.localizedDescription)")
return
}
//如果成功收到 ad
if let ad {
interStitialAd = ad
completion(interStitialAd) //導出收到的 ad
}
}
return
}

}

//將 Interstitial 廣告顯示在畫面上
static func showInterStitialAd(_ interStitialAd: GADInterstitialAd?, self: GADFullScreenContentDelegate) {

guard adRemoved else {
//先檢查是否成功讀取到 interStitialAd
if let interStitialAd {
interStitialAd.fullScreenContentDelegate = self //這裡將廣告的 fullScreenContentDelegate 設置成要顯示廣告的頁面。
}else {
print("Ad wasn't ready")
}
interStitialAd?.present(fromRootViewController: self as? UIViewController)
return
}
}

ii. 在要使用 Interstitial 廣告的頁面:

//要加入 GADFullScreenContentDelegate protocol!
class controller: ...,GADFullScreenContentDelegate{

var interStitialAd: GADInterstitialAd?


//一般顯示 interstitial ad:
func showAd() {
if adRemoved {
nextAction()
} else {
//interStitialAd 是一次性的,所以每次要用的時候都要重新讀取
Admob.loadInterStitialAd { loadedAd in
self.interStitialAd = loadedAd
Admob.showInterStitialAd(self.interStitialAd, self: self)
}
nextAction() //這裡主要是避免廣告讀取失敗:如果沒能跳轉至全頁廣告,App 仍可以繼續運行自定義的 nextAction()~
}
}


//要先設置 "interStitialAd.fullScreenContentDelegate = self",這個 dismissFullScreenConten 程式才有辦法被呼叫。(我設置在 Admob struct 中)
func adWillDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
//廣告即將消失時,準備下一步~
nextAction()
}

p.s. 如果是想在點擊 “Back” button 返回上一頁時插入廣告,可以使用 willMove(toParent:):

var interStitialAd: GADInterstitialAd?

//因為點擊了 back button 就會回到上一層 controller,所以要提前讀取廣告,避免廣告還沒讀取完,畫面就已經跳轉走啦!(這個頁面只會插入一次廣告,所以可以這麼設置。如果會需要多次設置的話要記得把這個 loadedAd 存在不同變數哦!)

override func viewDidLoad() {
super.viewDidLoad()
Admob.loadInterStitialAd { loadedAd in
self.interStitialAd = loadedAd
}
}

//使用 willMove(toParent) ,趁跳轉回上一個頁面前顯示讀取好的廣告:
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil {
Admob.showInterStitialAd(interStitialAd, self: self)
}
}

8. 設置內購去廣告

(沒有要設置去廣告功能的可以直接跳到 10. App 上架後~)

參考資料:

i. 先切到 Signing & Capabilities,點選 “+ Capability”:

點選之後 “In-App Purchase” 就會出現在下面啦!

ii. 再到 App Store Connect > Apps,點選右上角 “+” 號

iii. 新增 App,點選 Bundle ID 就會自動冒出選項:

iv. 到 Monetization > In-App Purchase

v. 開始創建 IAP~

依照自己的設置選擇此次購買是否是消耗性的 or 永久性的:

vi. 創建好啦:

v. 編輯 Availability 及 Price Schedule:

vii. 在 Localizatoion 設置使用者會看到的名稱

viii. 在 Xcode 中設置程式碼並創建 Test Account:

Jason 大神這篇太強大啦,基本上直接套用就可以成功了(膜拜🤲)

*我自己只有做了一點小改動:在點擊 restore 之後,出現 activity indicator 動畫,如果確認有購買紀錄則會刷新成無廣告版本、若是沒有購買及路則恢復原本的按鈕樣式。

以上設置完之後,在測試 App 中點擊購買選項會跳出視窗要求輸入帳號密碼,輸入沙盒帳密進行測試。(輸入之後手機 Setting > app store 中也會出現 SANDBOX 帳號資訊~)

*之前 priceLocale 一直失敗(顯示 “EXC_BREAKPOINT (code=1, subcode=0x188143df0)”),等使用 Sandbox 帳號登入後、再次嘗試 priceLocale 就成功了🙌

9. App 上架時

跟一般 App 差不多,不過使用 IAP 會 collect data ,所以要好好設定 App Privacy:

10. App 上架後

App Store 成功上架後,到 AdMob 頁面上打開該 App,在左側導航欄 App Settings > App store details 貼上該 App 的 App store 的網址,

格式如下:

http://apps.apple.com/<country>/app/<app–name>/id<store-ID>

更新之後 Approval status 就會自動從 “Requires review” 變成 “Getting ready”。審核通過後會收到 email 通知,這時候從實體裝置打開 App 就可以成功看到廣告啦~🙌

*查看 AdMob 報告:

在 App 送出 review 期間忽然收到 AdMob 通知說我的帳戶有 Invalid Activity,嚇得我趕緊上後台查看 traffic resources。

到 AdMob 後台的 Reports 頁面,選擇要查看的報告。(因為我只有使用 AdMob 串接廣告,故查看 AdMob Network 的報告就好)

當時的 Ads Activity report 頁面:

(這個期間會使用到 App 的都是 App Reviewers~ 看來我的 reviewers 分別來自台灣/新加坡/美國🤣🤣)

爬文說 Invalid Activity 在檢查沒有其他異常(雖然但是,有沒有異常 admob 說了算😅)後就會恢復正常。後續我的 Admob 已經移除了警告,但可能還是有點影響,這個 app 的 match rate 一直都不高😭😭😭

* Admob 通知要進行新加坡稅務申報

等到過了 Payment thresholds (款項起付額度)再來思考這個問題吧 🤣 萬一真的到了那天(?)再來更新~

--

--