From 7736f5ed0263327b234ced3e116795bcd24ddffd Mon Sep 17 00:00:00 2001 From: zeng Date: Fri, 11 Jul 2025 11:27:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=AF=E5=B1=8F=E5=B9=BF=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ThimraTV.xcodeproj/project.pbxproj | 4 + ThimraTV/AppDelegate/AppDelegate.swift | 2 + ThimraTV/AppDelegate/SceneDelegate.swift | 19 +++- .../Base/Controller/SPTabBarController.swift | 2 + .../SPWebViewController+ScriptMessage.swift | 3 + .../SPAppOpenAdViewController.swift | 51 +++++++++ .../Controller/SPSettingsViewController.swift | 3 +- ThimraTV/Libs/APPTool/SPAPPTool.swift | 2 +- .../AppOpenAd/SPAppOpenAdManager.swift | 106 ++++++++++++++---- .../SPRewardedAdManager+Admob.swift | 6 + .../RewardedAd/SPRewardedAdManager.swift | 26 +++-- ThimraTV/Libs/AdManager/SPAdManager.swift | 4 +- ThimraTV/Libs/AdManager/SPStatAdModel.swift | 1 + 13 files changed, 192 insertions(+), 37 deletions(-) create mode 100644 ThimraTV/Class/Guide/Controller/SPAppOpenAdViewController.swift diff --git a/ThimraTV.xcodeproj/project.pbxproj b/ThimraTV.xcodeproj/project.pbxproj index e71e64a..05184a8 100644 --- a/ThimraTV.xcodeproj/project.pbxproj +++ b/ThimraTV.xcodeproj/project.pbxproj @@ -281,6 +281,7 @@ 1BF5130E2E1F5D9B009750EA /* SPRewardedAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */; }; 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 */; }; C3D1CE788CA03A1878493356 /* Pods_ThimraTV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64805795B479324EB764157 /* Pods_ThimraTV.framework */; }; /* End PBXBuildFile section */ @@ -593,6 +594,7 @@ 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPRewardedAdManager.swift; sourceTree = ""; }; 1BF513102E1FA138009750EA /* SPStatAdModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPStatAdModel.swift; sourceTree = ""; }; 1BF513132E1FB8C1009750EA /* SPAppOpenAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAppOpenAdManager.swift; sourceTree = ""; }; + 1BF513152E20ADB4009750EA /* SPAppOpenAdViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAppOpenAdViewController.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -833,6 +835,7 @@ isa = PBXGroup; children = ( 1BB91C142E04FD6A00A2C715 /* SPGuideViewController.swift */, + 1BF513152E20ADB4009750EA /* SPAppOpenAdViewController.swift */, ); path = Controller; sourceTree = ""; @@ -1984,6 +1987,7 @@ 1BB91E062E04FD6A00A2C715 /* UIViewController+WMPageController.m in Sources */, 1BB91E072E04FD6A00A2C715 /* WMPageController.m in Sources */, 1BB91E082E04FD6A00A2C715 /* ZKCycleScrollView.swift in Sources */, + 1BF513162E20ADB4009750EA /* SPAppOpenAdViewController.swift in Sources */, 1BB91E092E04FD6A00A2C715 /* ZKCycleScrollViewFlowLayout.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ThimraTV/AppDelegate/AppDelegate.swift b/ThimraTV/AppDelegate/AppDelegate.swift index 4c2941a..8374de3 100644 --- a/ThimraTV/AppDelegate/AppDelegate.swift +++ b/ThimraTV/AppDelegate/AppDelegate.swift @@ -54,6 +54,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if SPNetworkReachabilityManager.manager.isReachable == true { // SPLoginManager.manager.requestVisitorLogin(completer: nil) SPLoginManager.manager.updateUserInfo(completer: nil) + + SPRewardedAdManager.manager.preloadRewardedAd() } } diff --git a/ThimraTV/AppDelegate/SceneDelegate.swift b/ThimraTV/AppDelegate/SceneDelegate.swift index acefb9f..601c861 100644 --- a/ThimraTV/AppDelegate/SceneDelegate.swift +++ b/ThimraTV/AppDelegate/SceneDelegate.swift @@ -110,22 +110,28 @@ extension SceneDelegate { } private func setRootVC() { + ///是否首次开启过 let hasOpenApp = UserDefaults.standard.object(forKey: kSPHasBeenOpenedAPPDefaultsKey) as? Bool ///引导页 let guideVc = SPGuideViewController() - if hasOpenApp != true && guideVc.lanuchVC != nil { - SPAPPTool.isAppOpen = false - guideVc.openAppBlock = { - self.handleOpenApp() + guideVc.openAppBlock = { [weak self] in + self?.handleOpenApp() } window?.rootViewController = guideVc window?.makeKeyAndVisible() + } else if !SPAPPTool.isAppOpen, hasOpenApp == true, SPNetworkReachabilityManager.manager.isReachable == true { //开屏广告 + let openAdVC = SPAppOpenAdViewController() + openAdVC.didEndBlock = { [weak self] in + self?.handleOpenApp() + } + window?.rootViewController = openAdVC + window?.makeKeyAndVisible() + } else { - SPAPPTool.isAppOpen = true - setTabBarController() + handleOpenApp() } } @@ -139,6 +145,7 @@ extension SceneDelegate { ///打开app @objc private func handleOpenApp() { + SPAPPTool.isAppOpen = true setTabBarController() retryHandleOpenAppMessage() diff --git a/ThimraTV/Base/Controller/SPTabBarController.swift b/ThimraTV/Base/Controller/SPTabBarController.swift index fc9018e..ec3af7e 100644 --- a/ThimraTV/Base/Controller/SPTabBarController.swift +++ b/ThimraTV/Base/Controller/SPTabBarController.swift @@ -34,6 +34,8 @@ class SPTabBarController: UITabBarController { SPAPPTool.requestIDFAAuthorization { idfa in } + ///加载激励广告 + SPRewardedAdManager.manager.preloadRewardedAd() } diff --git a/ThimraTV/Base/WebView/SPWebViewController+ScriptMessage.swift b/ThimraTV/Base/WebView/SPWebViewController+ScriptMessage.swift index 685bd5a..179ca7d 100644 --- a/ThimraTV/Base/WebView/SPWebViewController+ScriptMessage.swift +++ b/ThimraTV/Base/WebView/SPWebViewController+ScriptMessage.swift @@ -130,10 +130,12 @@ extension SPWebViewController: SPRewardedAdManagerDelegate { func rewardedAdManager(manager: SPRewardedAdManager, didLoadFail error: any Error) { self.needAutoRefresh = true + SPRewardedAdManager.manager.delegate = nil } func rewardedAdManager(manager: SPRewardedAdManager, didDisplayFail error: any Error) { self.needAutoRefresh = true + SPRewardedAdManager.manager.delegate = nil } func rewardedAdManagerDidDismiss(manager: SPRewardedAdManager) { @@ -141,5 +143,6 @@ extension SPWebViewController: SPRewardedAdManagerDelegate { let js = "uploadCheckSignIn()" self.webView.evaluateJavaScript(js) + SPRewardedAdManager.manager.delegate = nil } } diff --git a/ThimraTV/Class/Guide/Controller/SPAppOpenAdViewController.swift b/ThimraTV/Class/Guide/Controller/SPAppOpenAdViewController.swift new file mode 100644 index 0000000..c71aba5 --- /dev/null +++ b/ThimraTV/Class/Guide/Controller/SPAppOpenAdViewController.swift @@ -0,0 +1,51 @@ +// +// SPAppOpenAdViewController.swift +// ThimraTV +// +// Created by 长沙佳儿 on 2025/7/11. +// + +import UIKit + +class SPAppOpenAdViewController: SPViewController { + + var didEndBlock: (() -> Void)? + + private(set) lazy var lanuchVC: UIViewController? = { + let vc = SPAPPTool.getLanuchViewController() + return vc + }() + + override func viewDidLoad() { + super.viewDidLoad() + if let vc = lanuchVC { + addChild(vc) + view.addSubview(vc.view) + } + + + let manager = SPAppOpenAdManager.manager + manager.delegate = self + manager.showAdIfAvailable() + } + + + + +} + +extension SPAppOpenAdViewController: SPAppOpenAdManagerDelegate { + + ///广告加载失败 + func appOpenAdManager(manager: SPAppOpenAdManager, didLoadFail error: Error) { + self.didEndBlock?() + } + ///广告展示失败 + func appOpenAdManager(manager: SPAppOpenAdManager, didDisplayFail error: Error) { + self.didEndBlock?() + } + ///广告被关闭 + func appOpenAdManagerDidDismiss(manager: SPAppOpenAdManager) { + self.didEndBlock?() + } +} diff --git a/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift b/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift index b974c46..fe4dec9 100644 --- a/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift +++ b/ThimraTV/Class/Mine/Controller/SPSettingsViewController.swift @@ -149,8 +149,9 @@ extension SPSettingsViewController { SPMineItem(type: .clearCache, title: "movia_Clear_cache".localized, subtitle: SPAppCacheManager.cacheToString(cache: cache)), SPMineItem(type: .aboutUs, title: "movia_profile_About_Us".localized), ] - arr.append(SPMineItem(type: .deleteAccount, title: "DeleteAccount".localized)) + if SPLoginManager.manager.isLogin { + arr.append(SPMineItem(type: .deleteAccount, title: "DeleteAccount".localized)) arr.append(SPMineItem(type: .logout, title: "movia_signout".localized)) } diff --git a/ThimraTV/Libs/APPTool/SPAPPTool.swift b/ThimraTV/Libs/APPTool/SPAPPTool.swift index 7ed97db..1e276f6 100644 --- a/ThimraTV/Libs/APPTool/SPAPPTool.swift +++ b/ThimraTV/Libs/APPTool/SPAPPTool.swift @@ -12,7 +12,7 @@ import AdSupport class SPAPPTool: NSObject { ///app开启状态 引导页结束后变为已开启 - static var isAppOpen = true + static var isAppOpen = false static var appDelegate: AppDelegate? static var sceneDelegate: SceneDelegate? diff --git a/ThimraTV/Libs/AdManager/AppOpenAd/SPAppOpenAdManager.swift b/ThimraTV/Libs/AdManager/AppOpenAd/SPAppOpenAdManager.swift index d2e0823..96643c1 100644 --- a/ThimraTV/Libs/AdManager/AppOpenAd/SPAppOpenAdManager.swift +++ b/ThimraTV/Libs/AdManager/AppOpenAd/SPAppOpenAdManager.swift @@ -8,45 +8,78 @@ import UIKit import GoogleMobileAds +@objc protocol SPAppOpenAdManagerDelegate: NSObjectProtocol { + ///广告加载失败 + @objc optional func appOpenAdManager(manager: SPAppOpenAdManager, didLoadFail error: Error) + ///广告加载成功 + @objc optional func appOpenAdManagerDidLoadFinish(manager: SPAppOpenAdManager) + ///广告展示失败 + @objc optional func appOpenAdManager(manager: SPAppOpenAdManager, didDisplayFail error: Error) + ///广告被展示 + @objc optional func appOpenAdManagerDidShow(manager: SPAppOpenAdManager) + ///广告被关闭 + @objc optional func appOpenAdManagerDidDismiss(manager: SPAppOpenAdManager) +} + class SPAppOpenAdManager: NSObject { - var appOpenAd: AppOpenAd? - var isLoadingAd = false - var isShowingAd = false + weak var delegate: SPAppOpenAdManagerDelegate? - static let shared = SPAppOpenAdManager() +#if DEBUG + let adUnitID = "ca-app-pub-3940256099942544/5575463023" +#endif - private func loadAd() async { + private var appOpenAd: AppOpenAd? + private(set) var isLoadingAd = false + private(set) var isShowingAd = false + private var isNeedShow = false + + static let manager = SPAppOpenAdManager() + + private func loadAd() { // Do not load ad if there is an unused ad or one is already loading. if isLoadingAd || isAdAvailable() { return } isLoadingAd = true - do { - appOpenAd = try await AppOpenAd.load(with: "ca-app-pub-3940256099942544/5575463023", request: Request()) + AppOpenAd.load(with: adUnitID, request: Request()) { [weak self] appOpenAd, error in + guard let self = self else { return } + self.isLoadingAd = false - appOpenAd?.fullScreenContentDelegate = self - } catch { - print("App open ad failed to load with error: \(error.localizedDescription)") + 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) + } } - isLoadingAd = false + + + } func showAdIfAvailable() { // If the app open ad is already showing, do not show the ad again. guard !isShowingAd else { return } + self.isNeedShow = true + // If the app open ad is not available yet but is supposed to show, load // a new ad. if !isAdAvailable() { - Task { - await loadAd() - } + self.loadAd() return } if let ad = appOpenAd { + self.isNeedShow = false isShowingAd = true ad.present(from: nil) } @@ -60,24 +93,57 @@ class SPAppOpenAdManager: NSObject { 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) } func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) { + self.requestStatAd(type: "close", errorMsg: nil) + appOpenAd = nil isShowingAd = false // Reload an ad. - Task { - await loadAd() - } +// self.loadAd() + self.delegate?.appOpenAdManagerDidDismiss?(manager: self) } func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) { + self.requestStatAd(type: "show_failed", errorMsg: nil) + appOpenAd = nil isShowingAd = false // Reload an ad. - Task { - await loadAd() - } +// self.loadAd() + + self.delegate?.appOpenAdManager?(manager: self, didDisplayFail: error) + } + + func adDidRecordClick(_ ad: any FullScreenPresentingAd) { + self.requestStatAd(type: "click", errorMsg: nil) + } + +} + +//MARK: -------------- 统计 -------------- +extension SPAppOpenAdManager { + + private func requestStatAd(type: String, errorMsg: String?) { + guard appOpenAd != nil else { return } + + let model = SPStatAdModel() + model.type = type + model.ads_id = adUnitID + model.ad_platform_key = .google + model.error_msg = errorMsg + model.scene = .splash + + SPStatAPI.requestStatAd(model: model) + } + + + @objc private func didEnterBackgroundNotification() { + + self.requestStatAd(type: "Interrupt", errorMsg: nil) } } diff --git a/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager+Admob.swift b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager+Admob.swift index 62c3be2..ceaa76f 100644 --- a/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager+Admob.swift +++ b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager+Admob.swift @@ -65,7 +65,13 @@ extension SPRewardedAdManager { func admob_loadRewardedAd(adInfo: SPAdInfo) { guard !self.admob_isLoadingRewardedAd else { return } +#if DEBUG + adInfo.ads_id = "ca-app-pub-3940256099942544/1712485313" +#endif + let adUnitID = adInfo.ads_id ?? "" + + let request = Request() self.admob_isLoadingRewardedAd = true diff --git a/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager.swift b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager.swift index 740cb5f..72a105c 100644 --- a/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager.swift +++ b/ThimraTV/Libs/AdManager/RewardedAd/SPRewardedAdManager.swift @@ -59,6 +59,14 @@ class SPRewardedAdManager: NSObject { NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil) } + func isAdAvailable() -> Bool { + guard let adInfo = adInfo else { return false } + if adInfo.platform_key == .google { + return admob_rewardedAd != nil ? true : false + } + return false + } + ///加载并展示激励广告 func loadAndShowRewardedAd(isShowLoading: Bool = true, isShowToast: Bool = true) { guard isEnable else { @@ -105,13 +113,14 @@ class SPRewardedAdManager: NSObject { } } } - } ///预加载一个广告 func preloadRewardedAd() { guard isEnable else { return } guard !isLoadingRewardedAd else { return } + guard self.adInfo == nil else { return } + isShowLoading = false isShowToast = false @@ -151,6 +160,12 @@ class SPRewardedAdManager: NSObject { } } + private func clean() { + self.adInfo = nil + self.statScene = nil + self.videoInfo = nil + } + } @@ -179,9 +194,8 @@ extension SPRewardedAdManager { self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription) } - self.statScene = nil - self.videoInfo = nil self.isEnable = false + self.clean() self.retryLoadAd() } @@ -205,8 +219,7 @@ extension SPRewardedAdManager { self.delegate?.rewardedAdManager?(manager: self, didDisplayFail: error) self.requestStatAd(type: "show_failed", errorMsg: error.localizedDescription) - self.statScene = nil - self.videoInfo = nil + self.clean() } ///广告被展示 @@ -233,8 +246,7 @@ extension SPRewardedAdManager { self.delegate?.rewardedAdManagerDidDismiss?(manager: self) } - self.statScene = nil - self.videoInfo = nil + self.clean() preloadRewardedAd() } diff --git a/ThimraTV/Libs/AdManager/SPAdManager.swift b/ThimraTV/Libs/AdManager/SPAdManager.swift index 6e137d8..95b6030 100644 --- a/ThimraTV/Libs/AdManager/SPAdManager.swift +++ b/ThimraTV/Libs/AdManager/SPAdManager.swift @@ -33,8 +33,8 @@ class SPAdManager: NSObject { // // Start loading ads // } #endif - //预加载一个激励广告 - SPRewardedAdManager.manager.preloadRewardedAd() + +// SPRewardedAdManager.manager.preloadRewardedAd() } diff --git a/ThimraTV/Libs/AdManager/SPStatAdModel.swift b/ThimraTV/Libs/AdManager/SPStatAdModel.swift index ca075b8..a889fb6 100644 --- a/ThimraTV/Libs/AdManager/SPStatAdModel.swift +++ b/ThimraTV/Libs/AdManager/SPStatAdModel.swift @@ -14,6 +14,7 @@ class SPStatAdModel: SPModel, SmartCodable { case detail = "detail" case me = "me" case reward = "reward" + case splash = "splash" } var type: String? //start click error click show_failed load_failed Interrupt(退到后台) close