Skip to content

Commit

Permalink
Dynamic Price Banner Refactor (#105)
Browse files Browse the repository at this point in the history
* implemented refreshing banner and refreshing AdLoader banner use cases

* integrated refreshing banner to the original banner controller

* updated banner example to use refactored dynamic price implementation

* updated adLoader and banner GAM examples to use the new dynamic price api

* added refreshing to adLoader example, use new notifyNoFillIfNeeded()

* treat NimbusAd as optional

* pass mapping to address type ambiguity
  • Loading branch information
standa-dev authored Mar 5, 2024
1 parent 2b904a2 commit 1782b16
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 47 deletions.
106 changes: 78 additions & 28 deletions Application/Sources/DynamicPrice/GAMAdLoaderBannerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,78 @@ import NimbusSDK
import GoogleMobileAds

class GAMAdLoaderBannerViewController: GAMBaseViewController {
private let requestManager = NimbusRequestManager()
private lazy var dynamicPriceRenderer: NimbusDynamicPriceRenderer = {
return NimbusDynamicPriceRenderer(requestManager: requestManager)
}()
private static let refreshInterval: TimeInterval = 30

private let gamRequest = GAMRequest()
private let requestManager = NimbusRequestManager()
private var adLoader: GADAdLoader?
private var ad: NimbusAd?
private weak var bannerView: GAMBannerView?

private var refreshTimer: Timer?

/// Set this to false if you don't want a refreshing banner
private var isRefreshingBanner = true

override func viewDidLoad() {
super.viewDidLoad()

if isRefreshingBanner {
setupNotifications()
setupRefreshTimer()
}

requestManager.delegate = self

fetchNimbusBid()
}

func setupNotifications() {
NotificationCenter.default.addObserver(
self,
selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(appWillResignActive),
name: UIApplication.willResignActiveNotification,
object: nil
)
}

func fetchNimbusBid() {
requestManager.performRequest(request: NimbusRequest.forBannerAd(position: headerSubTitle))
}

func load(ad: NimbusAd? = nil) {
adLoader = GADAdLoader(
adUnitID: googleDynamicPricePlacementId,
rootViewController: self,
adTypes: [.gamBanner],
options: nil
)
adLoader?.delegate = self
adLoader?.loadDynamicPrice(gamRequest: GAMRequest(), ad: ad, mapping: mapping)
}

// MARK: - Refreshing Banner Logic

func setupRefreshTimer() {
refreshTimer?.invalidate() // just to make sure there's no outstanding timer
refreshTimer = Timer.scheduledTimer(withTimeInterval: Self.refreshInterval, repeats: true) { [weak self] _ in
self?.fetchNimbusBid()
}
print("\(Self.self) created refresh timer")
}

@objc private func appDidBecomeActive() {
setupRefreshTimer()
}

@objc private func appWillResignActive() {
refreshTimer?.invalidate()
print("\(Self.self) removed refresh timer")
}
}

// MARK: - GADAdLoaderDelegate
Expand All @@ -45,16 +102,16 @@ extension GAMAdLoaderBannerViewController: GADAdLoaderDelegate, GAMBannerAdLoade
func adLoader(_ adLoader: GADAdLoader, didReceive bannerView: GAMBannerView) {
print("adLoader got bannerView")

if let ad {
dynamicPriceRenderer.willRender(ad: ad, bannerView: bannerView)
}

bannerView.rootViewController = self
bannerView.adUnitID = googleDynamicPricePlacementId
bannerView.delegate = self
bannerView.appEventDelegate = self
bannerView.paidEventHandler = { [weak self] adValue in
self?.dynamicPriceRenderer.notifyBannerPrice(adValue: adValue, bannerView: bannerView)
bannerView.applyDynamicPrice(
requestManager: requestManager,
delegate: self,
ad: adLoader.nimbusAd
)
bannerView.paidEventHandler = { [weak bannerView] adValue in
bannerView?.updatePrice(adValue)
}

view.addSubview(bannerView)
Expand All @@ -64,6 +121,7 @@ extension GAMAdLoaderBannerViewController: GADAdLoaderDelegate, GAMBannerAdLoade
bannerView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
bannerView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
])
self.bannerView = bannerView
}

