FusionAds-iOS/FusionAds/Classes/fusion/engine/base/interstitial/DefaultInterstitialAdEngine...

384 lines
13 KiB
Swift

// DefaultInterstitialAdEngine.swift
// Default implementation of InterstitialAdEngine
// Corresponds to DefaultInterstitialAdEngine.kt in Android implementation
import Foundation
import UIKit
public class DefaultInterstitialAdEngine: InterstitialAdEngine {
private let ad: GuruInterstitialAd
// Constants
private enum Action {
static let LOAD = 1
static let SHOW = 2
static let DESTROY = 3
static let RETRY = 4
}
private enum Event {
static let ON_LOADED = 100
static let ON_LOAD_FAILED = 101
static let ON_DISPLAYED = 102
static let ON_DISPLAY_FAILED = 103
static let ON_CLICK = 104
static let ON_HIDDEN = 105
static let ON_USER_REWARDED = 106
static let REVENUE_PAID = 107
static let LOAD_TIMEOUT = 108
static let SHOW_TIMEOUT = 109
}
private static let LOAD_TIMEOUT_MILLIS: Int = 120_000
private static let SHOW_TIMEOUT_MILLIS: Int = 120_000
public override var supportedAdPlatforms: Set<AdPlatform> {
return [ad.adPlatform]
}
public init(viewController: UIViewController, id: Int, ad: GuruInterstitialAd) {
self.ad = ad
super.init(viewController: viewController, id: id, adType: .Interstitial, strategyName: ad.adPlatform.name)
setupAdListener()
initialize()
}
private func setupAdListener() {
ad.listener = FusionAdListenerImpl { [weak self] event in
guard let self = self else { return }
switch event {
case .onAdLoaded(let ad):
self.sendMessage(what: Event.ON_LOADED, obj: EventParams(ad: ad))
case .onAdDisplayed(let ad):
self.sendMessage(what: Event.ON_DISPLAYED, obj: EventParams(ad: ad))
case .onAdHidden(let ad):
self.sendMessage(what: Event.ON_HIDDEN, obj: EventParams(ad: ad))
case .onUserRewarded(let ad, let reward):
// Not used for interstitial ads, but handled for completeness
break
case .onAdClicked(let ad):
self.sendMessage(what: Event.ON_CLICK, obj: EventParams(ad: ad))
case .onAdLoadFailed(let loadFailedInfo):
self.sendMessage(what: Event.ON_LOAD_FAILED, obj: EventParams(loadFailedInfo: loadFailedInfo))
case .onAdDisplayFailed(let ad, let error):
self.sendMessage(what: Event.ON_DISPLAY_FAILED, obj: EventParams(ad: ad, displayFailedInfo: error))
case .onAdRevenuePaid(let ad):
self.sendMessage(what: Event.REVENUE_PAID, obj: EventParams(ad: ad))
}
}
}
// MARK: - State Classes
public override func createIdleState() -> Idle {
return IdleImpl(self)
}
public override func createLoadingState() -> Loading {
return LoadingImpl(self)
}
public override func createLoadedState() -> Loaded {
return LoadedImpl(self)
}
public override func createShowingState() -> Showing {
return ShowingImpl(self)
}
// MARK: - Request Methods
override internal func requestLoad() {
sendMessage(what: Action.LOAD)
}
override internal func requestShow(request: InterstitialShowRequest?) {
sendMessage(what: Action.SHOW, obj: request)
}
override internal func requestDestroy() {
sendMessage(what: Action.DESTROY)
}
// MARK: - Helper Methods
private func handleUnhandledMessage(_ msg: Message) -> Bool {
switch msg.what {
case Action.DESTROY:
ad.destroy()
transitionTo(idleState)
return true
default:
return false
}
}
// MARK: - State Implementations
private class IdleImpl: Idle {
private let engine: DefaultInterstitialAdEngine
init(_ engine: DefaultInterstitialAdEngine) {
self.engine = engine
super.init()
}
public override func enter(from: IState?, params: Any?) {
super.enter(from: from, params: params)
}
public override func processMessage(_ msg: Message) -> Bool {
switch msg.what {
case Action.LOAD:
if engine.ad.load() {
engine.transitionTo(engine.loadingState)
} else {
engine.adLoadFailed(loadFailedInfo: LoadFailedInfo(
engineId: engine.id,
adPlatform: engine.ad.adPlatform,
adType: engine.adType,
adUnitId: engine.ad.adUnitId,
error: LoadAdRequestError()
))
}
return true
default:
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
}
}
}
class LoadingImpl: Loading {
private var retryCount = 0
private let engine: DefaultInterstitialAdEngine
init(_ engine: DefaultInterstitialAdEngine) {
self.engine = engine
super.init()
}
public override func enter(from: IState?, params: Any?) {
super.enter(from: from, params: params)
retryCount = 0
// Set load timeout
engine.sendMessageDelayed(what: Event.LOAD_TIMEOUT, delayMillis: DefaultInterstitialAdEngine.LOAD_TIMEOUT_MILLIS)
}
public override func exit(to: IState?) {
engine.removeMessages(what: Event.LOAD_TIMEOUT)
engine.removeMessages(what: Action.RETRY)
retryCount = 0
super.exit(to: to)
}
public override func processMessage(_ msg: Message) -> Bool {
switch msg.what {
case Action.RETRY:
retryCount += 1
if !engine.ad.load() {
engine.sendMessage(what: Event.ON_LOAD_FAILED, obj: EventParams(
loadFailedInfo: LoadFailedInfo(
engineId: engine.id,
adPlatform: engine.ad.adPlatform,
adType: engine.adType,
adUnitId: engine.ad.adUnitId,
error: LoadAdRequestError()
)
))
} else {
engine.sendMessageDelayed(what: Event.LOAD_TIMEOUT, delayMillis: DefaultInterstitialAdEngine.LOAD_TIMEOUT_MILLIS)
}
return true
case Action.LOAD:
engine.logWarn("Already loading! Ignore load request")
return true
case Event.LOAD_TIMEOUT:
engine.removeMessages(what: Action.RETRY)
engine.sendMessage(what: Action.RETRY)
return true
case Event.ON_LOADED:
if let eventParams = msg.obj as? EventParams, eventParams.ad != nil {
engine.transitionTo(engine.loadedState, params: eventParams)
}
engine.deferMessage(msg)
return true
case Event.ON_LOAD_FAILED:
let params = msg.obj as? EventParams
engine.adLoadFailed(loadFailedInfo: params?.loadFailedInfo ?? LoadFailedInfo(
engineId: engine.id,
adPlatform: engine.ad.adPlatform,
adType: engine.adType,
adUnitId: engine.ad.adUnitId,
error: LoadAdRequestError()
))
engine.transitionTo(engine.idleState)
return true
default:
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
}
}
}
class LoadedImpl: Loaded {
private let engine: DefaultInterstitialAdEngine
init(_ engine: DefaultInterstitialAdEngine) {
self.engine = engine
super.init()
}
public override func enter(from: IState?, params: Any?) {
super.enter(from: from, params: params)
if let eventParams = params as? EventParams, let ad = eventParams.ad {
engine.adLoaded(ad: ad)
}
}
public override func processMessage(_ msg: Message) -> Bool {
switch msg.what {
case Action.SHOW:
let request = msg.obj as? InterstitialShowRequest
if engine.ad.show(request) {
engine.transitionTo(engine.showingState)
} else {
engine.logWarn("Failed to show interstitial ad")
// no FusionAd here
// engine.adDisplayFailed(ad: engine.ad, error: ShowAdRequestError())
if(engine.ad.load()) {
engine.transitionTo(engine.loadingState)
}
}
return true
default:
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
}
}
}
class ShowingImpl: Showing {
private let engine: DefaultInterstitialAdEngine
init(_ engine: DefaultInterstitialAdEngine) {
self.engine = engine
super.init()
}
public override func enter(from: IState?, params: Any?) {
super.enter(from: from, params: params)
engine.sendMessageDelayed(what: Event.SHOW_TIMEOUT, delayed: .milliseconds(SHOW_TIMEOUT_MILLIS))
}
public override func processMessage(_ msg: Message) -> Bool {
switch msg.what {
case Event.ON_DISPLAYED:
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
engine.adDisplayed(ad: ad)
}
engine.removeMessages(what: Event.SHOW_TIMEOUT)
return true
case Event.ON_CLICK:
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
engine.adClicked(ad: ad)
}
return true
case Event.ON_HIDDEN:
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
engine.adHidden(ad: ad)
engine.transitionTo(engine.idleState)
}
return true
case Event.REVENUE_PAID:
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
engine.adRevenuePaid(ad: ad)
}
return true
case Event.ON_DISPLAY_FAILED:
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
engine.adDisplayFailed(ad: ad, error: eventParams.displayFailedInfo)
}
engine.transitionTo(engine.idleState)
engine.requestLoad()
return true
default:
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
}
}
}
}
// Helper class to implement the FusionAdListener protocol
private class FusionAdListenerImpl: FusionAdListener {
enum Event {
case onAdLoaded(ad: FusionAd)
case onAdDisplayed(ad: FusionAd)
case onAdHidden(ad: FusionAd)
case onUserRewarded(ad: FusionAd, reward: FusionReward?)
case onAdClicked(ad: FusionAd)
case onAdLoadFailed(loadFailedInfo: LoadFailedInfo)
case onAdDisplayFailed(ad: FusionAd, error: FusionError?)
case onAdRevenuePaid(ad: FusionAd)
}
private let handler: (Event) -> Void
init(handler: @escaping (Event) -> Void) {
self.handler = handler
}
func onAdLoaded(ad: FusionAd) {
handler(.onAdLoaded(ad: ad))
}
func onAdDisplayed(ad: FusionAd) {
handler(.onAdDisplayed(ad: ad))
}
func onAdHidden(ad: FusionAd) {
handler(.onAdHidden(ad: ad))
}
func onUserRewarded(ad: FusionAd, reward: FusionReward?) {
handler(.onUserRewarded(ad: ad, reward: reward))
}
func onAdClicked(ad: FusionAd) {
handler(.onAdClicked(ad: ad))
}
func onAdLoadFailed(loadFailedInfo: LoadFailedInfo) {
handler(.onAdLoadFailed(loadFailedInfo: loadFailedInfo))
}
func onAdDisplayFailed(ad: FusionAd, error: FusionError?) {
handler(.onAdDisplayFailed(ad: ad, error: error))
}
func onAdRevenuePaid(ad: FusionAd) {
handler(.onAdRevenuePaid(ad: ad))
}
}