425 lines
14 KiB
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))
|
|
}
|
|
}
|