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))
 | |
|     }
 | |
| }
 |