【SwiftUI】nend のバナー、インタースティシャル広告を SwiftUI で実装する

iPhone アプリに貼る広告としては Google AdMob などが他にありますが nend で広告を実装したいと思います。

通常の Swift で実装する場合については以下の git hub で公開されている例の様に実装すれば問題ありませんがSwiftUI で実装する場合少し変更しなければならないのでその方法を今回紹介します。

https://github.com/fan-ADN/nendSDK-iOShttps://github.com/fan-ADN/nendSDK-iOShttps://github.com/fan-ADN/nendSDK-iOS

前提として pod で nend の SDK をインストールしておいて下さい。

バナー広告

簡単に言えば UIViewControllerRepresentable で上記 UIViewController で実装されてた BannerView320x50Controller をラップしただけにはなります。setNendID ではテスト用の ID を使用していますが本番はこちらを自分で申請したものと入れ替えてください。

例では以下の様な BannerView320x50Controller があります。

class BannerView320x50Controller: UIViewController, NADViewDelegate {
    
    fileprivate var nadViewManually: NADView!
    
    // ------------------------------------------------------------------------------------------------
    // NADViewを生成、ロード開始
    // ------------------------------------------------------------------------------------------------
    
    override func viewDidLoad() {
        super.viewDidLoad()
         
        // コードでバナー広告を生成
//        nadViewManually = NADView()
        // コードでバナー広告を生成(広告サイズの自動調整を行う場合)
        nadViewManually = NADView(isAdjustAdSize: true)
        
        // 広告枠のspotid/apikeyを設定(必須)
        nadViewManually.setNendID(3172, apiKey: "a6eca9dd074372c898dd1df549301f277c53f2b9")
        
        // delegateを受けるオブジェクトを指定(必須)
        nadViewManually.delegate = self
        
        // 読み込み開始(必須)
        nadViewManually.load()
        // 例) 問い合わせエラー時には60分間隔で再問い合わせする
//        nadViewManually.load(["3600":"retry"])
        
    }
    
    // ------------------------------------------------------------------------------------------------
    // リリース
    // ------------------------------------------------------------------------------------------------

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    deinit {
        // delegateには必ずnilセットして解放する
        
        
        nadViewManually.delegate = nil
        nadViewManually = nil
    }

    
    // ------------------------------------------------------------------------------------------------
    // リフレッシュの中断と再開
    // ------------------------------------------------------------------------------------------------
    
    // 画面遷移が発生するような構造で
    // 各ViewControllerごとにNADViewインスタンスを生成するような場合には
    // 画面の表示状態に応じて pause / resume メッセージを送信し
    // 広告のリフレッシュ(定期受信ローテーション)を 一時中断 / 再開 してください
    // 無駄なネットワークアクセスや、impressionを抑える事が出来ます。
    
    // この画面が表示される際に広告のリフレッシュを再開します
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // 再開
        nadViewManually.resume()
        
        // 注意:他アプリ起動から、自アプリが復帰した際に広告のリフレッシュを再開するには
        // AppDelegate applicationWillEnterForeground などを利用してください
        
    }
    
    // この画面が隠れたら、広告のリフレッシュを中断します
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // 中断
        nadViewManually.pause()
        
        // 注意:safariなど他アプリが起動し自分自身が背後に回った際に広告のリフレッシュを中止するには
        // AppDelegate applicationDidEnterBackground などを利用してください
    }
    
    
    // ------------------------------------------------------------------------------------------------
    // 受信状況の通知
    // ------------------------------------------------------------------------------------------------
    
    // MARK: - NADViewDelegate
    
    // 広告の受信に成功し表示できた場合に1度通知されます。必須メソッドです。
    func nadViewDidFinishLoad(_ adView: NADView!) {

        print("nadViewDidFinishLoad,nadViewManually:\(adView!)")

        // 広告のロードが終了してからViewを乗せる場合はnadViewDidFinishLoadを利用します。
        adView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(nadViewManually)

        // 画面下部に広告を表示させる場合
        view.addConstraints([
            NSLayoutConstraint.init(item: adView!,
                                    attribute: .width,
                                    relatedBy: .equal,
                                    toItem: nil,
                                    attribute: .width,
                                    multiplier: 1,
                                    constant: adView.frame.size.width),
            NSLayoutConstraint.init(item: adView!,
                                    attribute: .height,
                                    relatedBy: .equal,
                                    toItem: nil,
                                    attribute: .height,
                                    multiplier: 1,
                                    constant: adView.frame.size.height),
            NSLayoutConstraint.init(item: adView!,
                                    attribute: .centerX,
                                    relatedBy: .equal,
                                    toItem: view,
                                    attribute: .centerX,
                                    multiplier: 1,
                                    constant: 0),
            NSLayoutConstraint.init(item: adView!,
                                    attribute: .bottom,
                                    relatedBy: .equal,
                                    toItem: bottomLayoutGuide,
                                    attribute: .top,
                                    multiplier: 1,
                                    constant: 0),
        ])
    
    }
    
    // 以下は広告受信成功ごとに通知される任意メソッドです。
    func nadViewDidReceiveAd(_ adView: NADView!) {

    }
    
    // 以下は広告受信失敗ごとに通知される任意メソッドです。
    func nadViewDidFail(toReceiveAd adView: NADView!) {
        
        // エラーごとに処理を分岐する
        let error: NSError = adView.error as NSError
        
        switch (error.code) {
        case NADViewErrorCode.NADVIEW_AD_SIZE_TOO_LARGE.rawValue:
            // 広告サイズがディスプレイサイズよりも大きい
            break
        case NADViewErrorCode.NADVIEW_INVALID_RESPONSE_TYPE.rawValue:
            // 不明な広告ビュータイプ
            break
        case NADViewErrorCode.NADVIEW_FAILED_AD_REQUEST.rawValue:
            // 広告取得失敗
            break
        case NADViewErrorCode.NADVIEW_FAILED_AD_DOWNLOAD.rawValue:
            // 広告画像の取得失敗
            break
        case NADViewErrorCode.NADVIEW_AD_SIZE_DIFFERENCES.rawValue:
            // リクエストしたサイズと取得したサイズが異なる
            break
        default:
            break
        }
        
        print("nadViewDidFailToReceiveAd,nadViewManually,code=\(error.code)")
        print("nadViewDidFailToReceiveAd,nadViewManually,domain=\(error.domain)")
         
    }
    
    // 以下はバナー広告がクリックされるごとに通知される任意メソッドです。
    func nadViewDidClickAd(_ adView: NADView!) {
      
        print("nadViewDidClickAd,nadViewManually:\(adView!)")
        
    }

    // 以下はインフォメーションボタンがクリックされるごとに通知される任意メソッドです。
    func nadViewDidClickInformation(_ adView: NADView!) {
   
    }
}

