Fableon/Fableon/Object/Libs/AdManager/Native/FANativeAdManager.swift
2026-02-28 09:35:05 +08:00

265 lines
8.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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