// StrategyConfig.swift // Defines configuration structures for ad strategies // Corresponds to StrategyConfig.kt in Android implementation import Foundation // MARK: - Strategy Types public enum InterstitialStrategy: Int { case `default` = 0 case mab = 1 } public enum RewardedStrategy: Int { case `default` = 0 case mab = 1 } public enum BannerStrategy: Int { case `default` = 0 } protocol StrategyConfig: Decodable {} // MARK: - Default Config Classes public struct DefaultInterstitialConfig: Codable, StrategyConfig { public let adPlatform: String public let adUnitId: String public let adAmazonSlotId: String? public init(adPlatform: String, adUnitId: String, adAmazonSlotId: String? = nil) { self.adPlatform = adPlatform self.adUnitId = adUnitId self.adAmazonSlotId = adAmazonSlotId } private enum CodingKeys : String, CodingKey { case adPlatform = "ad_platform", adUnitId = "ad_unit_id", adAmazonSlotId = "ad_amz_slot_id" } } public struct DefaultRewardedConfig: Codable, StrategyConfig { public let adPlatform: String public let adUnitId: String public let adAmazonSlotId: String? public init(adPlatform: String, adUnitId: String, adAmazonSlotId: String? = nil) { self.adPlatform = adPlatform self.adUnitId = adUnitId self.adAmazonSlotId = adAmazonSlotId } private enum CodingKeys : String, CodingKey { case adPlatform = "ad_platform", adUnitId = "ad_unit_id", adAmazonSlotId = "ad_amz_slot_id" } } public struct DefaultBannerConfig: Codable, StrategyConfig { public let adPlatform: String public let adUnitId: String public let adAmazonSlotId: String? public init(adPlatform: String, adUnitId: String, adAmazonSlotId: String? = nil) { self.adPlatform = adPlatform self.adUnitId = adUnitId self.adAmazonSlotId = adAmazonSlotId } private enum CodingKeys : String, CodingKey { case adPlatform = "ad_platform", adUnitId = "ad_unit_id", adAmazonSlotId = "ad_amz_slot_id" } } public struct DefaultMRecConfig: Codable, StrategyConfig { public let adPlatform: String public let adUnitId: String public let adAmazonSlotId: String? public init(adPlatform: String, adUnitId: String, adAmazonSlotId: String? = nil) { self.adPlatform = adPlatform self.adUnitId = adUnitId self.adAmazonSlotId = adAmazonSlotId } private enum CodingKeys : String, CodingKey { case adPlatform = "ad_platform", adUnitId = "ad_unit_id", adAmazonSlotId = "ad_amz_slot_id" } } public struct MabRewardedConfig: Codable, StrategyConfig { public let aggregatorConfigs: [AggregatorConfig] public let showTimeoutInSecond: Double? init(aggregatorConfigs: [AggregatorConfig], showTimeoutInSecond: Double?) { self.aggregatorConfigs = aggregatorConfigs self.showTimeoutInSecond = showTimeoutInSecond } private enum CodingKeys : String, CodingKey { case aggregatorConfigs = "aggregator_configs", showTimeoutInSecond = "show_timeout_second" } } public struct MabInterstitialConfig: Codable, StrategyConfig { public let aggregatorConfigs: [AggregatorConfig] public let showTimeoutInSecond: Double? init(aggregatorConfigs: [AggregatorConfig], showTimeoutInSecond: Double?) { self.aggregatorConfigs = aggregatorConfigs self.showTimeoutInSecond = showTimeoutInSecond } private enum CodingKeys : String, CodingKey { case aggregatorConfigs = "aggregator_configs", showTimeoutInSecond = "show_timeout_second" } } public struct AggregatorConfig: Codable { public let aggregatorId: String public let adPlatform: String public let raiseRate: Double public let selfRaiseRate: Double? public let cacheExpireInSecond: Double public let priority: Int public let adUnitIds: [AdUnitSpec] public let loadTimeoutInSecond: Double? init(aggregatorId: String, adPlatform: String, raiseRate: Double, selfRaiseRate: Double?, cacheExpireInSecond: Double, priority: Int, adUnitIds: [AdUnitSpec], loadTimeoutInSecond: Double?) { self.aggregatorId = aggregatorId self.adPlatform = adPlatform self.raiseRate = raiseRate self.selfRaiseRate = selfRaiseRate self.cacheExpireInSecond = cacheExpireInSecond self.priority = priority self.adUnitIds = adUnitIds self.loadTimeoutInSecond = loadTimeoutInSecond } private enum CodingKeys : String, CodingKey { case aggregatorId = "aggregator_id", adPlatform = "ad_platform", raiseRate = "raise_rate", selfRaiseRate = "self_raise_rate", cacheExpireInSecond = "cache_expire_second", priority = "priority", adUnitIds = "ad_unit_ids", loadTimeoutInSecond = "load_timeout_second" } } public struct AdUnitSpec: Codable { public let adUnitId: String public let adAmazonSlotId: String? public let floorEcpm: Double public var floor: Double { return floorEcpm / 1000 } init(adUnitId: String, adAmazonSlotId: String?, floorEcpm: Double = 0.0) { self.adUnitId = adUnitId self.adAmazonSlotId = adAmazonSlotId self.floorEcpm = floorEcpm } private enum CodingKeys : String, CodingKey { case adUnitId = "ad_unit_id", adAmazonSlotId = "ad_amz_slot_id", floorEcpm = "floor_ecpm" } } // MARK: - AdEngineConfig public struct AdEngineConfig: Codable { public let strategy: Int public let faUnitId: String public let config: [String: Any] public init(strategy: Int, faUnitId: String, config: [String: Any]) { self.strategy = strategy self.faUnitId = faUnitId self.config = config } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(strategy, forKey: .strategy) try container.encode(faUnitId, forKey: .faUnitId) try container.encode(JSONCodable(config), forKey: .config) } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) strategy = try container.decode(Int.self, forKey: .strategy) faUnitId = try container.decode(String?.self, forKey: .faUnitId) ?? "default" config = try container.decode(JSONCodable.self, forKey: .config).value as! Dictionary } private enum CodingKeys: String, CodingKey { case strategy = "strategy", config = "config", faUnitId = "fa_unit_id" } } private struct JSONCodable: Codable { let value: Any init(_ value: Any) { self.value = value } init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if container.decodeNil() { self.value = NSNull() } else if let bool = try? container.decode(Bool.self) { self.value = bool } else if let int = try? container.decode(Int.self) { self.value = int } else if let uint = try? container.decode(UInt.self) { self.value = uint } else if let double = try? container.decode(Double.self) { self.value = double } else if let string = try? container.decode(String.self) { self.value = string } else if let array = try? container.decode([JSONCodable].self) { self.value = array.map { $0.value } } else if let dictionary = try? container.decode([String: JSONCodable].self) { self.value = dictionary.mapValues { $0.value } } else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "AnyCodable cannot decode value" ) } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self.value { case is NSNull: try container.encodeNil() case let bool as Bool: try container.encode(bool) case let int as Int: try container.encode(int) case let uint as UInt: try container.encode(uint) case let double as Double: try container.encode(double) case let string as String: try container.encode(string) case let array as [Any]: try container.encode(array.map { JSONCodable($0) }) case let dictionary as [String: Any]: try container.encode(dictionary.mapValues { JSONCodable($0) }) default: let context = EncodingError.Context( codingPath: container.codingPath, debugDescription: "AnyCodable cannot encode value \(self.value)" ) throw EncodingError.invalidValue(self.value, context) } } } extension StrategyConfig { init(_ dict: [Key: Any]) throws { do { let data = try JSONSerialization.data(withJSONObject: dict, options: []) self = try JSONDecoder().decode(Self.self, from: data) } catch { Logger.e(tag: "StrategyConfig", message: "strategy config deserialize to \(Self.self) fail, data is \(dict)") throw error; } } }