それをこの様にしてラップするだけです。

final class NendBannerView : UIViewControllerRepresentable {
        
    func makeUIViewController(context: Context) -> BannerView320x50Controller {
        
        let bannerVC = BannerView320x50Controller()
                
        return bannerVC
    }

    func updateUIViewController(_ uiView: BannerView320x50Controller, context: Context) {
    }
    
    
}

インタースティシャル

画面上に覆いかぶさる様にして表示されるインタースティシャルは SwiftUI では以下の様に実装した NendInterstitialView を使用します。

final class NendInterstitialView :NSObject, NADInterstitialLoadingDelegate, NADInterstitialClickDelegate {
        
    override init() {
        super.init()
        NADInterstitial.sharedInstance()?.loadingDelegate = self
        NADInterstitial.sharedInstance()?.clickDelegate = self
    }
    
    deinit {
        NADInterstitial.sharedInstance().delegate = nil
    }
    
    
    func showAd() {
            
        var showResult: NADInterstitialShowResult
        let root = UIApplication.shared.windows.first?.rootViewController
        showResult = NADInterstitial.sharedInstance().showAd(from: root)
        
        switch(showResult){
        case .AD_SHOW_SUCCESS:
            print("AD_SHOW_SUCCESS")
        case .AD_LOAD_INCOMPLETE:
            print("AD_LOAD_INCOMPLETE")
        case .AD_REQUEST_INCOMPLETE:
            print("FAILED_AD_REQUEST")
        case .AD_DOWNLOAD_INCOMPLETE:
            print("FAILED_AD_DOWNLOAD")
        case .AD_FREQUENCY_NOT_REACHABLE:
            print("AD_FREQUENCY_NOT_REACHABLE")
        case .AD_SHOW_ALREADY:
            print("AD_SHOW_ALREADY")
        case .AD_CANNOT_DISPLAY:
            print("AD_CANNOT_DISPLAY")
        @unknown default:
            break
        }
    }
    
    func didFinishLoadInterstitialAd(withStatus status: NADInterstitialStatusCode) {

    }
    
    func didClick(with type: NADInterstitialClickType) {
        switch(type){
        case .DOWNLOAD:
            print("DOWNLOAD")
        case .CLOSE:
            print("CLOSE")
        case .INFORMATION:
            print("INFORMATION")
        @unknown default:
            break
        }
    }
    
}

UIViewContoroller で実装する場合は NADInterstitial.sharedInstance().showAd(from: self) のように showAd の引数に自身を指定していますが、UIViewContoroller を使わない場合は UIApplication.shared.windows.first?.rootViewController を指定することでインタースティシャル広告を表示させます。

因みにほぼ同じ要領で Google AdMob のインタースティシャル広告も表示することができます。

let root = UIApplication.shared.windows.first?.rootViewController
self.interstitial.present(fromRootViewController: root!)

ここでのインスタンス interstitial は GADInterstitial(adUnitID: “your id") のものとなります。

また上記の NendInterstitialView では ID の指定を行っていませんがそれは AppDelegate 内で行います。

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        NADInterstitial.sharedInstance().loadAd(withSpotID: 213208, apiKey: "308c2499c75c4a192f03c02b2fcebd16dcb45cc9")
        
        return true
    }
}

それぞれの広告の表示

上記のクラスが出来上がったらあとは View で呼ぶだけです。

struct ContentView: View {
    
    var nendInterstitial = NendInterstitialView()
    
    var body: some View {
        
        ZStack {
            NendBannerView()
            
            Button(action: {
                nendInterstitial.showAd()
            }){
                Text("click here")
            }
        }
    }
}

nend を SwiftUI で実装するという内容の記事は現時点でざっと探してもなかったので今回取り扱いましたが、GoogleMobileAds でもだいたい同じ様な要領で実装できるので参考にしてみてください。

学習書籍


詳細! SwiftUI iPhoneアプリ開発入門ノート iOS 13 + Xcode11対応

詳細! SwiftUI iPhoneアプリ開発入門ノート[2020] iOS 14+Xcode 12対応

SwiftUI 徹底入門

1人でアプリを作る人を支えるSwiftUI開発レシピ (技術の泉シリーズ(NextPublishing))

基礎から学ぶ SwiftUI