1.1.3提审

This commit is contained in:
zeng 2025-07-16 17:06:13 +08:00
parent a6219af9ed
commit de51890ff7
20 changed files with 1334 additions and 879 deletions

View File

@ -282,7 +282,11 @@
1BF513112E1FA138009750EA /* SPStatAdModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513102E1FA138009750EA /* SPStatAdModel.swift */; };
1BF513142E1FB8C1009750EA /* SPAppOpenAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513132E1FB8C1009750EA /* SPAppOpenAdManager.swift */; };
1BF513162E20ADB4009750EA /* SPAppOpenAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513152E20ADB4009750EA /* SPAppOpenAdViewController.swift */; };
1BF513192E20DC85009750EA /* SPBannerAd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513182E20DC85009750EA /* SPBannerAd.swift */; };
1BF513192E20DC85009750EA /* SPBannerAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513182E20DC85009750EA /* SPBannerAdManager.swift */; };
1BF5131B2E265880009750EA /* SPAdmobAppOpenAd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5131A2E265880009750EA /* SPAdmobAppOpenAd.swift */; };
1BF5131D2E265A80009750EA /* SPApplovinAppOpenAd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5131C2E265A80009750EA /* SPApplovinAppOpenAd.swift */; };
1BF513212E2662DC009750EA /* SPAdmobBannerAd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513202E2662DC009750EA /* SPAdmobBannerAd.swift */; };
1BF513232E273482009750EA /* SPApplovinBannerAd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513222E273479009750EA /* SPApplovinBannerAd.swift */; };
C3D1CE788CA03A1878493356 /* Pods_ThimraTV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64805795B479324EB764157 /* Pods_ThimraTV.framework */; };
/* End PBXBuildFile section */
@ -596,7 +600,11 @@
1BF513102E1FA138009750EA /* SPStatAdModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPStatAdModel.swift; sourceTree = "<group>"; };
1BF513132E1FB8C1009750EA /* SPAppOpenAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAppOpenAdManager.swift; sourceTree = "<group>"; };
1BF513152E20ADB4009750EA /* SPAppOpenAdViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAppOpenAdViewController.swift; sourceTree = "<group>"; };
1BF513182E20DC85009750EA /* SPBannerAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPBannerAd.swift; sourceTree = "<group>"; };
1BF513182E20DC85009750EA /* SPBannerAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPBannerAdManager.swift; sourceTree = "<group>"; };
1BF5131A2E265880009750EA /* SPAdmobAppOpenAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAdmobAppOpenAd.swift; sourceTree = "<group>"; };
1BF5131C2E265A80009750EA /* SPApplovinAppOpenAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPApplovinAppOpenAd.swift; sourceTree = "<group>"; };
1BF513202E2662DC009750EA /* SPAdmobBannerAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAdmobBannerAd.swift; sourceTree = "<group>"; };
1BF513222E273479009750EA /* SPApplovinBannerAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPApplovinBannerAd.swift; sourceTree = "<group>"; };
1DBC40592DA4EDFC0093FCB0 /* ThimraTV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThimraTV.app; sourceTree = BUILT_PRODUCTS_DIR; };
1F666DE0B12C863F26BE5027 /* Pods-MoviaBox.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoviaBox.debug.xcconfig"; path = "Target Support Files/Pods-MoviaBox/Pods-MoviaBox.debug.xcconfig"; sourceTree = "<group>"; };
A1174E10BCF2C606F7818792 /* Pods-ThimraTV.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThimraTV.release.xcconfig"; path = "Target Support Files/Pods-ThimraTV/Pods-ThimraTV.release.xcconfig"; sourceTree = "<group>"; };
@ -1511,6 +1519,8 @@
isa = PBXGroup;
children = (
1BF513132E1FB8C1009750EA /* SPAppOpenAdManager.swift */,
1BF5131A2E265880009750EA /* SPAdmobAppOpenAd.swift */,
1BF5131C2E265A80009750EA /* SPApplovinAppOpenAd.swift */,
);
path = AppOpenAd;
sourceTree = "<group>";
@ -1518,7 +1528,9 @@
1BF513172E20DC63009750EA /* BannerAd */ = {
isa = PBXGroup;
children = (
1BF513182E20DC85009750EA /* SPBannerAd.swift */,
1BF513182E20DC85009750EA /* SPBannerAdManager.swift */,
1BF513202E2662DC009750EA /* SPAdmobBannerAd.swift */,
1BF513222E273479009750EA /* SPApplovinBannerAd.swift */,
);
path = BannerAd;
sourceTree = "<group>";
@ -1743,6 +1755,7 @@
1BB91D132E04FD6A00A2C715 /* AppDelegate+OpenApp.swift in Sources */,
1BB91D142E04FD6A00A2C715 /* AppDelegate+Thirdparty.swift in Sources */,
1BB91D152E04FD6A00A2C715 /* SceneDelegate.swift in Sources */,
1BF5131D2E265A80009750EA /* SPApplovinAppOpenAd.swift in Sources */,
1BB91D162E04FD6A00A2C715 /* SPNavigationController.swift in Sources */,
1BB91D172E04FD6A00A2C715 /* SPTabBarController.swift in Sources */,
1BB91D182E04FD6A00A2C715 /* SPViewController.swift in Sources */,
@ -1776,6 +1789,7 @@
1BC1F0D72E0A35EF00B579A4 /* SPVideoRevolutionManager.swift in Sources */,
1BB91D342E04FD6A00A2C715 /* SPSettingAPI.swift in Sources */,
1BB91D352E04FD6A00A2C715 /* SPStatAPI.swift in Sources */,
1BF5131B2E265880009750EA /* SPAdmobAppOpenAd.swift in Sources */,
1BB91D362E04FD6A00A2C715 /* SPUserAPI.swift in Sources */,
1BB91D372E04FD6A00A2C715 /* SPVideoAPI.swift in Sources */,
1BB91D382E04FD6A00A2C715 /* SPWalletAPI.swift in Sources */,
@ -1817,6 +1831,7 @@
1BB91D5A2E04FD6A00A2C715 /* SPHomePageController.swift in Sources */,
1BB91D5B2E04FD6A00A2C715 /* SPHomeV2ViewController.swift in Sources */,
1BDE201E2E1E3D3E00C2C2B5 /* SPRewardedAdManager+Admob.swift in Sources */,
1BF513212E2662DC009750EA /* SPAdmobBannerAd.swift in Sources */,
1BB91D5C2E04FD6A00A2C715 /* SPHomeViewController.swift in Sources */,
1BB91D5D2E04FD6A00A2C715 /* SPSearchViewController.swift in Sources */,
1BB91D5E2E04FD6A00A2C715 /* SPHomeCategoryModel.swift in Sources */,
@ -1842,7 +1857,7 @@
1BF5130C2E1F4660009750EA /* SPRewardedAdManager+AppLovin.swift in Sources */,
1BB91D712E04FD6A00A2C715 /* SPHomeHotView.swift in Sources */,
1BB91D722E04FD6A00A2C715 /* SPHomeNineSquareContentCell.swift in Sources */,
1BF513192E20DC85009750EA /* SPBannerAd.swift in Sources */,
1BF513192E20DC85009750EA /* SPBannerAdManager.swift in Sources */,
1BB91D732E04FD6A00A2C715 /* SPHomePlayHistoricalView.swift in Sources */,
1BDE20172E1E164600C2C2B5 /* SPAdAPI.swift in Sources */,
1BB91D742E04FD6A00A2C715 /* SPHomePlayHistoryCell.swift in Sources */,
@ -1964,6 +1979,7 @@
1BB91DE42E04FD6A00A2C715 /* SPTokenModel.swift in Sources */,
1BB91DE52E04FD6A00A2C715 /* SPPlayer.swift in Sources */,
1BB91DE62E04FD6A00A2C715 /* SPIAPManager.swift in Sources */,
1BF513232E273482009750EA /* SPApplovinBannerAd.swift in Sources */,
1BB91DE72E04FD6A00A2C715 /* SPIAPOrderModel.swift in Sources */,
1BB91DE82E04FD6A00A2C715 /* SPIAPVerifyModel.swift in Sources */,
1BB91DE92E04FD6A00A2C715 /* SPWaitRestoreModel.swift in Sources */,
@ -2098,7 +2114,7 @@
CODE_SIGN_ENTITLEMENTS = ThimraTV/ThimraTV.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = TWDZ3MP9DV;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
@ -2146,7 +2162,7 @@
CODE_SIGN_ENTITLEMENTS = ThimraTV/ThimraTV.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = TWDZ3MP9DV;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;

View File

@ -12,8 +12,13 @@ import FirebaseCore
extension AppDelegate {
///
static var haveBeenShownAPNS = false
///
static var isRequestAuthorization = false
func registerAPNS() {
guard !Self.isRequestAuthorization else { return }
Self.isRequestAuthorization = true
FirebaseApp.configure()
Messaging.messaging().delegate = self

View File

@ -30,7 +30,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// SPLoginManager.manager.requestVisitorLogin(completer: nil)
SPLoginManager.manager.updateUserInfo(completer: nil)
///
registerAPNS()
// registerAPNS()
return true
}

View File

@ -122,16 +122,20 @@ extension SceneDelegate {
window?.rootViewController = guideVc
window?.makeKeyAndVisible()
SPAPPTool.appDelegate?.registerAPNS()
} else if SPLoginManager.manager.userInfo?.user_level == .ad, !SPAPPTool.isAppOpen, hasOpenApp == true, SPNetworkReachabilityManager.manager.isReachable == true { //广
let openAdVC = SPAppOpenAdViewController()
openAdVC.didEndBlock = { [weak self] in
self?.handleOpenApp()
SPAPPTool.appDelegate?.registerAPNS()
}
window?.rootViewController = openAdVC
window?.makeKeyAndVisible()
} else {
handleOpenApp()
SPAPPTool.appDelegate?.registerAPNS()
}
}

View File

@ -32,20 +32,28 @@ class SPAppOpenAdViewController: SPViewController {
private func openApp() {
self.didEndBlock?()
NotificationCenter.default.post(name: SPGuideViewController.didOpenAppNotification, object: nil, userInfo: nil)
}
}
extension SPAppOpenAdViewController: SPAppOpenAdManagerDelegate {
///广
func appOpenAdManager(manager: SPAppOpenAdManager, didLoadFail error: Error) {
self.didEndBlock?()
openApp()
}
///广
func appOpenAdManager(manager: SPAppOpenAdManager, didDisplayFail error: Error) {
self.didEndBlock?()
openApp()
}
///广
func appOpenAdManagerDidDismiss(manager: SPAppOpenAdManager) {
self.didEndBlock?()
openApp()
}
func appOpenAdManager(manager: SPAppOpenAdManager, didOtherFail error: any Error) {
openApp()
}
}

View File

@ -17,9 +17,18 @@ class SPVersionUpdateModel: SPModel, SmartCodable {
func canUpdate() -> Bool {
let currentCode = NSNumber(string: kSPAPPBundleVersion)?.intValue ?? 0
let serverCode = NSNumber(string: version_code ?? "0")?.intValue ?? 0
return serverCode > currentCode
// let currentCode = NSNumber(string: kSPAPPBundleVersion)?.intValue ?? 0
// let serverCode = NSNumber(string: version_code ?? "0")?.intValue ?? 0
// return serverCode > currentCode
guard let versionName = version_name else { return false }
let result = kSPAPPVersion.compare(versionName, options: .numeric)
if result == .orderedAscending {
return true
} else {
return false
}
}

View File

@ -28,8 +28,8 @@ class SPPlayerDetailRecommandView: HWPanModalContentView {
}
}
private lazy var bannerAd: SPBannerAd = {
let ad = SPBannerAd()
private lazy var bannerAd: SPBannerAdManager = {
let ad = SPBannerAdManager()
return ad
}()
@ -213,6 +213,7 @@ extension SPPlayerDetailRecommandView {
bannerAd.view.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalToSuperview().offset(-kSPTabbarSafeBottomMargin)
make.size.equalTo(bannerAd.size)
}
}

View File

@ -128,11 +128,12 @@ extension SPPlayerListViewModel {
completer?(false)
case .success:
videoInfo?.is_lock = false
completer?(true)
//
SPLoginManager.manager.updateUserInfo(completer: nil)
SPLoginManager.manager.updateUserInfo {
videoInfo?.is_lock = false
completer?(true)
}
default:
completer?(false)

View File

@ -0,0 +1,67 @@
//
// SPAdmobAppOpenAd.swift
// ThimraTV
//
// Created by on 2025/7/15.
//
import UIKit
import GoogleMobileAds
class SPAdmobAppOpenAd: NSObject, SPAppOpenAd {
private var appOpenAd: AppOpenAd?
var delegate: (any SPAppOpenAdDelegate)?
var adPlatformKey: String {
return SPAdPlatformKey.google.rawValue
}
var adUnitID: String {
return SPAdManager.manager.admob_appOpenAdUnitID
}
var isReady: Bool {
return appOpenAd != nil
}
func loadAd() {
AppOpenAd.load(with: adUnitID, request: Request()) { [weak self] appOpenAd, error in
guard let self = self else { return }
self.appOpenAd = appOpenAd
self.appOpenAd?.fullScreenContentDelegate = self
if let error = error {
self.delegate?.appOpenAd?(ad: self, didLoadFail: error)
} else {
self.delegate?.appOpenAdDidLoadFinish?(ad: self)
}
}
}
func showAd() {
appOpenAd?.present(from: nil)
}
}
extension SPAdmobAppOpenAd: FullScreenContentDelegate {
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
self.delegate?.appOpenAdDidShow?(ad: self)
}
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
self.delegate?.appOpenAdDidDismiss?(ad: self)
}
func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
self.delegate?.appOpenAd?(ad: self, didDisplayFail: error)
}
func adDidRecordClick(_ ad: any FullScreenPresentingAd) {
self.delegate?.appOpenAdDidClick?(ad: self)
}
}