func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: Error) {
Expand All @@ -76,7 +134,7 @@ extension GAMAdLoaderBannerViewController: GADAdLoaderDelegate, GAMBannerAdLoade
extension GAMAdLoaderBannerViewController: GADAppEventDelegate {
func adView(_ banner: GADBannerView, didReceiveAppEvent name: String, withInfo info: String?) {
print("adView:didReceiveAppEvent")
dynamicPriceRenderer.handleBannerEventForNimbus(bannerView: banner, name: name, info: info)
bannerView?.handleEventForNimbus(name: name, info: info)
}
}

Expand All @@ -89,12 +147,10 @@ extension GAMAdLoaderBannerViewController: GADBannerViewDelegate {

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

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

func bannerViewDidRecordClick(_ bannerView: GADBannerView) {
Expand All @@ -117,23 +173,17 @@ extension GAMAdLoaderBannerViewController: GADBannerViewDelegate {
// MARK: - NimbusRequestManagerDelegate

extension GAMAdLoaderBannerViewController: NimbusRequestManagerDelegate {
func didCompleteNimbusRequest(request: NimbusRequestKit.NimbusRequest, ad: NimbusCoreKit.NimbusAd) {
func didCompleteNimbusRequest(request: NimbusRequest, ad: NimbusAd) {
print("didCompleteNimbusRequest")

ad.applyDynamicPrice(into: gamRequest, mapping: mapping)
self.ad = ad
// Remove old bannerView if exists
bannerView?.removeFromSuperview()

adLoader = GADAdLoader(
adUnitID: googleDynamicPricePlacementId,
rootViewController: self,
adTypes: [.gamBanner],
options: nil
)
adLoader?.delegate = self
adLoader?.load(gamRequest)
load(ad: ad)
}

func didFailNimbusRequest(request: NimbusRequestKit.NimbusRequest, error: NimbusCoreKit.NimbusError) {
func didFailNimbusRequest(request: NimbusRequest, error: NimbusError) {
print("didFailNimbusRequest: \(error.localizedDescription)")
load()
}
}
80 changes: 62 additions & 18 deletions Application/Sources/DynamicPrice/GAMBannerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,62 @@ import NimbusSDK
import GoogleMobileAds

class GAMBannerViewController: GAMBaseViewController {
private static let refreshInterval: TimeInterval = 30

private let requestManager = NimbusRequestManager()
private lazy var dynamicPriceRenderer: NimbusDynamicPriceRenderer = {
return NimbusDynamicPriceRenderer(requestManager: requestManager)
}()

private let gamRequest = GAMRequest()
private let bannerView = GAMBannerView(adSize: GADAdSizeBanner)
private var refreshTimer: Timer?

/// Set this to false if you don't want a refreshing banner
private var isRefreshingBanner = true

deinit {
NotificationCenter.default.removeObserver(self)
}

override func viewDidLoad() {
super.viewDidLoad()

setupBannerView()

if isRefreshingBanner {
setupNotifications()
setupRefreshTimer()
}

requestManager.delegate = self

fetchNimbusBid()
}

func setupNotifications() {
NotificationCenter.default.addObserver(
self,
selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(appWillResignActive),
name: UIApplication.willResignActiveNotification,
object: nil
)
}

func fetchNimbusBid() {
requestManager.performRequest(request: NimbusRequest.forBannerAd(position: headerSubTitle))
}

func setupBannerView() {
bannerView.translatesAutoresizingMaskIntoConstraints = false
bannerView.adUnitID = googleDynamicPricePlacementId
bannerView.delegate = self
bannerView.rootViewController = self
bannerView.appEventDelegate = self
bannerView.paidEventHandler = { [weak self] adValue in
guard let bannerView = self?.bannerView else { return }
self?.dynamicPriceRenderer.notifyBannerPrice(adValue: adValue, bannerView: bannerView)
bannerView.applyDynamicPrice(requestManager: requestManager, delegate: self)
bannerView.paidEventHandler = { [weak bannerView] adValue in
bannerView?.updatePrice(adValue)
}

view.addSubview(bannerView)
Expand All @@ -49,14 +79,33 @@ class GAMBannerViewController: GAMBaseViewController {
bannerView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
])
}

// MARK: - Refreshing Banner Logic

func setupRefreshTimer() {
refreshTimer?.invalidate() // just to make sure there's no outstanding timer
refreshTimer = Timer.scheduledTimer(withTimeInterval: Self.refreshInterval, repeats: true) { [weak self] _ in
self?.fetchNimbusBid()
}
print("\(Self.self) created refresh timer")
}

@objc private func appDidBecomeActive() {
setupRefreshTimer()
}

@objc private func appWillResignActive() {
refreshTimer?.invalidate()
print("\(Self.self) removed refresh timer")
}
}

// MARK: - GADAppEventDelegate

extension GAMBannerViewController: GADAppEventDelegate {
func adView(_ banner: GADBannerView, didReceiveAppEvent name: String, withInfo info: String?) {
print("adView:didReceiveAppEvent")
dynamicPriceRenderer.handleBannerEventForNimbus(bannerView: banner, name: name, info: info)
bannerView.handleEventForNimbus(name: name, info: info)
}
}

Expand All @@ -69,12 +118,10 @@ extension GAMBannerViewController: GADBannerViewDelegate {

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

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

func bannerViewDidRecordClick(_ bannerView: GADBannerView) {
Expand All @@ -97,17 +144,14 @@ extension GAMBannerViewController: GADBannerViewDelegate {
// MARK: - NimbusRequestManagerDelegate

extension GAMBannerViewController: NimbusRequestManagerDelegate {
func didCompleteNimbusRequest(request: NimbusRequestKit.NimbusRequest, ad: NimbusCoreKit.NimbusAd) {
func didCompleteNimbusRequest(request: NimbusRequest, ad: NimbusAd) {
print("didCompleteNimbusRequest")

dynamicPriceRenderer.willRender(ad: ad, bannerView: bannerView)

ad.applyDynamicPrice(into: gamRequest, mapping: mapping)
bannerView.load(gamRequest)
bannerView.loadDynamicPrice(gamRequest: GAMRequest(), ad: ad, mapping: mapping)
}

func didFailNimbusRequest(request: NimbusRequestKit.NimbusRequest, error: NimbusCoreKit.NimbusError) {
func didFailNimbusRequest(request: NimbusRequest, error: NimbusError) {
print("didFailNimbusRequest: \(error.localizedDescription)")
bannerView.loadDynamicPrice(gamRequest: GAMRequest(), mapping: mapping)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class GAMInterstitialViewController: GAMBaseViewController {
) { [weak self] interstitialAd, error in
if let error {
if let nimbusAd {
self?.requestManager.notifyNoFill(ad: nimbusAd)
self?.requestManager.notifyError(ad: nimbusAd, error: error)
}

print("Failed to load dynamic price interstitial ad with error: \(error.localizedDescription)")
Expand Down

0 comments on commit 1782b16

Please sign in to comment.