265 lines
8.3 KiB
Swift
265 lines
8.3 KiB
Swift
//
|
||
// FANativeAdManager.swift
|
||
// Fableon
|
||
//
|
||
// Created by 湖北秦九 on 2026/1/29.
|
||
//
|
||
|
||
import UIKit
|
||
|
||
@objc protocol FANativeAdManagerDelegate: NSObjectProtocol {
|
||
///广告加载失败
|
||
@objc optional func fa_nativeAdManager(manager: FANativeAdManager, didLoadFail error: Error)
|
||
///广告加载成功
|
||
@objc optional func fa_nativeAdManagerDidLoadFinish(manager: FANativeAdManager)
|
||
///广告被展示
|
||
@objc optional func fa_nativeAdManagerDidShow(manager: FANativeAdManager)
|
||
/// 广告展示失败
|
||
@objc optional func fa_nativeAdManager(manager: FANativeAdManager, didDisplayFail error: Error)
|
||
///广告被关闭
|
||
@objc optional func fa_nativeAdManagerDidDismiss(manager: FANativeAdManager)
|
||
///广告被点击
|
||
@objc optional func fa_nativeAdManagerDidClick(manager: FANativeAdManager)
|
||
}
|
||
|
||
|
||
protocol FANativeAdDelegate: NSObjectProtocol {
|
||
func fa_nativeAdDidStartLoad(ad: FANativeAd)
|
||
///广告加载失败
|
||
func fa_nativeAd(ad: FANativeAd, didLoadFail error: Error)
|
||
///广告加载成功
|
||
func fa_nativeAdDidLoadFinish(ad: FANativeAd)
|
||
///广告被展示
|
||
func fa_nativeAdDidShow(ad: FANativeAd, adInfo: [AnyHashable : Any])
|
||
///广告展示失败
|
||
func fa_nativeAd(ad: FANativeAd, didDisplayFail error: Error)
|
||
///广告被关闭
|
||
func fa_nativeAdDidDismiss(ad: FANativeAd)
|
||
///广告被点击
|
||
func fa_nativeAdDidClick(ad: FANativeAd)
|
||
}
|
||
|
||
protocol FANativeAd: NSObjectProtocol {
|
||
|
||
var delegate: FANativeAdDelegate? { get set }
|
||
|
||
var adView: UIView? { get set }
|
||
|
||
var adSize: CGSize? { get set }
|
||
|
||
var adPlatform: FAAdPlatform { get }
|
||
|
||
var adType: FAAdType { get }
|
||
|
||
var adUnitID: String { get }
|
||
|
||
var networkName: String { get set }
|
||
|
||
var isReady: Bool { get }
|
||
|
||
func loadAd()
|
||
func show()
|
||
|
||
|
||
}
|
||
|
||
class FANativeAdManager: NSObject {
|
||
|
||
weak var delegate: FANativeAdManagerDelegate?
|
||
|
||
///外部传入,用来承载广告的视图
|
||
var contentView: UIView? {
|
||
didSet {
|
||
self.nativeAd?.adView = contentView
|
||
}
|
||
}
|
||
|
||
let contentSize: CGSize = .init(width: 300, height: 400)
|
||
|
||
var isAdAvailable: Bool {
|
||
if FALogin.manager.userInfo?.user_level == .ad {
|
||
return nativeAd?.isReady ?? false
|
||
} else {
|
||
return false
|
||
}
|
||
}
|
||
|
||
var shortPlayId: String?
|
||
var shortPlayVideoId: String?
|
||
|
||
///广告是否在加载中
|
||
private(set) var isLoadingAd = false
|
||
///广告正在被展示
|
||
private(set) var isShowingAd = false
|
||
|
||
///是否展示过
|
||
var hasShow = false
|
||
|
||
private var startShowDate: Date?
|
||
private var startLoadDate: Date?
|
||
private var endLoadDate: Date?
|
||
|
||
|
||
private var nativeAd: FANativeAd? {
|
||
didSet {
|
||
oldValue?.delegate = nil
|
||
nativeAd?.delegate = self
|
||
}
|
||
}
|
||
|
||
deinit {
|
||
// NotificationCenter.default.removeObserver(self)
|
||
}
|
||
|
||
override init() {
|
||
super.init()
|
||
// NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
||
}
|
||
|
||
|
||
func load() {
|
||
guard FALogin.manager.userInfo?.user_level == .ad else {
|
||
let error = NSError(domain: "非广告用户", code: -1)
|
||
self.delegate?.fa_nativeAdManager?(manager: self, didLoadFail: error)
|
||
return
|
||
}
|
||
guard !self.isLoadingAd else { return }
|
||
|
||
self.startLoadDate = nil
|
||
self.endLoadDate = nil
|
||
self.startShowDate = nil
|
||
self.isLoadingAd = true
|
||
|
||
self.nativeAd = FATradPlusNativeAd()
|
||
self.nativeAd?.adSize = self.contentSize
|
||
self.nativeAd?.adView = contentView
|
||
self.nativeAd?.delegate = self
|
||
self.nativeAd?.loadAd()
|
||
}
|
||
|
||
func show() {
|
||
guard FALogin.manager.userInfo?.user_level == .ad else {
|
||
let error = NSError(domain: "非广告用户", code: -1)
|
||
self.delegate?.fa_nativeAdManager?(manager: self, didDisplayFail: error)
|
||
return
|
||
}
|
||
guard !hasShow, self.isAdAvailable else { return }
|
||
|
||
self.isShowingAd = true
|
||
self.hasShow = true
|
||
self.nativeAd?.show()
|
||
}
|
||
|
||
///上报关闭广告历史
|
||
func uploadCloseAdsHistory() {
|
||
guard let ad = self.nativeAd else { return }
|
||
guard self.isShowingAd else { return }
|
||
self.uploadAdsHistory(ad: ad, type: .close)
|
||
}
|
||
|
||
}
|
||
|
||
extension FANativeAdManager: FANativeAdDelegate {
|
||
|
||
func fa_nativeAdDidStartLoad(ad: any FANativeAd) {
|
||
self.startLoadDate = Date()
|
||
self.uploadAdsHistory(ad: ad, type: .startLoad)
|
||
}
|
||
|
||
func fa_nativeAdDidLoadFinish(ad: any FANativeAd) {
|
||
self.isLoadingAd = false
|
||
self.endLoadDate = Date()
|
||
self.uploadAdsHistory(ad: ad, type: .endLoad)
|
||
self.delegate?.fa_nativeAdManagerDidLoadFinish?(manager: self)
|
||
}
|
||
|
||
func fa_nativeAdDidShow(ad: any FANativeAd, adInfo: [AnyHashable : Any]) {
|
||
self.startShowDate = Date()
|
||
self.uploadAdsHistory(ad: ad, type: .show, viewSeconds: 0)
|
||
self.requestUploadAdsPrice(ad: ad, adInfo: adInfo)
|
||
self.delegate?.fa_nativeAdManagerDidShow?(manager: self)
|
||
}
|
||
|
||
func fa_nativeAd(ad: any FANativeAd, didLoadFail error: any Error) {
|
||
self.isLoadingAd = false
|
||
self.endLoadDate = Date()
|
||
uploadAdsHistory(ad: ad, type: .loadFailed, errorMsg: error.localizedDescription)
|
||
self.delegate?.fa_nativeAdManager?(manager: self, didLoadFail: error)
|
||
}
|
||
|
||
func fa_nativeAd(ad: any FANativeAd, didDisplayFail error: any Error) {
|
||
uploadAdsHistory(ad: ad, type: .showFailed, errorMsg: error.localizedDescription)
|
||
self.delegate?.fa_nativeAdManager?(manager: self, didDisplayFail: error)
|
||
}
|
||
|
||
func fa_nativeAdDidDismiss(ad: any FANativeAd) {
|
||
self.uploadCloseAdsHistory()
|
||
self.delegate?.fa_nativeAdManagerDidDismiss?(manager: self)
|
||
}
|
||
|
||
func fa_nativeAdDidClick(ad: any FANativeAd) {
|
||
self.uploadAdsHistory(ad: ad, type: .click)
|
||
self.delegate?.fa_nativeAdManagerDidClick?(manager: self)
|
||
}
|
||
|
||
|
||
}
|
||
|
||
extension FANativeAdManager {
|
||
|
||
@objc private func didEnterBackgroundNotification() {
|
||
if !self.isShowingAd { return }
|
||
guard let ad = self.nativeAd else { return }
|
||
|
||
self.uploadAdsHistory(ad: ad, type: .interrupt)
|
||
}
|
||
|
||
func uploadAdsHistory(ad: any FANativeAd, type: FAAdManager.StatType, viewSeconds: Int? = nil, errorMsg: String? = nil) {
|
||
var loadTime: Int?
|
||
if let startDate = self.startLoadDate, let endDate = self.endLoadDate {
|
||
loadTime = Int(endDate.timeIntervalSince(startDate))
|
||
}
|
||
|
||
var showTime: Int? = viewSeconds
|
||
if showTime == nil && (type == .click || type == .interrupt || type == .close) {
|
||
if let startShowDate = self.startShowDate {
|
||
showTime = Int(Date().timeIntervalSince(startShowDate))
|
||
}
|
||
}
|
||
|
||
let model = FAAdStatModel()
|
||
model.scene = .native
|
||
if let loadTime = loadTime {
|
||
model.loading_time = max(0, loadTime)
|
||
}
|
||
model.short_play_id = self.shortPlayId
|
||
model.short_play_video_id = self.shortPlayVideoId
|
||
model.view_seconds = showTime
|
||
model.ad_type = ad.adType.rawValue
|
||
model.ads_id = ad.adUnitID
|
||
model.ad_platform_key = ad.adPlatform
|
||
model.type = type
|
||
model.ag_platform_name = ad.networkName
|
||
model.ad_platform_name = ad.adPlatform
|
||
|
||
FAStatAPI.requestUploadAdsHistory(model: model)
|
||
}
|
||
|
||
func requestUploadAdsPrice(ad: any FANativeAd, adInfo: [AnyHashable : Any]) {
|
||
guard let ecpm = adInfo["ecpm"] as? String else { return }
|
||
let money = (NSNumber(string: ecpm)?.doubleValue ?? 0) / 1000
|
||
|
||
let model = FAUploadAdsPriceModel()
|
||
model.short_play_id = self.shortPlayId
|
||
model.short_play_video_id = self.shortPlayVideoId
|
||
model.short_play_launch_id = ad.adUnitID
|
||
model.type = ad.adType.rawValue
|
||
model.agent_name = ad.adPlatform.rawValue
|
||
model.alliance_name = ad.networkName
|
||
model.money = "\(money)"
|
||
model.scene = .native
|
||
|
||
FAStatAPI.requestUploadAdsPrice(model: model)
|
||
}
|
||
}
|