View File

@ -6,7 +6,6 @@
//
import UIKit
import GoogleMobileAds
@objc protocol SPAppOpenAdManagerDelegate: NSObjectProtocol {
///广
@ -19,21 +18,60 @@ import GoogleMobileAds
@objc optional func appOpenAdManagerDidShow(manager: SPAppOpenAdManager)
///广
@objc optional func appOpenAdManagerDidDismiss(manager: SPAppOpenAdManager)
///
@objc optional func appOpenAdManager(manager: SPAppOpenAdManager, didOtherFail error: Error)
}
@objc protocol SPAppOpenAdDelegate: NSObjectProtocol {
///广
@objc optional func appOpenAd(ad: SPAppOpenAd, didLoadFail error: Error)
///广
@objc optional func appOpenAdDidLoadFinish(ad: SPAppOpenAd)
///广
@objc optional func appOpenAd(ad: SPAppOpenAd, didDisplayFail error: Error)
///广
@objc optional func appOpenAdDidShow(ad: SPAppOpenAd)
///广
@objc optional func appOpenAdDidDismiss(ad: SPAppOpenAd)
///广
@objc optional func appOpenAdDidClick(ad: SPAppOpenAd)
}
@objc protocol SPAppOpenAd: NSObjectProtocol {
weak var delegate: SPAppOpenAdDelegate? { get set }
var adPlatformKey: String { get }
var adUnitID: String { get }
var isReady: Bool { get }
func loadAd()
func showAd()
}
class SPAppOpenAdManager: NSObject {
static let manager = SPAppOpenAdManager()
weak var delegate: SPAppOpenAdManagerDelegate?
let adUnitID = SPAdManager.manager.appOpenAdUnitID
let adUnitID = SPAdManager.manager.admob_appOpenAdUnitID
private var appOpenAd: SPAppOpenAd?
private var appOpenAd: AppOpenAd?
private(set) var isLoadingAd = false
private(set) var isShowingAd = false
private var isNeedShow = false
static let manager = SPAppOpenAdManager()
private var timeOutTimer: Timer?
deinit {
NotificationCenter.default.removeObserver(self)
@ -51,26 +89,17 @@ class SPAppOpenAdManager: NSObject {
}
isLoadingAd = true
AppOpenAd.load(with: adUnitID, request: Request()) { [weak self] appOpenAd, error in
guard let self = self else { return }
self.isLoadingAd = false
self.appOpenAd = appOpenAd
self.appOpenAd?.fullScreenContentDelegate = self
if appOpenAd != nil, self.isNeedShow {
self.showAdIfAvailable()
}
if let error = error {
self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
self.delegate?.appOpenAdManager?(manager: self, didLoadFail: error)
} else {
self.delegate?.appOpenAdManagerDidLoadFinish?(manager: self)
}
appOpenAd = SPAdmobAppOpenAd()
appOpenAd?.delegate = self
appOpenAd?.loadAd()
}
func showAd() {
if let ad = appOpenAd {
self.isNeedShow = false
isShowingAd = true
ad.showAd()
}
}
func showAdIfAvailable() {
@ -79,6 +108,8 @@ class SPAppOpenAdManager: NSObject {
self.isNeedShow = true
self.timeOutTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(handleTimeOutTimer), userInfo: nil, repeats: false)
// If the app open ad is not available yet but is supposed to show, load
// a new ad.
if !isAdAvailable() {
@ -86,48 +117,80 @@ class SPAppOpenAdManager: NSObject {
return
}
if let ad = appOpenAd {
self.isNeedShow = false
isShowingAd = true
ad.present(from: nil)
}
showAd()
}
private func isAdAvailable() -> Bool {
// Check if ad exists and can be shown.
return appOpenAd != nil
}
return appOpenAd?.isReady ?? false
}
extension SPAppOpenAdManager: FullScreenContentDelegate {
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
self.requestStatAd(type: "start", errorMsg: nil)
print("App open ad will be presented.")
self.delegate?.appOpenAdManagerDidShow?(manager: self)
@objc private func handleTimeOutTimer() {
self.isNeedShow = false
self.timeOutTimer?.invalidate()
self.timeOutTimer = nil
clearTimer()
let error = NSError(domain: "time-out", code: -1)
self.delegate?.appOpenAdManager?(manager: self, didOtherFail: error)
}
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
self.requestStatAd(type: "close", errorMsg: nil)
appOpenAd = nil
isShowingAd = false
// Reload an ad.
// self.loadAd()
self.delegate?.appOpenAdManagerDidDismiss?(manager: self)
private func clearTimer() {
self.timeOutTimer?.invalidate()
self.timeOutTimer = nil
}
func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
}
//MARK: -------------- SPAppOpenAdDelegate --------------
extension SPAppOpenAdManager: SPAppOpenAdDelegate {
///广
func appOpenAd(ad: SPAppOpenAd, didLoadFail error: Error) {
self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
isLoadingAd = false
clearTimer()
self.delegate?.appOpenAdManager?(manager: self, didLoadFail: error)
}
///广
func appOpenAdDidLoadFinish(ad: SPAppOpenAd) {
isLoadingAd = false
clearTimer()
self.delegate?.appOpenAdManagerDidLoadFinish?(manager: self)
if isNeedShow {
self.showAd()
}
}
///广
func appOpenAd(ad: SPAppOpenAd, didDisplayFail error: Error) {
self.requestStatAd(type: "show_failed", errorMsg: error.localizedDescription)
appOpenAd = nil
isShowingAd = false
// Reload an ad.
// self.loadAd()
clearTimer()
self.delegate?.appOpenAdManager?(manager: self, didDisplayFail: error)
}
///广
func appOpenAdDidShow(ad: SPAppOpenAd) {
self.requestStatAd(type: "start", errorMsg: nil)
self.delegate?.appOpenAdManagerDidShow?(manager: self)
}
///广
func appOpenAdDidDismiss(ad: SPAppOpenAd) {
self.requestStatAd(type: "close", errorMsg: nil)
func adDidRecordClick(_ ad: any FullScreenPresentingAd) {
appOpenAd = nil
isShowingAd = false
self.delegate?.appOpenAdManagerDidDismiss?(manager: self)
}
///广
func appOpenAdDidClick(ad: SPAppOpenAd) {
self.requestStatAd(type: "click", errorMsg: nil)
}
@ -137,10 +200,12 @@ extension SPAppOpenAdManager: FullScreenContentDelegate {
extension SPAppOpenAdManager {
private func requestStatAd(type: String, errorMsg: String?) {
guard let appOpenAd = appOpenAd else { return }
let model = SPStatAdModel()
model.type = type
model.ads_id = adUnitID
model.ad_platform_key = .google
model.ads_id = appOpenAd.adUnitID
model.ad_platform_key = SPAdPlatformKey(rawValue: appOpenAd.adPlatformKey)
model.error_msg = errorMsg
model.scene = .splash
@ -149,7 +214,7 @@ extension SPAppOpenAdManager {
@objc private func didEnterBackgroundNotification() {
guard appOpenAd != nil else { return }
if !self.isShowingAd { return }
self.requestStatAd(type: "Interrupt", errorMsg: nil)
}

View File

@ -0,0 +1,79 @@
//
// SPApplovinAppOpenAd.swift
// ThimraTV
//
// Created by on 2025/7/15.
//
import UIKit
import AppLovinSDK
class SPApplovinAppOpenAd: NSObject, SPAppOpenAd {
private lazy var appOpenAd = MAAppOpenAd(adUnitIdentifier: adUnitID)
var delegate: (any SPAppOpenAdDelegate)?
var adPlatformKey: String {
return SPAdPlatformKey.applovin.rawValue
}
var adUnitID: String {
return SPAdManager.manager.applovin_appOpenAdUnitID
}
var isReady: Bool {
if ALSdk.shared().isInitialized && appOpenAd.isReady {
return true
} else {
return false
}
}
func loadAd() {
SPAdManager.manager.initialize_applovinSdk { [weak self] in
guard let self = self else { return }
appOpenAd.delegate = self
appOpenAd.load()
}
}
func showAd() {
guard isReady else { return }
appOpenAd.show()
}
}
//MARK: -------------- MAAdDelegate --------------
extension SPApplovinAppOpenAd: MAAdDelegate {
func didLoad(_ ad: MAAd) {
self.delegate?.appOpenAdDidLoadFinish?(ad: self)
}
func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
let nsError = NSError(domain: error.message, code: error.code.rawValue)
self.delegate?.appOpenAd?(ad: self, didLoadFail: nsError)
}
func didDisplay(_ ad: MAAd) {
self.delegate?.appOpenAdDidShow?(ad: self)
}
func didHide(_ ad: MAAd) {
self.delegate?.appOpenAdDidDismiss?(ad: self)
}
func didClick(_ ad: MAAd) {
self.delegate?.appOpenAdDidClick?(ad: self)
}
func didFail(toDisplay ad: MAAd, withError error: MAError) {
let nsError = NSError(domain: error.message, code: error.code.rawValue)
self.delegate?.appOpenAd?(ad: self, didDisplayFail: nsError)
}
}

View File

@ -0,0 +1,75 @@
//
// SPAdmobBannerAd.swift
// ThimraTV
//
// Created by on 2025/7/15.
//
import UIKit
import GoogleMobileAds
class SPAdmobBannerAd: NSObject, SPBannerAd {
var delegate: (any SPBannerAdDelegate)?
let size = CGSize.init(width: kSPScreenWidth, height: 59)
private lazy var _adView: BannerView = {
let view = BannerView()
view.adUnitID = self.adUnitID
view.adSize = inlineAdaptiveBanner(width: size.width, maxHeight:size.height)
view.delegate = self
return view
}()
var adView: UIView {
return _adView
}
var adPlatformKey: String {
return SPAdPlatformKey.google.rawValue
}
var adUnitID: String {
return SPAdManager.manager.admob_bannerAdUnitID
}
func loadAd() {
_adView.load(Request())
}
}
//MARK: -------------- BannerViewDelegate --------------
extension SPAdmobBannerAd: BannerViewDelegate {
func bannerViewDidReceiveAd(_ bannerView: BannerView) {
self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
}
func bannerView(_ bannerView: BannerView, didFailToReceiveAdWithError error: Error) {
self.delegate?.bannerAd?(bannerAd: self, didLoadFail: error)
}
func bannerViewDidRecordClick(_ bannerView: BannerView) {
self.delegate?.bannerAdDidClick?(ad: self)
}
func bannerViewDidRecordImpression(_ bannerView: BannerView) {
}
func bannerViewWillPresentScreen(_ bannerView: BannerView) {
self.delegate?.bannerAdDidShow?(bannerAd: self)
}
func bannerViewWillDismissScreen(_ bannerView: BannerView) {
}
func bannerViewDidDismissScreen(_ bannerView: BannerView) {
self.delegate?.bannerAdDidDismiss?(bannerAd: self)
}
}

View File

@ -0,0 +1,82 @@
//
// SPApplovinBannerAd.swift
// ThimraTV
//
// Created by on 2025/7/16.
//
import UIKit
import AppLovinSDK
class SPApplovinBannerAd: NSObject, SPBannerAd {
let size = CGSize.init(width: kSPScreenWidth, height: 59)
var delegate: (any SPBannerAdDelegate)?
private(set) lazy var _adView: MAAdView = {
let view = MAAdView(adUnitIdentifier: adUnitID)
view.frame = .init(x: 0, y: 0, width: size.width, height: size.height)
view.delegate = self
return view
}()
var adView: UIView {
return _adView
}
var adPlatformKey: String {
return SPAdPlatformKey.applovin.rawValue
}
var adUnitID: String {
return SPAdManager.manager.applovin_bannerAdUnitID
}
func loadAd() {
_adView.loadAd()
}
}
//MARK: -------------- MAAdViewAdDelegate --------------
extension SPApplovinBannerAd: MAAdViewAdDelegate {
func didExpand(_ ad: MAAd) {
}
func didCollapse(_ ad: MAAd) {
}
func didLoad(_ ad: MAAd) {
self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
}
func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
let nsError = NSError(domain: error.message, code: error.code.rawValue)
self.delegate?.bannerAd?(bannerAd: self, didLoadFail: nsError)
}
func didDisplay(_ ad: MAAd) {
self.delegate?.bannerAdDidShow?(bannerAd: self)
}
func didHide(_ ad: MAAd) {
self.delegate?.bannerAdDidDismiss?(bannerAd: self)
}
func didClick(_ ad: MAAd) {
self.delegate?.bannerAdDidClick?(ad: self)
}
func didFail(toDisplay ad: MAAd, withError error: MAError) {
}
}

View File

@ -1,111 +0,0 @@
//
// SPBannerAd.swift
// ThimraTV
//
// Created by on 2025/7/11.
//
import UIKit
import GoogleMobileAds
@objc protocol SPBannerAdDelegate: NSObjectProtocol {
///广
@objc optional func bannerAd(bannerAd: SPBannerAd, didLoadFail error: Error)
///广
@objc optional func bannerAdDidLoadFinish(bannerAd: SPBannerAd)
///广
@objc optional func bannerAdDidShow(bannerAd: SPBannerAd)
///广
@objc optional func bannerAdDidDismiss(bannerAd: SPBannerAd)
}
class SPBannerAd: NSObject {
let adUnitID = SPAdManager.manager.bannerAdUnitID
let size = CGSize.init(width: kSPScreenWidth, height: 59)
weak var delegate: SPBannerAdDelegate?
private(set) lazy var view: BannerView = {
let view = BannerView()
view.adUnitID = self.adUnitID
view.adSize = inlineAdaptiveBanner(width: size.width, maxHeight:size.height)
// view.adSize = adSizeFor(cgSize: size)
view.delegate = self
return view
}()
deinit {
NotificationCenter.default.removeObserver(self)
}
override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil)
view.load(Request())
}
}
//MARK: -------------- BannerViewDelegate --------------
extension SPBannerAd: BannerViewDelegate {
func bannerViewDidReceiveAd(_ bannerView: BannerView) {
self.requestStatAd(type: "start", errorMsg: nil)
self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
}
func bannerView(_ bannerView: BannerView, didFailToReceiveAdWithError error: Error) {
self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
self.delegate?.bannerAd?(bannerAd: self, didLoadFail: error)
}
func bannerViewDidRecordClick(_ bannerView: BannerView) {
print(#function)
self.requestStatAd(type: "click", errorMsg: nil)
}
func bannerViewDidRecordImpression(_ bannerView: BannerView) {
print(#function)
}
func bannerViewWillPresentScreen(_ bannerView: BannerView) {
self.delegate?.bannerAdDidShow?(bannerAd: self)
}
func bannerViewWillDismissScreen(_ bannerView: BannerView) {
print(#function)
}
func bannerViewDidDismissScreen(_ bannerView: BannerView) {
self.requestStatAd(type: "close", errorMsg: nil)
self.delegate?.bannerAdDidDismiss?(bannerAd: self)
}
}
//MARK: -------------- --------------
extension SPBannerAd {
func requestStatAd(type: String, errorMsg: String?) {
guard self.view.superview != nil else { return }
let model = SPStatAdModel()
model.type = type
model.ads_id = adUnitID
model.ad_platform_key = .google
model.error_msg = errorMsg
model.scene = .banner
SPStatAPI.requestStatAd(model: model)
}
@objc private func didEnterBackgroundNotification() {
self.requestStatAd(type: "Interrupt", errorMsg: nil)
}
}

View File

@ -0,0 +1,135 @@
//
// SPBannerAdManager.swift
// ThimraTV
//
// Created by on 2025/7/11.
//
import UIKit
import GoogleMobileAds
@objc protocol SPBannerAdManagerDelegate: NSObjectProtocol {
///广
@objc optional func bannerAdManager(adManager: SPBannerAdManager, didLoadFail error: Error)
///广
@objc optional func bannerAdManagerDidLoadFinish(adManager: SPBannerAdManager)
///广
@objc optional func bannerAdManagerDidShow(adManager: SPBannerAdManager)
///广
@objc optional func bannerAdManagerDidDismiss(adManager: SPBannerAdManager)
}
@objc protocol SPBannerAdDelegate: NSObjectProtocol {
///广
@objc optional func bannerAd(bannerAd: SPBannerAd, didLoadFail error: Error)
///广
@objc optional func bannerAdDidLoadFinish(bannerAd: SPBannerAd)
///广
@objc optional func bannerAdDidShow(bannerAd: SPBannerAd)
///广
@objc optional func bannerAdDidDismiss(bannerAd: SPBannerAd)
///广
@objc optional func bannerAdDidClick(ad: SPBannerAd)
}
@objc protocol SPBannerAd: NSObjectProtocol {
weak var delegate: SPBannerAdDelegate? { get set }
var adView: UIView { get }
var adPlatformKey: String { get }
var adUnitID: String { get }
func loadAd()
}
class SPBannerAdManager: NSObject {
let adUnitID = SPAdManager.manager.admob_bannerAdUnitID
let size = CGSize.init(width: kSPScreenWidth, height: 59)
weak var delegate: SPBannerAdManagerDelegate?
private lazy var bannerAd: SPBannerAd = {
let ad = SPAdmobBannerAd()
ad.delegate = self
return ad
}()
var view: UIView {
return bannerAd.adView
}
deinit {
NotificationCenter.default.removeObserver(self)
}
override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil)
bannerAd.loadAd()
}
}
//MARK: -------------- SPBannerAdDelegate --------------
extension SPBannerAdManager: SPBannerAdDelegate {
///广
func bannerAd(bannerAd: SPBannerAd, didLoadFail error: Error) {
self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
self.delegate?.bannerAdManager?(adManager: self, didLoadFail: error)
}
///广
func bannerAdDidLoadFinish(bannerAd: SPBannerAd) {
self.requestStatAd(type: "start", errorMsg: nil)
self.delegate?.bannerAdManagerDidLoadFinish?(adManager: self)
}
///广
func bannerAdDidShow(bannerAd: SPBannerAd) {
self.delegate?.bannerAdManagerDidShow?(adManager: self)
}
///广
func bannerAdDidDismiss(bannerAd: SPBannerAd) {
self.requestStatAd(type: "close", errorMsg: nil)
self.delegate?.bannerAdManagerDidDismiss?(adManager: self)
}
func bannerAdDidClick(ad: any SPBannerAd) {
self.requestStatAd(type: "click", errorMsg: nil)
}
}
//MARK: -------------- --------------
extension SPBannerAdManager {
func requestStatAd(type: String, errorMsg: String?) {
guard self.view.superview != nil else { return }
let model = SPStatAdModel()
model.type = type
model.ads_id = adUnitID
model.ad_platform_key = SPAdPlatformKey(rawValue: bannerAd.adPlatformKey)
model.error_msg = errorMsg
model.scene = .banner
SPStatAPI.requestStatAd(model: model)
}
@objc private func didEnterBackgroundNotification() {
self.requestStatAd(type: "Interrupt", errorMsg: nil)
}
}

View File

@ -44,6 +44,9 @@ extension SPRewardedAdManager {
}
}
func admob_isReady() -> Bool {
return admob_rewardedAd != nil ? true : false
}
///广
func admob_loadAndShowRewardedAd(adInfo: SPAdInfo) {
@ -75,7 +78,6 @@ extension SPRewardedAdManager {
let request = Request()
self.admob_isLoadingRewardedAd = true
spLog(message: "====Ad 加载广告")
RewardedAd.load(with: adUnitID, request: request) { [weak self] rewardedAd, error in
guard let self = self else { return }
self.admob_isLoadingRewardedAd = false
@ -83,7 +85,6 @@ extension SPRewardedAdManager {
if let error = error {
self.admob_needShowRewardedAd = false
self.loadFailHandler(error: error)
spLog(message: "====Ad 广告加载失败")
return
}
self.loadFinishHandler()
@ -94,7 +95,6 @@ extension SPRewardedAdManager {
if self.admob_needShowRewardedAd {
self.admob_show()
}
spLog(message: "====Ad 广告加载成功")
}
}
@ -102,14 +102,12 @@ extension SPRewardedAdManager {
///广
private func admob_show() {
guard let rewardedAd = admob_rewardedAd, self.admob_needShowRewardedAd else {
return print("====Ad wasn't ready.")
return
}
self.admob_needShowRewardedAd = false
// The UIViewController parameter is an optional.
rewardedAd.present(from: nil) { [weak self] in
let reward = rewardedAd.adReward
print("Reward received with currency \(reward.amount), amount \(reward.amount.doubleValue)")
self?.userDidEarnRewardHandler()
// TODO: Reward the user.
}
@ -127,13 +125,11 @@ extension SPRewardedAdManager: FullScreenContentDelegate {
/// Tells the delegate that the ad will present full screen content.
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
print("====Ad will present full screen content.")
self.didShowHandler()
}
/// Tells the delegate that the ad dismissed full screen content.
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
print("====Ad did dismiss full screen content.")
self.admob_rewardedAd = nil
self.didDismissHandler()

View File

@ -61,13 +61,13 @@ extension SPRewardedAdManager {
func appLovin_loadRewardedAd(adInfo: SPAdInfo) {
#if canImport(AppLovinSDK)
if !appLovin_isLoadingRewardedAd {
if self.appLovin_isLoadingRewardedAd { return }
self.appLovin_isLoadingRewardedAd = true
appLovin_rewardedAd = MARewardedAd.shared(withAdUnitIdentifier: adInfo.ads_id ?? "")
appLovin_rewardedAd?.delegate = self
appLovin_rewardedAd?.load()
}
self.appLovin_rewardedAd = MARewardedAd.shared(withAdUnitIdentifier: adInfo.ads_id ?? "")
self.appLovin_rewardedAd?.delegate = self
self.appLovin_rewardedAd?.load()
#endif
}

View File

@ -62,7 +62,7 @@ class SPRewardedAdManager: NSObject {
func isAdAvailable() -> Bool {
guard let adInfo = adInfo else { return false }
if adInfo.platform_key == .google {
return admob_rewardedAd != nil ? true : false
return admob_isReady()
} else if adInfo.platform_key == .applovin {
return appLovin_isReady()
}
@ -90,60 +90,43 @@ class SPRewardedAdManager: NSObject {
SPHUD.show()
}
if let adInfo = adInfo {//广
if adInfo.platform_key == .google {
self.admob_loadAndShowRewardedAd(adInfo: adInfo)
} else if adInfo.platform_key == .applovin {
self.appLovin_loadAndShowRewardedAd(adInfo: adInfo)
}
} else {
guard !isLoadingRewardedAd else { return }
if !isAdAvailable() {
self.isLoadingRewardedAd = true
SPAdAPI.requestShowAdInfo { [weak self] adInfo in
guard let self = self else { return }
guard let adInfo = adInfo else {
let text = "movia_no_ads_tip".localized
let error = NSError(domain: text.localized, code: -1)
loadFailHandler(isStat: false, error: error)
return
}
self.adInfo = adInfo
self.requestAdInfo { [weak self] adInfo in
guard let self = self else { return }
if adInfo.platform_key == .google {
self.admob_loadAndShowRewardedAd(adInfo: adInfo)
} else if adInfo.platform_key == .applovin {
self.appLovin_loadAndShowRewardedAd(adInfo: adInfo)
} else {
self.isLoadingRewardedAd = false
self.adInfo = nil
}
}
}
}
///广
func preloadRewardedAd() {
guard isEnable else { return }
guard !isLoadingRewardedAd else { return }
guard self.adInfo == nil else { return }
isShowLoading = false
isShowToast = false
self.isLoadingRewardedAd = true
SPAdAPI.requestShowAdInfo { [weak self] adInfo in
self.requestAdInfo { [weak self] adInfo in
guard let self = self else { return }
guard let adInfo = adInfo else {
self.isLoadingRewardedAd = false
return
}
self.adInfo = adInfo
if adInfo.platform_key == .google {
self.admob_loadRewardedAd(adInfo: adInfo)
} else if adInfo.platform_key == .applovin {
self.appLovin_loadRewardedAd(adInfo: adInfo)
} else {
self.isLoadingRewardedAd = false
self.adInfo = nil
}
}
@ -169,7 +152,6 @@ class SPRewardedAdManager: NSObject {
}
private func clean() {
self.adInfo = nil
self.statScene = nil
self.videoInfo = nil
}
@ -256,6 +238,7 @@ extension SPRewardedAdManager {
}
///广
func didDismissHandler() {
let adInfo = self.adInfo
var seconds = 0
if let adsDate = self.adsDate {
@ -264,13 +247,13 @@ extension SPRewardedAdManager {
self.requestStatAd(type: "close", seconds: seconds, errorMsg: nil) { [weak self] in
guard let self = self else { return }
if let adInfo = self.adInfo {
if let adInfo = adInfo {
self.delegate?.rewardedAdManager?(manager: self, didDismiss: adInfo)
}
}
self.clean()
self.preloadRewardedAd()
}
}
@ -314,4 +297,25 @@ extension SPRewardedAdManager {
func showToast(text: String? = "movia_no_ads_tip".localized) {
SPToast.show(text: text)
}
private func requestAdInfo(completer: ((_ adInfo: SPAdInfo) -> Void)?) {
if let adInfo = self.adInfo {
completer?(adInfo)
return
}
SPAdAPI.requestShowAdInfo { [weak self] adInfo in
guard let self = self else { return }
if let adInfo = adInfo {
self.adInfo = adInfo
} else {
self.adInfo = SPAdInfo()
self.adInfo?.platform_key = .google
self.adInfo?.ads_id = SPAdManager.manager.admob_rewardedAdUnitID
}
completer?(self.adInfo!)
}
}
}

View File

@ -17,30 +17,42 @@ class SPAdManager: NSObject {
static let manager = SPAdManager()
/// AppLovinSDK
private var appLovininitializeCompleter: (() -> Void)?
func start() {
//admob
MobileAds.shared.start()
initialize_applovinSdk(completer: nil)
}
///AppLovinSDK
func initialize_applovinSdk(completer: (() -> Void)? = nil) {
if completer != nil {
self.appLovininitializeCompleter = completer
}
#if canImport(AppLovinSDK)
if !ALSdk.shared().isInitialized {
//
let initConfig = ALSdkInitializationConfiguration(sdkKey: "XW2aulJv9urKD4MIIFT1xcSCuyTHaDZ9qUbDqygnTLS04GkdX7WMQJviGP5vDRWGsk4OJJIyLGRV3mbLqOWx0W") { builder in
builder.mediationProvider = ALMediationProviderMAX
#if DEBUG
builder.testDeviceAdvertisingIdentifiers = [JXUUID.idfa()]
#endif
//#if DEBUG
// builder.testDeviceAdvertisingIdentifiers = [JXUUID.idfa()]
//#endif
}
// Initialize the SDK with the configuration
ALSdk.shared().initialize(with: initConfig) { sdkConfig in
// Start loading ads
self.appLovininitializeCompleter?()
}
} else {
self.appLovininitializeCompleter?()
}
#endif
}
}
//MARK: -------------- ID --------------
@ -48,31 +60,38 @@ extension SPAdManager {
///广ID
var admob_rewardedAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/1712485313"
#else
return ""
#endif
//#if DEBUG
// return "ca-app-pub-3940256099942544/1712485313"
//#else
//#endif
return "ca-app-pub-5209443898911659/3535954087"
}
///广ID
var appOpenAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/5575463023"
#else
return ""
#endif
var admob_appOpenAdUnitID: String {
//#if DEBUG
// return "ca-app-pub-3940256099942544/5575463023"
//#else
//#endif
return "ca-app-pub-5209443898911659/3886651330"
}
///广ID
var bannerAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/2435281174"
#else
var admob_bannerAdUnitID: String {
//#if DEBUG
// return "ca-app-pub-3940256099942544/2435281174"
//#else
//#endif
return "ca-app-pub-5209443898911659/8858726666"
}
///广ID
var applovin_appOpenAdUnitID: String {
return ""
}
///广ID
var applovin_bannerAdUnitID: String {
return ""
#endif
}
}

View File

@ -30,7 +30,7 @@
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
<string>ca-app-pub-5209443898911659~1849745003</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fbapi</string>