// AdsEngineManager.swift // Manages all ad engines // Corresponds to AdsEngineManager.kt in Android implementation import Foundation import UIKit class AdsEngineManager { private let viewController: UIViewController private var interstitialAdEngines: [Int: InterstitialAdEngine] = [:] private var rewardedAdEngines: [Int: RewardedAdEngine] = [:] private var bannerAdEngines: [Int: BannerAdEngine] = [:] private var mrecAdEngines: [Int: MRecAdEngine] = [:] private var supportedAdsSdk: [AdPlatform: GuruAdsSdk] = [:] init(viewController: UIViewController) { self.viewController = viewController } // MARK: - Logging private func logInfo(_ message: String) { Logger.i(tag: "StrategyManager", message: message) } private func logError(_ message: String, error: Error? = nil) { Logger.e(tag: "StrategyManager", message: message) } // MARK: - SDK Management internal func addAdsSdk(_ adsSdk: GuruAdsSdk) { supportedAdsSdk[adsSdk.adPlatform] = adsSdk } private func getAdsSdk(adPlatform: AdPlatform) -> GuruAdsSdk? { return supportedAdsSdk[adPlatform] } // MARK: - Interstitial Engine Methods private func createDefaultInterstitialAdEngine( id: Int, config: DefaultInterstitialConfig ) -> DefaultInterstitialAdEngine? { if let adSdk = getAdsSdk(adPlatform: AdPlatform.fromName(config.adPlatform))?.obtainInterstitialAd( adConfig: AdConfig( engineId: id, adUnitId: config.adUnitId, adAmazonSlotId: config.adAmazonSlotId ) ) { return DefaultInterstitialAdEngine(viewController: viewController, id: id, ad: adSdk) } else { return nil; } } private func createMabInterstitialEngine( id: Int, faUnitId: String, config: MabInterstitialConfig ) -> MabInterstitialEngine { return MabInterstitialEngine(viewController: viewController, id: id, faUnitId: faUnitId, mabConfig: config) { [weak self] adPlatform, adConfig in self?.getAdsSdk(adPlatform: adPlatform)?.obtainInterstitialAd(adConfig: adConfig) } } func getInterstitialAdEngine(id: Int) -> InterstitialAdEngine? { return interstitialAdEngines[id] } func createInterstitialAdEngine(adEngineConfig: AdEngineConfig) -> InterstitialAdEngine? { // Potential hashcode collision let id = adEngineConfig.hashCode() logInfo("createInterstitialAdEngine: \(id)") let engine:InterstitialAdEngine? = getInterstitialAdEngine(id: id) ?? { logInfo("config parsed!, with strategy \(InterstitialStrategy.default.rawValue)") switch adEngineConfig.strategy { case InterstitialStrategy.default.rawValue: guard let config = try? DefaultInterstitialConfig(adEngineConfig.config) else { return nil; } return createDefaultInterstitialAdEngine( id: id, config: config ) case InterstitialStrategy.mab.rawValue: guard let config = try? MabInterstitialConfig(adEngineConfig.config) else { return nil; } return createMabInterstitialEngine(id: id, faUnitId: adEngineConfig.faUnitId, config: config) default: // todo forward compatible guard let config = try? DefaultInterstitialConfig(adEngineConfig.config) else { return nil; } return createDefaultInterstitialAdEngine( id: id, config:config ) } }() if let engine = engine { interstitialAdEngines[id] = engine } return engine } // MARK: - Rewarded Engine Methods private func createDefaultRewardedAdEngine( id: Int, config: DefaultRewardedConfig ) -> DefaultRewardedAdEngine? { if let adSdk = getAdsSdk(adPlatform: AdPlatform.fromName(config.adPlatform))?.obtainRewardedAd( adConfig: AdConfig( engineId: id, adUnitId: config.adUnitId, adAmazonSlotId: config.adAmazonSlotId ) ) { return DefaultRewardedAdEngine(viewController: viewController, id: id, ad: adSdk) } else { return nil; } } private func createMabRewardedEngine( id: Int, faUnitId: String, config: MabRewardedConfig ) -> MabRewardedEngine { return MabRewardedEngine(viewController: viewController, id: id, faUnitId: faUnitId, mabConfig: config) { [weak self] adPlatform, adConfig in self?.getAdsSdk(adPlatform: adPlatform)?.obtainRewardedAd(adConfig: adConfig) } } func getRewardedAdEngine(id: Int) -> RewardedAdEngine? { return rewardedAdEngines[id] } func createRewardedAdEngine(adEngineConfig: AdEngineConfig) -> RewardedAdEngine? { let id = adEngineConfig.hashCode() logInfo("createRewardedAdEngine: \(id)") let engine = getRewardedAdEngine(id: id) ?? { switch adEngineConfig.strategy { case RewardedStrategy.default.rawValue: guard let config = try? DefaultRewardedConfig(adEngineConfig.config) else { return nil; } return createDefaultRewardedAdEngine( id: id, config: config ) case RewardedStrategy.mab.rawValue: guard let config = try? MabRewardedConfig(adEngineConfig.config) else { return nil; } return createMabRewardedEngine(id: id, faUnitId: adEngineConfig.faUnitId, config: config) default: guard let config = try? DefaultRewardedConfig(adEngineConfig.config) else { return nil; } return createDefaultRewardedAdEngine( id: id, config: config ) } }() if let engine = engine { rewardedAdEngines[id] = engine } return engine } // MARK: - Banner Engine Methods private func createDefaultBannerAdEngine( id: Int, config: DefaultBannerConfig ) -> DefaultBannerAdEngine? { if let adSdk = getAdsSdk(adPlatform: AdPlatform.fromName(config.adPlatform))?.obtainBannerAd( adConfig: AdConfig( engineId: id, adUnitId: config.adUnitId, adAmazonSlotId: config.adAmazonSlotId ) ) { return DefaultBannerAdEngine(viewController: viewController, id: id, ad: adSdk) } else { return nil; } } func getBannerAdEngine(id: Int) -> BannerAdEngine? { return bannerAdEngines[id] } func createBannerAdEngine(adEngineConfig: AdEngineConfig) -> BannerAdEngine? { let id = adEngineConfig.hashCode() logInfo("createBannerAdEngine: \(id)") let engine = getBannerAdEngine(id: id) ?? { switch adEngineConfig.strategy { case BannerStrategy.default.rawValue: guard let config = try? DefaultBannerConfig(adEngineConfig.config) else { return nil; } return createDefaultBannerAdEngine( id: id, config: config ) default: guard let config = try? DefaultBannerConfig(adEngineConfig.config) else { return nil; } return createDefaultBannerAdEngine( id: id, config: config ) } }() if let engine = engine { bannerAdEngines[id] = engine } return engine } // MARK: - MRec Engine Methods private func createDefaultMRecAdEngine( id: Int, config: DefaultMRecConfig ) -> DefaultMRecAdEngine? { if let adSdk = getAdsSdk(adPlatform: AdPlatform.fromName(config.adPlatform))?.obtainMRecAd( adConfig: AdConfig( engineId: id, adUnitId: config.adUnitId, adAmazonSlotId: config.adAmazonSlotId ) ) { return DefaultMRecAdEngine(viewController: viewController, id: id, ad: adSdk) } else { return nil; } } func getMRecAdEngine(id: Int) -> MRecAdEngine? { return mrecAdEngines[id] } func createMRecAdEngine(adEngineConfig: AdEngineConfig) -> MRecAdEngine? { let id = adEngineConfig.hashCode() logInfo("createMRecAdEngine: \(id)") let engine = getMRecAdEngine(id: id) ?? { switch adEngineConfig.strategy { case BannerStrategy.default.rawValue: guard let config = try? DefaultMRecConfig(adEngineConfig.config) else { return nil; } return createDefaultMRecAdEngine( id: id, config: config ) default: guard let config = try? DefaultMRecConfig(adEngineConfig.config) else { return nil; } return createDefaultMRecAdEngine( id: id, config: config ) } }() if let engine = engine { mrecAdEngines[id] = engine } return engine } func dispatchOrientationUpdate(orientation: ScreenOrientation) { let body: (Any) throws -> Void = { it in (it as? OrientationAware)?.onOrientationUpdate(orientation: orientation) } try? interstitialAdEngines.values.forEach(body) try? bannerAdEngines.values.forEach(body) try? rewardedAdEngines.values.forEach(body) try? mrecAdEngines.values.forEach(body) } // for testing only! func clearCachedEngines() { interstitialAdEngines.removeAll() bannerAdEngines.removeAll() rewardedAdEngines.removeAll() mrecAdEngines.removeAll() } } // MARK: - Hashable Extension for AdEngineConfig extension AdEngineConfig: Hashable { func hashCode() -> Int { return hashValue } public static func == (lhs: AdEngineConfig, rhs: AdEngineConfig) -> Bool { return lhs.strategy == rhs.strategy && (lhs.config as NSDictionary).isEqual(to: rhs.config) } public func hash(into hasher: inout Hasher) { hasher.combine(strategy) for (key, value) in config { hasher.combine(key) if let hashableValue = value as? (any Hashable) { hasher.combine(hashableValue) } } } } // MARK: - Hashable Extensions extension Int { func hashCode() -> Int { return self } } extension String { func hashCode() -> Int { var h: Int = 0 for c in self.unicodeScalars { h = 31 &* h &+ Int(c.value) } return h } }