看广告签到

This commit is contained in:
zeng 2025-07-11 09:41:41 +08:00
parent 1771235bec
commit 0232c818d8
16 changed files with 185 additions and 27 deletions

View File

@ -280,6 +280,7 @@
1BF5130C2E1F4660009750EA /* SPRewardedAdManager+AppLovin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BF5130B2E1F4654009750EA /* SPRewardedAdManager+AppLovin.swift */; };
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 */; };
C3D1CE788CA03A1878493356 /* Pods_ThimraTV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64805795B479324EB764157 /* Pods_ThimraTV.framework */; };
/* End PBXBuildFile section */
@ -591,6 +592,7 @@
1BF5130B2E1F4654009750EA /* SPRewardedAdManager+AppLovin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SPRewardedAdManager+AppLovin.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>"; };
1BF513132E1FB8C1009750EA /* SPAppOpenAdManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAppOpenAdManager.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>"; };
@ -1480,6 +1482,7 @@
1BDE20112E1E158400C2C2B5 /* AdManager */ = {
isa = PBXGroup;
children = (
1BF513122E1FB897009750EA /* AppOpenAd */,
1BF5130F2E1F5EE4009750EA /* RewardedAd */,
1BDE20122E1E159B00C2C2B5 /* SPAdManager.swift */,
1BDE20182E1E175800C2C2B5 /* SPAdInfo.swift */,
@ -1498,6 +1501,14 @@
path = RewardedAd;
sourceTree = "<group>";
};
1BF513122E1FB897009750EA /* AppOpenAd */ = {
isa = PBXGroup;
children = (
1BF513132E1FB8C1009750EA /* SPAppOpenAdManager.swift */,
);
path = AppOpenAd;
sourceTree = "<group>";
};
1DBC40502DA4EDFC0093FCB0 = {
isa = PBXGroup;
children = (
@ -1773,6 +1784,7 @@
1BB91D492E04FD6A00A2C715 /* SPTextField.swift in Sources */,
1BB91D4A2E04FD6A00A2C715 /* SPCampaignWebViewController.swift in Sources */,
1BB91D4B2E04FD6A00A2C715 /* SPWebMessageModel.swift in Sources */,
1BF513142E1FB8C1009750EA /* SPAppOpenAdManager.swift in Sources */,
1BDE20192E1E175800C2C2B5 /* SPAdInfo.swift in Sources */,
1BB91D4C2E04FD6A00A2C715 /* SPWebView.swift in Sources */,
1BB91D4D2E04FD6A00A2C715 /* SPWebViewController.swift in Sources */,
@ -2094,7 +2106,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.2;
MARKETING_VERSION = 1.1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.thimratv.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2142,7 +2154,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.2;
MARKETING_VERSION = 1.1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.thimratv.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -20,7 +20,7 @@ class SPTabBarController: UITabBarController {
let nav3 = createNavigationController(viewController: SPMyListViewController(), title: "movia_my_list".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected"))
let nav4 = createNavigationController(viewController: SPRewardsViewController(), title: "movia_rewards".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected"))
let nav4 = createNavigationController(viewController: SPRewardsViewController(), title: "movia_rewards".localized, image: UIImage(named: "tabbar_icon_03"), selectedImage: UIImage(named: "tabbar_icon_03_selected"))
let nav5 = createNavigationController(viewController: SPMineViewController(), title: "movia_profile".localized, image: UIImage(named: "tabbar_icon_05"), selectedImage: UIImage(named: "tabbar_icon_05_selected"))

View File

@ -74,7 +74,7 @@ class SPStatAPI: NSObject {
}
///广
static func requestStatAd(model: SPStatAdModel) {
static func requestStatAd(model: SPStatAdModel, completer: (() -> Void)? = nil) {
var param = SPNetworkParameters(path: "/ad/history")
param.isToast = false
@ -82,7 +82,7 @@ class SPStatAPI: NSObject {
param.parameters = model.toDictionary()
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<String>) in
completer?()
}
}

View File

@ -17,6 +17,7 @@ class SPWebView: WKWebView {
WebMessageOpenFeedbackList,
WebMessageOpenFeedbackDetail,
WebMessageOpenPhotoPicker,
WebMessageOpenCheckSignIn,
]
@ -62,8 +63,8 @@ class SPWebView: WKWebView {
func load(urlStr: String) {
guard let url = URL(string: urlStr) else { return }
// var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
var request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 30)
// let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 30)
self.load(request)
}

View File

@ -18,6 +18,8 @@ let WebMessageOpenFeedbackList: SPWebViewMessageName = "openFeedbackList"
let WebMessageOpenFeedbackDetail: SPWebViewMessageName = "openFeedbackDetail"
///
let WebMessageOpenPhotoPicker: SPWebViewMessageName = "openPhotoPicker"
///
let WebMessageOpenCheckSignIn: SPWebViewMessageName = "openCheckSignIn"
extension SPWebViewController {
@ -74,7 +76,12 @@ extension SPWebViewController {
}
} else if name == WebMessageOpenCheckSignIn { //
self.needAutoRefresh = false
let manager = SPRewardedAdManager.manager
manager.statScene = .reward
manager.delegate = self
manager.loadAndShowRewardedAd()
}
@ -117,3 +124,22 @@ extension SPWebViewController: TZImagePickerControllerDelegate {
}
}
//MARK: -------------- SPRewardedAdManagerDelegate --------------
extension SPWebViewController: SPRewardedAdManagerDelegate {
func rewardedAdManager(manager: SPRewardedAdManager, didLoadFail error: any Error) {
self.needAutoRefresh = true
}
func rewardedAdManager(manager: SPRewardedAdManager, didDisplayFail error: any Error) {
self.needAutoRefresh = true
}
func rewardedAdManagerDidDismiss(manager: SPRewardedAdManager) {
self.needAutoRefresh = true
let js = "uploadCheckSignIn()"
self.webView.evaluateJavaScript(js)
}
}

View File

@ -15,6 +15,8 @@ class SPWebViewController: SPViewController {
///
var autoTitle = true
var needAutoRefresh = true
private(set) lazy var webView: SPWebView = {
let controller = WKUserContentController()
@ -51,10 +53,7 @@ class SPWebViewController: SPViewController {
func load(urlString: String) {
let str: String = urlString
guard let url = URL(string: str) else { return }
let request = URLRequest(url: url, timeoutInterval: 30)
self.webView.load(request)
self.webView.load(urlStr: str)
}
func reload() {

View File

@ -118,6 +118,7 @@ extension SPMineViewController {
guard SPLoginManager.manager.userInfo?.user_level == .ad else { return }
guard needShowRewardedAd else { return }
needShowRewardedAd = false
guard SPRewardedAdManager.manager.isEnable else { return }
let manager = SPRewardedAdManager.manager
manager.statScene = .me

View File

@ -16,6 +16,7 @@ class SPRewardsViewController: SPCampaignWebViewController {
private var isFirst = true
override func viewDidLoad() {
self.urlStr = SPRewardsWebUrl
super.viewDidLoad()
@ -54,7 +55,9 @@ class SPRewardsViewController: SPCampaignWebViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !isFirst {
self.reload()
if needAutoRefresh {
self.reload()
}
} else {
isFirst = false
}

View File

@ -0,0 +1,83 @@
//
// SPAppOpenAdManager.swift
// ThimraTV
//
// Created by on 2025/7/10.
//
import UIKit
import GoogleMobileAds
class SPAppOpenAdManager: NSObject {
var appOpenAd: AppOpenAd?
var isLoadingAd = false
var isShowingAd = false
static let shared = SPAppOpenAdManager()
private func loadAd() async {
// 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?.fullScreenContentDelegate = self
} catch {
print("App open ad failed to load with error: \(error.localizedDescription)")
}
isLoadingAd = false
}
func showAdIfAvailable() {
// If the app open ad is already showing, do not show the ad again.
guard !isShowingAd else { return }
// If the app open ad is not available yet but is supposed to show, load
// a new ad.
if !isAdAvailable() {
Task {
await loadAd()
}
return
}
if let ad = appOpenAd {
isShowingAd = true
ad.present(from: nil)
}
}
private func isAdAvailable() -> Bool {
// Check if ad exists and can be shown.
return appOpenAd != nil
}
}
extension SPAppOpenAdManager: FullScreenContentDelegate {
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
print("App open ad will be presented.")
}
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
appOpenAd = nil
isShowingAd = false
// Reload an ad.
Task {
await loadAd()
}
}
func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
appOpenAd = nil
isShowingAd = false
// Reload an ad.
Task {
await loadAd()
}
}
}

View File

@ -36,6 +36,8 @@ class SPRewardedAdManager: NSObject {
private var retryCount = 0
///
private let retryMaxCount = 1
///广
private(set) var isEnable = true
private(set) var adInfo: SPAdInfo?
@ -47,6 +49,7 @@ class SPRewardedAdManager: NSObject {
var statScene: SPStatAdModel.AdScene?
var videoInfo: SPVideoInfoModel?
deinit {
NotificationCenter.default.removeObserver(self)
}
@ -57,23 +60,41 @@ class SPRewardedAdManager: NSObject {
}
///广
func loadAndShowRewardedAd() {
isShowLoading = true
isShowToast = true
func loadAndShowRewardedAd(isShowLoading: Bool = true, isShowToast: Bool = true) {
guard isEnable else {
let text = "movia_no_ads_tip".localized
if isShowToast {
SPToast.show(text: text)
}
self.isShowToast = false
self.isShowLoading = false
let error = NSError(domain: text.localized, code: -1)
loadFailHandler(isStat: false, error: error)
return
}
SPHUD.show()
self.isShowLoading = isShowLoading
self.isShowToast = isShowToast
if let adInfo = adInfo {//广
if self.isShowLoading {
SPHUD.show()
}
if let adInfo = adInfo {//广
if adInfo.platform_key == .google {
self.admob_loadAndShowRewardedAd(adInfo: adInfo)
}
} else {
guard !isLoadingRewardedAd else { return }
self.isLoadingRewardedAd = true
SPAdAPI.requestShowAdInfo { [weak self] adInfo in
guard let self = self else { return }
guard let adInfo = adInfo else {
SPHUD.dismiss()
SPToast.show(text: "movia_no_ads_tip".localized)
let text = "movia_no_ads_tip".localized
let error = NSError(domain: text.localized, code: -1)
loadFailHandler(isStat: false, error: error)
return
}
self.adInfo = adInfo
@ -89,6 +110,7 @@ class SPRewardedAdManager: NSObject {
///广
func preloadRewardedAd() {
guard isEnable else { return }
guard !isLoadingRewardedAd else { return }
isShowLoading = false
isShowToast = false
@ -112,6 +134,8 @@ class SPRewardedAdManager: NSObject {
///广
private func retryLoadAd() {
guard isEnable else { return }
guard retryCount < retryMaxCount else {
retryCount = 0
retryAttempt = 0
@ -141,7 +165,7 @@ extension SPRewardedAdManager {
}
///广
func loadFailHandler(error: Error) {
func loadFailHandler(isStat: Bool = true, error: Error) {
if isShowLoading {
SPHUD.dismiss()
@ -151,10 +175,13 @@ extension SPRewardedAdManager {
}
self.isLoadingRewardedAd = false
self.delegate?.rewardedAdManager?(manager: self, didLoadFail: error)
self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
if isStat {
self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
}
self.statScene = nil
self.videoInfo = nil
self.isEnable = false
self.retryLoadAd()
}
@ -195,13 +222,16 @@ extension SPRewardedAdManager {
}
///广
func didDismissHandler() {
self.delegate?.rewardedAdManagerDidDismiss?(manager: self)
var seconds = 0
if let adsDate = self.adsDate {
seconds = Int(Date().timeIntervalSince(adsDate))
}
self.requestStatAd(type: "close", seconds: seconds, errorMsg: nil)
self.requestStatAd(type: "close", seconds: seconds, errorMsg: nil) { [weak self] in
guard let self = self else { return }
self.delegate?.rewardedAdManagerDidDismiss?(manager: self)
}
self.statScene = nil
self.videoInfo = nil
@ -224,7 +254,7 @@ extension SPRewardedAdManager {
//MARK: -------------- --------------
extension SPRewardedAdManager {
private func requestStatAd(type: String, seconds: Int = 0, errorMsg: String?) {
private func requestStatAd(type: String, seconds: Int = 0, errorMsg: String?, completer: (() -> Void)? = nil) {
guard let adInfo = adInfo else { return }
let model = SPStatAdModel()
@ -237,7 +267,7 @@ extension SPRewardedAdManager {
model.short_play_id = self.videoInfo?.short_play_id
model.short_play_video_id = self.videoInfo?.short_play_video_id
SPStatAPI.requestStatAd(model: model)
SPStatAPI.requestStatAd(model: model, completer: completer)
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -5,10 +5,12 @@
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB