FusionAds-iOS/FusionAds/Classes/fusion/engine/base/mrec/DefaultMRecAdEngine.swift

425 lines
14 KiB
Swift

// DefaultMRecAdEngine.swift
// Default implementation of MRecAdEngine
// Corresponds to DefaultMRecAdEngine.kt in Android implementation
import Foundation
import UIKit
public class DefaultMRecAdEngine: MRecAdEngine {
private let ad: GuruMRecAd
private var currentShowRequest: MRecShowRequest?
// Constants
private enum Action {
static let LOAD = 1
static let SHOW = 2
static let HIDE = 3
static let DESTROY = 4
static let RETRY = 5
}
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: TimeInterval = 120000 / 1000.0
private static let SHOW_TIMEOUT_MILLIS: TimeInterval = 120000 / 1000.0
public override var supportedAdPlatforms: Set<AdPlatform> {
return [ad.adPlatform]
}
public init(viewController: UIViewController, id: Int, ad: GuruMRecAd) {
self.ad = ad
super.init(viewController: viewController, id: id, adType: .MRec, 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):
// Not used for MREC ads, but handled for completeness
break
case .onAdHidden(let ad):
// Not used for MREC ads, but handled for completeness
break
case .onUserRewarded(let ad, let reward):
// Not used for MREC 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 createActiveState() -> Active {
return ActiveImpl(self)
}
public override func createShowingState() -> Showing {
return ShowingImpl(self)
}
public override func createHiddenState() -> Hidden {
return HiddenImpl(self)
}
// MARK: - Request Methods
override internal func requestLoad() {
sendMessage(what: Action.LOAD)
}
override internal func requestShow(_ request: MRecShowRequest? = nil) {
currentShowRequest = request
sendMessage(what: Action.SHOW, obj: request)
}
override internal func requestHide() {
sendMessage(what: Action.HIDE)
}
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
class IdleImpl: Idle {
private let engine: DefaultMRecAdEngine
init(_ engine: DefaultMRecAdEngine) {
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: DefaultMRecAdEngine
init(_ engine: DefaultMRecAdEngine) {
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: Int(DefaultMRecAdEngine.LOAD_TIMEOUT_MILLIS * 1000))
}
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: Int(DefaultMRecAdEngine.LOAD_TIMEOUT_MILLIS * 1000))
}
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.hiddenState, 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 ActiveImpl: Active {
private let engine: DefaultMRecAdEngine
init(_ engine: DefaultMRecAdEngine) {
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 Event.ON_CLICK:
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
engine.adClicked(ad: ad)
}
return true
case Event.REVENUE_PAID:
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
engine.adRevenuePaid(ad: ad)
}
return true
default:
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
}
}
}
class ShowingImpl: Showing {
private let engine: DefaultMRecAdEngine
init(_ engine: DefaultMRecAdEngine) {
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.adDisplayed(ad: ad)
}
}
public override func processMessage(_ msg: Message) -> Bool {
switch msg.what {
case Action.HIDE:
if engine.ad.hide() {
engine.transitionTo(engine.hiddenState)
} else {
engine.logWarn("Failed to hide MREC")
}
return true
case Action.SHOW:
engine.logWarn("Already showing! Ignore show request")
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.hiddenState)
engine.requestLoad()
return true
default:
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
}
}
}
class HiddenImpl: Hidden {
private let engine: DefaultMRecAdEngine
init(_ engine: DefaultMRecAdEngine) {
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? MRecShowRequest
if engine.ad.show(request: request) {
engine.transitionTo(engine.showingState)
} else {
engine.logWarn("Failed to show MREC")
// no FusionAd here
// engine.adDisplayFailed(ad: engine.ad, error: ShowAdRequestError())
if(engine.load()) {
engine.transitionTo(engine.loadingState)
}
}
return true
case Action.HIDE:
engine.logWarn("Already hidden! Ignore hide request")
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))
}
}