SwiftUI로 Google Admob 사용하기

SwiftUI 기반의 프로젝트에 Admob을 사용해 광고를 띄워봅시다.
Posted on 2022-06-24 by GKSRUDTN99
Swift&Xcode SwiftUI Google Admob

1. SPM 또는 CocoaPods를 통해 구글 모바일 광고 SDK 설치하기

CocoaPods

Podfile에 아래 내용을 추가하고,

pod 'Google-Mobile-Ads-SDK'


CommandLine에서 다음을 실행합니다.

pod install --repo-update


Swift Package Manager

프로젝트를 열고, File > Add Packages로 이동합니다.
패키지 검색 창에 다음 링크를 검색한 뒤, 설치합니다.

https://github.com/googleads/swift-package-manager-google-mobile-ads.git



2. Google Admob 가입 및 앱 등록

Google Admob에서 로그인 한 뒤, 가입합니다.


콘솔의 좌측 탭에서 앱을 선택한 뒤,
앱 추가 버튼을 누르고, 앱 추가를 진행합니다.


앱 추가가 완료되면, 콘솔의 좌측 탭에서 광고 단위를 선택하여 추가할 수 있습니다.
표시하고자 하는 광고 형태를 선택하여 광고 추가를 진행합니다.


광고 추가까지 진행했다면, 콘솔의 좌측 탭에서 앱 설정을 선택하고,
앱 ID를 확인합니다. (뒤에서 Info.plist 파일 설정에 사용합니다.)



3. Info.plist 설정

광고를 추가할 프로젝트를 열고,


Info.plist에서 "GADApplicationIdentifier"라는 이름을 가진 프로퍼티를 생성한 뒤,
Value 값에는 앞에서 확인한 앱 ID를 넣어줍니다.


다음으로, "GADIsAdManagerApp"라는 이름의 Boolean 프로퍼티를 생성한 뒤,
Value 값에는 1을 넣어줍니다.

이 옵션 없이 Admob 객체를 초기화하려고 시도하면,
"The Google Mobile Ads SDK was initialized without AppMeasurement. ~"
로 시작하는 에러가 발생합니다.


세번째로, "SKAdNetworkItems"라는 이름을 가진 Array 프로퍼티를 생성한 뒤,
첫번째 Item을 Dictionary 형태로 변경하고,
Dictionary안에 "SKAdNetworkIdentifier"라는 이름을 가진 프로퍼티들을 추가해줍니다.
Value 값에 들어가야 하는 Identifier들은 Admob 개발자 문서에서 확인할 수 있습니다.

iOS 14 이상의 기기들에는 '앱 추적 투명성'이라는 프레임워크가 포함되어 있어,
광고를 통한 앱 설치 여부를 확인하고, 맞춤형 광고를 제공하는 등의 '추적' 기능을 사용하기 위해서는.
사용자의 동의를 구해야 합니다.

사용자가 추적에 동의하지 않아도 앱 설치를 통한 광고 수익을 얻기 위해서는,
위에서 설정한 "SKAdNetworkItems"가 필요합니다.

위 방법 대신, Info.plist를 다른 텍스트 편집기를 통해 열어서, key값을 붙여넣어도 됩니다.
붙여넣은 뒤 Indentation이 맞지 않아서 불편하다면,
붙여넣고 저장한 뒤, Xcode로 Info.plist를 열어서 "SKAdNetworkItems"의 위치를 옮겼다 돌리면,
Indentation이 자동으로 맞춰집니다.


마지막으로, 앱 추적 투명성 승인 요청을 표시할 때 보여줄 메시지를 작성합니다.
"Privacy - Tracking Usage Description" 프로퍼티를 추가한 뒤, 메시지를 Value로 작성합니다.


4. 모바일 광고 SDK 초기화 및 앱 추적 투명성 승인 요청

광고를 로드하기 전에, GADMobileAds.sharedInstance에서 startWithCompletionHandler를 호출하여
SDK를 초기화시켜야 합니다.


저는 SwiftUI를 사용하는 환경이므로, App 객체의 init() 함수 안에서 초기화를 진행하겠습니다.

import GoogleMobileAds

@main
struct SampleApp: App {

  var body: some Scene {
    ...
  }

  init() {
    GADMobileAds.sharedInstance().start(completionHandler: nil)
  }
}


다음으로, 앱 추적 투명성 승인 요청 화면을 띄웁니다.
앱 내에서 표시하는 모든 광고 이전에 승인 요청 화면을 띄워야 합니다.

iOS 15 이상에서, ATTrackingManager에 버그가 있어서,
init() 함수 안에서 바로 호출하면 승인 요청 화면이 보이지 않습니다.

따라서 DispatchQueue를 통해 1초의 딜레이를 주거나,
didBecomeActive Notification을 통해 화면을 띄워줍니다.

import GoogleMobileAds
import AppTrackingTransparency

@main
struct SampleApp: App {

  var body: some Scene {
    // NotificationCenter 이용
    WindowGroup {
      HomeView()
      .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
        ATTrackingManager.requestTrackingAuthorization(completionHandler: { _ in })
      }
    }
  }

  init() {
    GADMobileAds.sharedInstance().start(completionHandler: nil)

    // DispatchQueue 이용
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
      ATTrackingManager.requestTrackingAuthorization(completionHandler: { _ in })
    }
  }
}

5. GoogleAdView를 만들고, 화면에 표시하기

