550 lines
20 KiB
Swift
550 lines
20 KiB
Swift
//
|
||
// bbbAdManager.swift
|
||
// playbtest
|
||
//
|
||
// Created by mac on 2025/3/11.
|
||
//
|
||
|
||
import Foundation
|
||
import AnyThinkInterstitial
|
||
import AnyThinkSDK
|
||
|
||
class bConfig: NSObject {
|
||
var appId:String = "h682ad31de9bb3"
|
||
/// 广告Key
|
||
var adKey:String = "af927fa8beee871757a1d54e560441d18"
|
||
|
||
/// 广告数组
|
||
var allAdIds:[String] = ["n682ae1dc6d1b6", "n682ae1c9416bb", "n682ae1b496431","n682ae0f785ed8"]
|
||
/// 广告数组
|
||
var adids:[String] = []
|
||
///设备ID
|
||
var adbrush_deviceid:String?
|
||
///最低ecpm
|
||
var adbrush_ecpm:Double = 0.0005
|
||
/// 本地ip
|
||
var adbrush_localip:String?
|
||
/// A面load show
|
||
var adbrush_base_url:String = "http://183.222.62.53:58078"
|
||
/// 本地服务
|
||
var adbrush_local_url:String = "http://127.0.0.1:6000"
|
||
|
||
/// 远程ip
|
||
var remouteIP:String = ""
|
||
/// dataId
|
||
var dataId:String = ""
|
||
|
||
///IDFA
|
||
var idfa:String = ""
|
||
|
||
|
||
|
||
///
|
||
var washParam:Bool = false
|
||
///
|
||
var linkId:String = ""
|
||
|
||
///load次数
|
||
var loadcount:Int = 0
|
||
///load次数
|
||
var loadcount1:Int = 0
|
||
|
||
var ipTime:Int = 0
|
||
|
||
override init() {
|
||
super.init()
|
||
|
||
if self.allAdIds.count > 3 {
|
||
self.adids = Array(self.allAdIds.shuffled().prefix(3))
|
||
} else {
|
||
self.adids = self.allAdIds
|
||
}
|
||
|
||
}
|
||
|
||
///是否正在展示中
|
||
@objc dynamic var isadsureshow:Bool = false
|
||
|
||
|
||
func getRandomString() -> String? {
|
||
return adids.randomElement()
|
||
}
|
||
// 判断当前是否为广告不量模式
|
||
func isADSSMode() -> Bool {
|
||
// return true
|
||
return UserDefaults.standard.bool(forKey: "kLuxSSFaceKey")
|
||
}
|
||
}
|
||
|
||
class AdItem :NSObject, ATInterstitialDelegate {
|
||
|
||
|
||
func didFinishLoadingAD(withPlacementID placementID: String!) {
|
||
BbbAdManager.config.loadcount1 += 1
|
||
NSLog("XS- didFinishLoadingAD\(String(describing: placementID))")
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "加载广告1: \(String(describing: placementID)) 成功 - \(BbbAdManager.config.loadcount1)"])
|
||
changeStatus(st: 2)
|
||
}
|
||
|
||
|
||
// var interstitialAd:MAInterstitialAd!
|
||
var interstitialAdID: String = ""
|
||
|
||
var retryAttempt = 0.0
|
||
|
||
// 定义广告关闭后的操作闭包
|
||
var _onAdClosed: (() -> Void)?
|
||
var onStatusChange:((_:String,_:Int, _:Double) -> Void)?
|
||
|
||
var startLoadTime: DispatchTime?
|
||
|
||
// 0: 初始,1:加载中,2:加载完成,3:展示中,4:关闭,5:加载失败,6:展示失败
|
||
private(set) var status: Int = 0
|
||
|
||
private(set) var ecpm:Double = 0.0
|
||
|
||
|
||
init(adID:String){
|
||
|
||
super.init()
|
||
self.interstitialAdID = adID
|
||
// loadInterstitialAd()
|
||
|
||
}
|
||
func onAdClosed() {
|
||
if self._onAdClosed != nil {
|
||
self._onAdClosed!()
|
||
}
|
||
}
|
||
|
||
func changeStatus(st:Int) {
|
||
self.status = st
|
||
onStatusChange?(self.interstitialAdID, st, self.ecpm)
|
||
}
|
||
|
||
|
||
func loadInterstitialAd(){
|
||
NSLog("XS- placementIDLoad 1: ---- \(interstitialAdID)")
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text":"begin load:\(interstitialAdID)"])
|
||
|
||
startLoadTime = DispatchTime.now()
|
||
changeStatus(st: 1)
|
||
let extra: [String: Any] = [
|
||
kATAdLoadingExtraMediaExtraKey: "custom_values"
|
||
]
|
||
|
||
ATAdManager.shared().loadAD(withPlacementID: interstitialAdID, extra: extra, delegate: self)
|
||
NSLog("XS- placementIDLoad 2: ---- \(interstitialAdID)")
|
||
|
||
}
|
||
|
||
|
||
func showAd(viewController:UIViewController, onAdClosed: @escaping () -> Void) -> Bool{
|
||
self._onAdClosed = onAdClosed
|
||
NSLog("XS- onAdClosed set: \(self._onAdClosed != nil)")
|
||
if ATAdManager.shared().interstitialReady(forPlacementID: interstitialAdID) {
|
||
ATAdManager.shared().showInterstitial(withPlacementID: interstitialAdID, in: viewController, delegate: self)
|
||
NSLog("XS- placementIDShow : ---- \(interstitialAdID)")
|
||
return true
|
||
} else {
|
||
NSLog("XS- interstitialAdID no redy 插页广告尚未准备好")
|
||
// retryLoadAdIfNecessary()
|
||
}
|
||
return false
|
||
}
|
||
|
||
func to_network(_ network: Int) -> String {
|
||
switch network {
|
||
case 6:
|
||
return "Mintegral"
|
||
case 11:
|
||
return "Ironsource"
|
||
case 12:
|
||
return "UnityAds"
|
||
case 13:
|
||
return "Vungle"
|
||
case 50:
|
||
return "Pangle"
|
||
default:
|
||
return "Mintegral"
|
||
}
|
||
}
|
||
|
||
|
||
// 完成加载广告
|
||
func didFinishLoadingADSource(withPlacementID placementID: String!,extra: [AnyHashable : Any]?) {
|
||
|
||
BbbAdManager.config.loadcount += 1
|
||
|
||
|
||
var thatecpm = 0.0
|
||
if let adsourcePriceValue = extra?["adsource_price"] as? NSNumber {
|
||
thatecpm = Double(Float(truncating: adsourcePriceValue) / 1000)
|
||
} else {
|
||
NSLog("XS- not get type adsource_price or type not match")
|
||
}
|
||
if thatecpm > self.ecpm {
|
||
self.ecpm = thatecpm
|
||
}
|
||
NSLog("XS- ad load ok:\(BbbAdManager.config.linkId) - \(String(describing: placementID)) ecpm:\(self.ecpm * 1000)")
|
||
// 计算并打印加载时间
|
||
var time = 0
|
||
if let startTime = startLoadTime {
|
||
let loadDuration = calculateElapsedTime(since: startTime)
|
||
NSLog("XS- ad \(String(describing: placementID)) load time: \(loadDuration) ms")
|
||
time = loadDuration
|
||
}
|
||
|
||
let networkID:Int = extra?["network_firm_id"] as! Int
|
||
let network = to_network(networkID)
|
||
let country = extra?["country"] ?? ""
|
||
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "加载广告: \(String(describing: placementID)) 成功 - \(network), ecpm:\(self.ecpm * 1000) \(country) \(BbbAdManager.config.loadcount)"])
|
||
retryAttempt = 0 // 重置重试次数
|
||
|
||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||
guard self != nil else { return }
|
||
YL_NetWorkManager.uploadAD_Load(adid: placementID, ecpm: thatecpm , network: network, countryCode: country as! String, platformResponseTime: TimeInterval(time/1000) , dsp: "MTG", loadTime: time)
|
||
}
|
||
// 发布广告加载成功通知
|
||
// NotificationCenter.default.post(name: .adDidLoad, object: nil, userInfo: ["adId": ad.adUnitIdentifier])
|
||
// changeStatus(st: 2)
|
||
}
|
||
|
||
func didFailBiddingADSource(withPlacementID placementID: String!,extra: [AnyHashable : Any]?, error: (any Error)!) {
|
||
// BbbAdManager.config.loadcount += 1
|
||
NSLog("XS- load \(String(describing: placementID)) err.... :\(String(describing: error))")
|
||
/*
|
||
var time = 0
|
||
|
||
if let startTime = startLoadTime {
|
||
let loadDuration = calculateElapsedTime(since: startTime)
|
||
NSLog("XS- ad \(String(describing: placementID)) load time: \(loadDuration) ms")
|
||
time = loadDuration
|
||
}
|
||
|
||
|
||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||
guard self != nil else { return }
|
||
YL_NetWorkManager.uploadAD_Load(adid: placementID, ecpm: 0.0 , network: "", countryCode: "", platformResponseTime: TimeInterval(time/1000) , dsp: "MTG", loadTime: time,errMsg: "\(String(describing: error))")
|
||
}
|
||
*/
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "bidding error:\(String(describing: placementID) ),\(String(describing: error)) 失败"])
|
||
// self.onAdClosed()
|
||
// changeStatus(st: 5)
|
||
}
|
||
|
||
func didFailToLoadAD(withPlacementID placementID: String!, error: (any Error)!) {
|
||
BbbAdManager.config.loadcount += 1
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "加载:\(String(describing: placementID) ),\(String(describing: error)) 失败"])
|
||
NSLog("XS- load\(String(describing: placementID)) err.... :\(String(describing: error))")
|
||
// NotificationCenter.default.post(name: .adDidFailToLoad, object: nil, userInfo: ["adId": adUnitIdentifier])
|
||
var time = 0
|
||
if let startTime = startLoadTime {
|
||
let loadDuration = calculateElapsedTime(since: startTime)
|
||
NSLog("XS- ad \(String(describing: placementID)) load time: \(loadDuration) ms")
|
||
time = loadDuration
|
||
}
|
||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||
guard self != nil else { return }
|
||
YL_NetWorkManager.uploadAD_Load(adid: placementID, ecpm: 0.0 , network: "", countryCode: "", platformResponseTime: TimeInterval(time/1000) , dsp: "MTG", loadTime: time,errMsg: "\(String(describing: error))")
|
||
}
|
||
// self.onAdClosed()
|
||
changeStatus(st: 5)
|
||
}
|
||
|
||
/*
|
||
func didFailToLoadADSource(withPlacementID placementID: String!,extra: [AnyHashable : Any]?, error: (any Error)!) {
|
||
BbbAdManager.config.loadcount += 1
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "加载:\(String(describing: placementID) ),\(String(describing: error)) 失败"])
|
||
NSLog("XS- load\(String(describing: placementID)) err.... :\(String(describing: error))")
|
||
// NotificationCenter.default.post(name: .adDidFailToLoad, object: nil, userInfo: ["adId": adUnitIdentifier])
|
||
var thatecpm = 0.0
|
||
if let adsourcePriceValue = extra?["adsource_price"] as? NSNumber{
|
||
thatecpm = Double(Float(truncating: adsourcePriceValue) / 1000)
|
||
} else {
|
||
NSLog("XS- not get type adsource_price or type not match")
|
||
}
|
||
// self.ecpm = thatecpm
|
||
|
||
// 计算并打印加载时间
|
||
var time = 0
|
||
if let startTime = startLoadTime {
|
||
let loadDuration = calculateElapsedTime(since: startTime)
|
||
NSLog("广告 \(String(describing: placementID)) 加载时间: \(loadDuration) ms")
|
||
time = loadDuration
|
||
}
|
||
|
||
let networkID:Int = extra?["network_firm_id"] as! Int
|
||
let network = to_network(networkID)
|
||
let country = extra?["country"] ?? ""
|
||
|
||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||
guard self != nil else { return }
|
||
YL_NetWorkManager.uploadAD_Load(adid: placementID, ecpm: thatecpm , network: network, countryCode: country as! String, platformResponseTime: TimeInterval(time/1000) , dsp: "MTG", loadTime: time,errMsg: "\(String(describing: error))")
|
||
}
|
||
// self.onAdClosed()
|
||
changeStatus(st: 5)
|
||
}
|
||
*/
|
||
|
||
// 展示广告
|
||
func interstitialDidShow(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
|
||
NSLog("XS- show ok ad\(placementID)")
|
||
// NotificationCenter.default.post(name: .adDidDisplay, object: nil, userInfo: ["adId": ad.adUnitIdentifier])
|
||
|
||
// let currentIDFV = UIDevice.current.identifierForVendor?.uuidString
|
||
var ecpmprice: Double?
|
||
if let adsourcePriceValue = extra["adsource_price"] as? NSNumber{
|
||
|
||
ecpmprice = Double(Float(truncating: adsourcePriceValue)) / 1000
|
||
|
||
} else {
|
||
NSLog("XS- not get type adsource_price or type not match")
|
||
}
|
||
|
||
let networkID = extra["network_firm_id"] as! Int
|
||
let network = to_network(networkID)
|
||
let country = extra["country"] ?? ""
|
||
// let currentIDFV = UIDevice.current.identifierForVendor?.uuidString
|
||
|
||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||
guard self != nil else { return }
|
||
YL_NetWorkManager.showAd(adId: placementID, ecpm: ecpmprice, ad: true) {
|
||
self?.changeStatus(st: 4)
|
||
NSLog("XS- close ad ok\(placementID)")
|
||
self?.onAdClosed()
|
||
}
|
||
}
|
||
|
||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||
guard self != nil else { return }
|
||
YL_NetWorkManager.uploadAD_Show(adid: placementID, ecpm: ecpmprice ?? 0.0, network: network, countryCode: country as! String, platformResponseTime:0 , dsp: "MTG")
|
||
}
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "成功展示了ad\(placementID)"])
|
||
|
||
changeStatus(st: 3)
|
||
}
|
||
|
||
func interstitialDidClose(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
|
||
changeStatus(st: 4)
|
||
NSLog("XS- close ad ok\(placementID)")
|
||
self.onAdClosed()
|
||
}
|
||
|
||
func interstitialDidClick(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
|
||
NSLog("XS- ad click ok\(placementID)")
|
||
}
|
||
/*
|
||
func didFail(toDisplay ad: MAAd, withError error: MAError) {
|
||
changeStatus(st: 6)
|
||
print("展示广告\(ad.adUnitIdentifier),\(error)失败")
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "展示广告\(ad.adUnitIdentifier),\(error)失败"])
|
||
self.onAdClosed!()
|
||
}
|
||
*/
|
||
// 辅助函数以毫秒为单位计算经过的时间
|
||
private func calculateElapsedTime(since startTime: DispatchTime) -> Int {
|
||
let endTime = DispatchTime.now()
|
||
let nanoseconds = endTime.uptimeNanoseconds - startTime.uptimeNanoseconds
|
||
return Int(nanoseconds / 1_000_000) // 转换为毫秒
|
||
}
|
||
|
||
}
|
||
|
||
|
||
class BbbAdManager: NSObject {
|
||
static let shared = BbbAdManager()
|
||
static let config = bConfig()
|
||
|
||
///是否正在展示中
|
||
@objc dynamic var isshow:Bool = false
|
||
|
||
// 用于存储多个广告位的管理器,Key 是广告位的 ID
|
||
private var adItems: [String: AdItem] = [:]
|
||
|
||
var openADTimer:Timer?
|
||
let kOpenADPerSec: CGFloat = 1 // 假设的秒数
|
||
let kOpenAdCTimeLength: CGFloat = 60 // 假设的超时时长
|
||
private var view:UIViewController?
|
||
|
||
static var totalTimeC: CGFloat = 0.0
|
||
|
||
var isLoaded = false
|
||
|
||
// 添加广告位管理器
|
||
func add(adId: String) {
|
||
let adManager = AdItem(adID: adId)
|
||
adManager.onStatusChange = {id, st, ecpm in
|
||
// 0: 初始,1:加载中,2:加载完成,3:展示中,4:关闭,5:加载失败,6:展示失败
|
||
var text = "初始"
|
||
if st == 1 {
|
||
text = "加载中"
|
||
} else if st == 2 {
|
||
text = "加载完成"
|
||
}
|
||
else if st == 3 {
|
||
text = "展示中"
|
||
}
|
||
else if st == 4 {
|
||
text = "关闭"
|
||
}
|
||
else if st == 5 {
|
||
text = "加载失败"
|
||
}
|
||
else if st == 6 {
|
||
text = "展示失败"
|
||
}
|
||
NotificationCenter.default.post(name: NSNotification.Name("adStatus"), object: nil, userInfo: ["id": id, "text":"\(text),ecpm:\(ecpm * 1000)"])
|
||
}
|
||
adItems[adId] = adManager
|
||
adManager.loadInterstitialAd()
|
||
}
|
||
|
||
override init(){
|
||
|
||
super.init()
|
||
if BbbAdManager.config.isADSSMode() {
|
||
self.initConfig()
|
||
}
|
||
|
||
}
|
||
|
||
func initConfig () {
|
||
if #available(iOS 14, *) {
|
||
IDFA.shared.checkATT { idfa in
|
||
if let idfa = idfa {
|
||
NSLog("IDFA: \(idfa)")
|
||
BbbAdManager.config.idfa = idfa
|
||
} else {
|
||
NSLog("无法获取 IDFA")
|
||
}
|
||
}
|
||
} else {
|
||
IDFA.shared.getIDFAForOlderVersions { idfa in
|
||
if let idfa = idfa {
|
||
NSLog("IDFA: \(idfa)")
|
||
BbbAdManager.config.idfa = idfa
|
||
} else {
|
||
NSLog("无法获取 IDFA")
|
||
}
|
||
}
|
||
}
|
||
if let bfaceDict = UserDefaults.standard.dictionary(forKey: "bfaceDictKey"){
|
||
|
||
BbbAdManager.config.adbrush_base_url = bfaceDict["adbrush_base_url"] as? String ?? "http://183.222.62.53:58078"
|
||
|
||
BbbAdManager.config.adbrush_deviceid = bfaceDict["adbrush_deviceid"] as? String ?? ""
|
||
BbbAdManager.config.adbrush_localip = bfaceDict["adbrush_localip"] as? String ?? ""
|
||
BbbAdManager.config.remouteIP = bfaceDict["remouteIP"] as? String ?? ""
|
||
|
||
|
||
BbbAdManager.config.adbrush_local_url = bfaceDict["adbrush_local_url"] as? String ?? "http://127.0.0.1:6000"
|
||
BbbAdManager.config.dataId = bfaceDict["dataId"] as? String ?? ""
|
||
|
||
|
||
BbbAdManager.config.adbrush_ecpm = bfaceDict["adbrush_ecpm"] as? Double ?? 0.005
|
||
BbbAdManager.config.linkId = bfaceDict["linkId"] as? String ?? ""
|
||
BbbAdManager.config.washParam = bfaceDict["washParam"] as? Bool ?? false
|
||
} else {
|
||
NotificationCenter.default.post(name: NSNotification.Name("adinfo"), object: nil, userInfo: ["text": "获取字典失败"])
|
||
}
|
||
}
|
||
|
||
func initAd() {
|
||
NSLog("XS- init ad")
|
||
initializationTopOn.toponeSDK(BbbAdManager.config.appId,appKey: BbbAdManager.config.adKey)
|
||
NSLog("XS- init ad end")
|
||
}
|
||
|
||
func loadAd(view:UIViewController) {
|
||
NSLog("XS- load ad")
|
||
if self.isLoaded {
|
||
return
|
||
}
|
||
self.isLoaded = true
|
||
self.view = view
|
||
if BbbAdManager.config.washParam == true{
|
||
for (_, adId) in BbbAdManager.config.adids.enumerated() {
|
||
|
||
BbbAdManager.shared.add(adId: adId)
|
||
|
||
}
|
||
}else{
|
||
for (_, adId) in BbbAdManager.config.adids.enumerated() {
|
||
NSLog("XS- ad load start:\(BbbAdManager.config.linkId) - \(adId)")
|
||
BbbAdManager.shared.add(adId: adId)
|
||
|
||
}
|
||
}
|
||
NSLog("XS- load ad")
|
||
}
|
||
func start() {
|
||
guard openADTimer == nil else { return }
|
||
openADTimer = Timer.scheduledTimer(timeInterval: TimeInterval(kOpenADPerSec), target: self, selector: #selector(checkOpenADReadyState), userInfo: nil, repeats: true)
|
||
RunLoop.current.add(openADTimer!, forMode: .common)
|
||
}
|
||
func isEnd () -> Bool {
|
||
NSLog("XS- ad end")
|
||
if(self.isshow) {
|
||
NSLog("XS- ad end 1")
|
||
return false
|
||
}
|
||
for (_, ad) in BbbAdManager.shared.adItems {
|
||
if(ad.status == 1) {
|
||
NSLog("XS- ad end 2")
|
||
return false
|
||
}
|
||
if (ad.status == 2 && ad.ecpm >= BbbAdManager.config.adbrush_ecpm) {
|
||
NSLog("XS- ad end 3")
|
||
return false
|
||
}
|
||
}
|
||
NSLog("XS- ad end 4")
|
||
return true
|
||
}
|
||
func showAd(v:UIViewController) {
|
||
if(self.isshow == false) {
|
||
for (_, ad) in BbbAdManager.shared.adItems {
|
||
NSLog("XS- ad info:\(ad.interstitialAdID), ecpm:\(ad.ecpm * 1000)")
|
||
if (ad.status == 2 && ad.ecpm >= BbbAdManager.config.adbrush_ecpm) {
|
||
self.isshow = ad.showAd(viewController: v) { [weak self] in
|
||
NSLog("XS- ad close")
|
||
self!.isshow = false
|
||
}
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
func closeAd(v:Int) {
|
||
initializationTopOn.removeADVC(byDelayTime: v, onclose:{
|
||
for (_, ad) in BbbAdManager.shared.adItems {
|
||
if(ad.status == 3) {
|
||
ad.changeStatus(st: 4)
|
||
}
|
||
}
|
||
})
|
||
self.isshow = false
|
||
}
|
||
|
||
@objc func checkOpenADReadyState(){
|
||
BbbAdManager.totalTimeC += kOpenADPerSec
|
||
|
||
if (self.isEnd() && BbbAdManager.totalTimeC > 8) || BbbAdManager.totalTimeC >= kOpenAdCTimeLength {
|
||
openADTimer?.invalidate()
|
||
openADTimer = nil
|
||
DispatchQueue.global().async { [weak self] in
|
||
guard self != nil else { return }
|
||
YL_NetWorkManager.loadend(max_ecpm: 0)
|
||
|
||
}
|
||
}
|
||
|
||
if self.isshow == false{
|
||
self.showAd(v: self.view!)
|
||
}
|
||
|
||
}
|
||
}
|