启屏广告

This commit is contained in:
zeng 2025-07-11 11:27:45 +08:00
parent 0232c818d8
commit 7736f5ed02
13 changed files with 192 additions and 37 deletions

View File

@ -281,6 +281,7 @@
1BF5130E2E1F5D9B009750EA /* SPRewardedAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */; }; 1BF5130E2E1F5D9B009750EA /* SPRewardedAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */; };
1BF513112E1FA138009750EA /* SPStatAdModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513102E1FA138009750EA /* SPStatAdModel.swift */; }; 1BF513112E1FA138009750EA /* SPStatAdModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513102E1FA138009750EA /* SPStatAdModel.swift */; };
1BF513142E1FB8C1009750EA /* SPAppOpenAdManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF513132E1FB8C1009750EA /* SPAppOpenAdManager.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 */; }; C3D1CE788CA03A1878493356 /* Pods_ThimraTV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64805795B479324EB764157 /* Pods_ThimraTV.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -593,6 +594,7 @@
1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPRewardedAdManager.swift; sourceTree = "<group>"; }; 1BF5130D2E1F5D8F009750EA /* SPRewardedAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPRewardedAdManager.swift; sourceTree = "<group>"; };
1BF513102E1FA138009750EA /* SPStatAdModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPStatAdModel.swift; sourceTree = "<group>"; }; 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>"; }; 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>"; };
1DBC40592DA4EDFC0093FCB0 /* ThimraTV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThimraTV.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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>"; }; 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>"; }; 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>"; };
@ -833,6 +835,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1BB91C142E04FD6A00A2C715 /* SPGuideViewController.swift */, 1BB91C142E04FD6A00A2C715 /* SPGuideViewController.swift */,
1BF513152E20ADB4009750EA /* SPAppOpenAdViewController.swift */,
); );
path = Controller; path = Controller;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1984,6 +1987,7 @@
1BB91E062E04FD6A00A2C715 /* UIViewController+WMPageController.m in Sources */, 1BB91E062E04FD6A00A2C715 /* UIViewController+WMPageController.m in Sources */,
1BB91E072E04FD6A00A2C715 /* WMPageController.m in Sources */, 1BB91E072E04FD6A00A2C715 /* WMPageController.m in Sources */,
1BB91E082E04FD6A00A2C715 /* ZKCycleScrollView.swift in Sources */, 1BB91E082E04FD6A00A2C715 /* ZKCycleScrollView.swift in Sources */,
1BF513162E20ADB4009750EA /* SPAppOpenAdViewController.swift in Sources */,
1BB91E092E04FD6A00A2C715 /* ZKCycleScrollViewFlowLayout.swift in Sources */, 1BB91E092E04FD6A00A2C715 /* ZKCycleScrollViewFlowLayout.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -54,6 +54,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if SPNetworkReachabilityManager.manager.isReachable == true { if SPNetworkReachabilityManager.manager.isReachable == true {
// SPLoginManager.manager.requestVisitorLogin(completer: nil) // SPLoginManager.manager.requestVisitorLogin(completer: nil)
SPLoginManager.manager.updateUserInfo(completer: nil) SPLoginManager.manager.updateUserInfo(completer: nil)
SPRewardedAdManager.manager.preloadRewardedAd()
} }
} }

View File

@ -110,22 +110,28 @@ extension SceneDelegate {
} }
private func setRootVC() { private func setRootVC() {
///
let hasOpenApp = UserDefaults.standard.object(forKey: kSPHasBeenOpenedAPPDefaultsKey) as? Bool let hasOpenApp = UserDefaults.standard.object(forKey: kSPHasBeenOpenedAPPDefaultsKey) as? Bool
/// ///
let guideVc = SPGuideViewController() let guideVc = SPGuideViewController()
if hasOpenApp != true && guideVc.lanuchVC != nil { if hasOpenApp != true && guideVc.lanuchVC != nil {
SPAPPTool.isAppOpen = false guideVc.openAppBlock = { [weak self] in
guideVc.openAppBlock = { self?.handleOpenApp()
self.handleOpenApp()
} }
window?.rootViewController = guideVc window?.rootViewController = guideVc
window?.makeKeyAndVisible() 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 { } else {
SPAPPTool.isAppOpen = true handleOpenApp()
setTabBarController()
} }
} }
@ -139,6 +145,7 @@ extension SceneDelegate {
///app ///app
@objc private func handleOpenApp() { @objc private func handleOpenApp() {
SPAPPTool.isAppOpen = true
setTabBarController() setTabBarController()
retryHandleOpenAppMessage() retryHandleOpenAppMessage()

View File

@ -34,6 +34,8 @@ class SPTabBarController: UITabBarController {
SPAPPTool.requestIDFAAuthorization { idfa in SPAPPTool.requestIDFAAuthorization { idfa in
} }
///广
SPRewardedAdManager.manager.preloadRewardedAd()
} }

View File

@ -130,10 +130,12 @@ extension SPWebViewController: SPRewardedAdManagerDelegate {
func rewardedAdManager(manager: SPRewardedAdManager, didLoadFail error: any Error) { func rewardedAdManager(manager: SPRewardedAdManager, didLoadFail error: any Error) {
self.needAutoRefresh = true self.needAutoRefresh = true
SPRewardedAdManager.manager.delegate = nil
} }
func rewardedAdManager(manager: SPRewardedAdManager, didDisplayFail error: any Error) { func rewardedAdManager(manager: SPRewardedAdManager, didDisplayFail error: any Error) {
self.needAutoRefresh = true self.needAutoRefresh = true
SPRewardedAdManager.manager.delegate = nil
} }
func rewardedAdManagerDidDismiss(manager: SPRewardedAdManager) { func rewardedAdManagerDidDismiss(manager: SPRewardedAdManager) {
@ -141,5 +143,6 @@ extension SPWebViewController: SPRewardedAdManagerDelegate {
let js = "uploadCheckSignIn()" let js = "uploadCheckSignIn()"
self.webView.evaluateJavaScript(js) self.webView.evaluateJavaScript(js)
SPRewardedAdManager.manager.delegate = nil
} }
} }

View File

@ -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?()
}
}

View File

@ -149,8 +149,9 @@ extension SPSettingsViewController {
SPMineItem(type: .clearCache, title: "movia_Clear_cache".localized, subtitle: SPAppCacheManager.cacheToString(cache: cache)), SPMineItem(type: .clearCache, title: "movia_Clear_cache".localized, subtitle: SPAppCacheManager.cacheToString(cache: cache)),
SPMineItem(type: .aboutUs, title: "movia_profile_About_Us".localized), SPMineItem(type: .aboutUs, title: "movia_profile_About_Us".localized),
] ]
arr.append(SPMineItem(type: .deleteAccount, title: "DeleteAccount".localized))
if SPLoginManager.manager.isLogin { if SPLoginManager.manager.isLogin {
arr.append(SPMineItem(type: .deleteAccount, title: "DeleteAccount".localized))
arr.append(SPMineItem(type: .logout, title: "movia_signout".localized)) arr.append(SPMineItem(type: .logout, title: "movia_signout".localized))
} }

View File

@ -12,7 +12,7 @@ import AdSupport
class SPAPPTool: NSObject { class SPAPPTool: NSObject {
///app ///app
static var isAppOpen = true static var isAppOpen = false
static var appDelegate: AppDelegate? static var appDelegate: AppDelegate?
static var sceneDelegate: SceneDelegate? static var sceneDelegate: SceneDelegate?

View File

@ -8,45 +8,78 @@
import UIKit import UIKit
import GoogleMobileAds 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 { class SPAppOpenAdManager: NSObject {
var appOpenAd: AppOpenAd? weak var delegate: SPAppOpenAdManagerDelegate?
var isLoadingAd = false
var isShowingAd = false
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. // Do not load ad if there is an unused ad or one is already loading.
if isLoadingAd || isAdAvailable() { if isLoadingAd || isAdAvailable() {
return return
} }
isLoadingAd = true isLoadingAd = true
do { AppOpenAd.load(with: adUnitID, request: Request()) { [weak self] appOpenAd, error in
appOpenAd = try await AppOpenAd.load(with: "ca-app-pub-3940256099942544/5575463023", request: Request()) guard let self = self else { return }
self.isLoadingAd = false
appOpenAd?.fullScreenContentDelegate = self self.appOpenAd = appOpenAd
} catch { self.appOpenAd?.fullScreenContentDelegate = self
print("App open ad failed to load with error: \(error.localizedDescription)")
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() { func showAdIfAvailable() {
// If the app open ad is already showing, do not show the ad again. // If the app open ad is already showing, do not show the ad again.
guard !isShowingAd else { return } guard !isShowingAd else { return }
self.isNeedShow = true
// If the app open ad is not available yet but is supposed to show, load // If the app open ad is not available yet but is supposed to show, load
// a new ad. // a new ad.
if !isAdAvailable() { if !isAdAvailable() {
Task { self.loadAd()
await loadAd()
}
return return
} }
if let ad = appOpenAd { if let ad = appOpenAd {
self.isNeedShow = false
isShowingAd = true isShowingAd = true
ad.present(from: nil) ad.present(from: nil)
} }
@ -60,24 +93,57 @@ class SPAppOpenAdManager: NSObject {
extension SPAppOpenAdManager: FullScreenContentDelegate { extension SPAppOpenAdManager: FullScreenContentDelegate {
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) { func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
self.requestStatAd(type: "start", errorMsg: nil)
print("App open ad will be presented.") print("App open ad will be presented.")
self.delegate?.appOpenAdManagerDidShow?(manager: self)
} }
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) { func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
self.requestStatAd(type: "close", errorMsg: nil)
appOpenAd = nil appOpenAd = nil
isShowingAd = false isShowingAd = false
// Reload an ad. // Reload an ad.
Task { // self.loadAd()
await loadAd() self.delegate?.appOpenAdManagerDidDismiss?(manager: self)
}
} }
func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) { func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
self.requestStatAd(type: "show_failed", errorMsg: nil)
appOpenAd = nil appOpenAd = nil
isShowingAd = false isShowingAd = false
// Reload an ad. // Reload an ad.
Task { // self.loadAd()
await 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)
} }
} }

View File

@ -65,7 +65,13 @@ extension SPRewardedAdManager {
func admob_loadRewardedAd(adInfo: SPAdInfo) { func admob_loadRewardedAd(adInfo: SPAdInfo) {
guard !self.admob_isLoadingRewardedAd else { return } guard !self.admob_isLoadingRewardedAd else { return }
#if DEBUG
adInfo.ads_id = "ca-app-pub-3940256099942544/1712485313"
#endif
let adUnitID = adInfo.ads_id ?? "" let adUnitID = adInfo.ads_id ?? ""
let request = Request() let request = Request()
self.admob_isLoadingRewardedAd = true self.admob_isLoadingRewardedAd = true

View File

@ -59,6 +59,14 @@ class SPRewardedAdManager: NSObject {
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil) 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) { func loadAndShowRewardedAd(isShowLoading: Bool = true, isShowToast: Bool = true) {
guard isEnable else { guard isEnable else {
@ -105,13 +113,14 @@ class SPRewardedAdManager: NSObject {
} }
} }
} }
} }
///广 ///广
func preloadRewardedAd() { func preloadRewardedAd() {
guard isEnable else { return } guard isEnable else { return }
guard !isLoadingRewardedAd else { return } guard !isLoadingRewardedAd else { return }
guard self.adInfo == nil else { return }
isShowLoading = false isShowLoading = false
isShowToast = 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.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
} }
self.statScene = nil
self.videoInfo = nil
self.isEnable = false self.isEnable = false
self.clean()
self.retryLoadAd() self.retryLoadAd()
} }
@ -205,8 +219,7 @@ extension SPRewardedAdManager {
self.delegate?.rewardedAdManager?(manager: self, didDisplayFail: error) self.delegate?.rewardedAdManager?(manager: self, didDisplayFail: error)
self.requestStatAd(type: "show_failed", errorMsg: error.localizedDescription) self.requestStatAd(type: "show_failed", errorMsg: error.localizedDescription)
self.statScene = nil self.clean()
self.videoInfo = nil
} }
///广 ///广
@ -233,8 +246,7 @@ extension SPRewardedAdManager {
self.delegate?.rewardedAdManagerDidDismiss?(manager: self) self.delegate?.rewardedAdManagerDidDismiss?(manager: self)
} }
self.statScene = nil self.clean()
self.videoInfo = nil
preloadRewardedAd() preloadRewardedAd()
} }

View File

@ -33,8 +33,8 @@ class SPAdManager: NSObject {
// // Start loading ads // // Start loading ads
// } // }
#endif #endif
//广
SPRewardedAdManager.manager.preloadRewardedAd() // SPRewardedAdManager.manager.preloadRewardedAd()
} }

View File

@ -14,6 +14,7 @@ class SPStatAdModel: SPModel, SmartCodable {
case detail = "detail" case detail = "detail"
case me = "me" case me = "me"
case reward = "reward" case reward = "reward"
case splash = "splash"
} }
var type: String? //start click error click show_failed load_failed Interrupt(退) close var type: String? //start click error click show_failed load_failed Interrupt(退) close