Google Admob이 SwiftUI를 직접적으로 지원하는 바가 없고,
광고를 표시할 때 싸용하는 GADBannerView 객체는 ViewController 위에 표시되어야 해서,
UIViewControllerRepresentable 프로토콜을 활용해 구현합니다.

import SwiftUI
import GoogleMobileAds

struct GoogleAdView: UIViewControllerRepresentable {    
  func makeUIViewController(context: Context) -> UIViewController {
    let viewController = UIViewController()
    return viewController
  }

  func updateUIViewController(_ viewController: UIViewController, context: Context) {

  }
}   


저는 세로모드에서 하단 일정 부분에 고정되어 표시되는 배너 광고를 띄워볼겁니다.
가로는 화면 크기와 같게하고, 세로 길이는 광고에 알맞게 조절되도록 합니다.


GADPortraitAnchoredAdaptiveBannerAdSizeWithWidth 함수를 통해 배너의 크기를 가져옵니다.

이 함수는 일정 가로 길이를 매개변수로 받아서,
광고에 따라 해당 가로길이에 알맞은 세로 길이를 찾아,
GADAdSize 형태로 반환합니다.

func makeUIViewController(context: Context) -> UIViewController {
  let viewController = UIViewController()
  let bannerSize = GADPortraitAnchoredAdaptiveBannerAdSizeWithWidth(UIScreen.main.bounds.width)
  return viewController
}


배너 객체를 생성하고,
viewController의 크기를 배너 크기와 동일하게 설정합니다.

func makeUIViewController(context: Context) -> UIViewController {
  let viewController = UIViewController()
  let bannerSize = GADPortraitAnchoredAdaptiveBannerAdSizeWithWidth(UIScreen.main.bounds.width)
  let banner = GADBannerView(adSize: bannerSize)
  banner.rootViewController = viewController
  viewController.view.addSubview(banner)
  viewController.view.frame = CGRect(origin: .zero, size: bannerSize.size)
  return viewController
}


banner에 광고 단위 ID값을 넣은 뒤, 광고를 로드합니다.

'2. Google Admob 가입 및 앱 등록' 단계에서 확인했던 광고 Id입니다.

테스트 중인 앱의 경우 구글 Admob에서 제공하는 데모 광고 단위를 사용해야합니다.
데모 광고 단위는 테스트 광고 사용 설정에서 확인하실 수 있습니다.
테스트 과정에서 실제 광고 단위를 반복 사용하면, 계정이 정지될 수 있으니 주의하시길 바랍니다.

func makeUIViewController(context: Context) -> UIViewController {
  let viewController = UIViewController()
  let bannerSize = GADPortraitAnchoredAdaptiveBannerAdSizeWithWidth(UIScreen.main.bounds.width)
  let banner = GADBannerView(adSize: bannerSize)
  banner.rootViewController = viewController
  viewController.view.addSubview(banner)
  viewController.view.frame = CGRect(origin: .zero, size: bannerSize.size)
  banner.adUnitID = "ca-app-pub-3940256099942544/2934735716"
  banner.load(GADRequest())
  return viewController
}


마지막으로, 광고를 표시할 View의 body에서, 광고를 표시하면 됩니다.

struct HomeView: View {
  var body: some View {
    VStack {
      Spacer()
      Mobile.GoogleAdView(adType: .banner)
        .frame(width: UIScreen.main.bounds.width, height: GADPortraitAnchoredAdaptiveBannerAdSizeWithWidth(UIScreen.main.bounds.width).size.height)
    }
  }
}



번외. Delegate 설정하기

Google Admob은 GADBannerView의 상태가 변함을 알려주는 Delegate도 제공합니다.
UIViewRepresentable의 Coordinator를 활용해서 Delegate 활용이 가능합니다.

struct GoogleAdView: UIViewControllerRepresentable {
  func makeUIViewController(context: Context) -> UIViewController {
    let viewController = UIViewController()
    let bannerSize = GADPortraitAnchoredAdaptiveBannerAdSizeWithWidth(UIScreen.main.bounds.width)
    let banner = GADBannerView(adSize: bannerSize)
    banner.rootViewController = viewController
    viewController.view.addSubview(banner)
    viewController.view.frame = CGRect(origin: .zero, size: bannerSize.size)
    banner.adUnitID = "ca-app-pub-3940256099942544/2934735716"
    banner.load(GADRequest())
    banner.delegate = context.coordinator
    return viewController
  }

  func updateUIViewController(_ viewController: UIViewController, context: Context) {

  }

  func makeCoordinator() -> Coordinator {
    return Coordinator()
  }

  class Coordinator: NSObject, GADBannerViewDelegate {
    func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
      print("bannerViewDidReceiveAd")
    }

    func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
      print("bannerView:didFailToReceiveAdWithError: \(error.localizedDescription)")
    }

    func bannerViewDidRecordImpression(_ bannerView: GADBannerView) {
      print("bannerViewDidRecordImpression")
    }

    func bannerViewWillPresentScreen(_ bannerView: GADBannerView) {
      print("bannerViewWillPresentScreen")
    }

    func bannerViewWillDismissScreen(_ bannerView: GADBannerView) {
      print("bannerViewWillDIsmissScreen")
    }

    func bannerViewDidDismissScreen(_ bannerView: GADBannerView) {
      print("bannerViewDidDismissScreen")
    }
  }
}

...
Anonymous    Dec. 28, 2023, 10:13 a.m.

감사합니다 덕분에 손쉽게 구현 할 수 있었어요!