Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b02f655fb1 | ||
|
ab478455fd | ||
|
c1ad644d81 | ||
|
01eb1c6c6a | ||
|
ee36da46d3 | ||
|
d6042c7786 | ||
|
ac09b0612b |
@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
1B222BCF2E2B80DD002F5A68 /* SPPayTemplateRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B222BCE2E2B80DD002F5A68 /* SPPayTemplateRequest.swift */; };
|
||||||
1BB91D102E04FD6A00A2C715 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBD2E04FD6A00A2C715 /* AppDelegate.swift */; };
|
1BB91D102E04FD6A00A2C715 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBD2E04FD6A00A2C715 /* AppDelegate.swift */; };
|
||||||
1BB91D112E04FD6A00A2C715 /* AppDelegate+APNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBE2E04FD6A00A2C715 /* AppDelegate+APNS.swift */; };
|
1BB91D112E04FD6A00A2C715 /* AppDelegate+APNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBE2E04FD6A00A2C715 /* AppDelegate+APNS.swift */; };
|
||||||
1BB91D122E04FD6A00A2C715 /* AppDelegate+Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBF2E04FD6A00A2C715 /* AppDelegate+Config.swift */; };
|
1BB91D122E04FD6A00A2C715 /* AppDelegate+Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB91BBF2E04FD6A00A2C715 /* AppDelegate+Config.swift */; };
|
||||||
@ -316,7 +317,7 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
0538826A0638D33FEF3A2E38 /* Pods-ThimraTV.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThimraTV.debug.xcconfig"; path = "Target Support Files/Pods-ThimraTV/Pods-ThimraTV.debug.xcconfig"; sourceTree = "<group>"; };
|
0538826A0638D33FEF3A2E38 /* Pods-ThimraTV.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThimraTV.debug.xcconfig"; path = "Target Support Files/Pods-ThimraTV/Pods-ThimraTV.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
109EB01BE447EE135493CA38 /* Pods-MoviaBox.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoviaBox.release.xcconfig"; path = "Target Support Files/Pods-MoviaBox/Pods-MoviaBox.release.xcconfig"; sourceTree = "<group>"; };
|
1B222BCE2E2B80DD002F5A68 /* SPPayTemplateRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPPayTemplateRequest.swift; sourceTree = "<group>"; };
|
||||||
1BB91BBD2E04FD6A00A2C715 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
1BB91BBD2E04FD6A00A2C715 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
1BB91BBE2E04FD6A00A2C715 /* AppDelegate+APNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+APNS.swift"; sourceTree = "<group>"; };
|
1BB91BBE2E04FD6A00A2C715 /* AppDelegate+APNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+APNS.swift"; sourceTree = "<group>"; };
|
||||||
1BB91BBF2E04FD6A00A2C715 /* AppDelegate+Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Config.swift"; sourceTree = "<group>"; };
|
1BB91BBF2E04FD6A00A2C715 /* AppDelegate+Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Config.swift"; sourceTree = "<group>"; };
|
||||||
@ -606,11 +607,8 @@
|
|||||||
1BF513202E2662DC009750EA /* SPAdmobBannerAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPAdmobBannerAd.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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
||||||
B64805795B479324EB764157 /* Pods_ThimraTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ThimraTV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
B64805795B479324EB764157 /* Pods_ThimraTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ThimraTV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
F7763FEFB6BEB1A75D6FBA0A /* Pods-Thimra.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Thimra.debug.xcconfig"; path = "Target Support Files/Pods-Thimra/Pods-Thimra.debug.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
FEA583158A7C05D8D7C5A9FC /* Pods-Thimra.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Thimra.release.xcconfig"; path = "Target Support Files/Pods-Thimra/Pods-Thimra.release.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -640,10 +638,6 @@
|
|||||||
0061C3496D158807460301A9 /* Pods */ = {
|
0061C3496D158807460301A9 /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
F7763FEFB6BEB1A75D6FBA0A /* Pods-Thimra.debug.xcconfig */,
|
|
||||||
FEA583158A7C05D8D7C5A9FC /* Pods-Thimra.release.xcconfig */,
|
|
||||||
1F666DE0B12C863F26BE5027 /* Pods-MoviaBox.debug.xcconfig */,
|
|
||||||
109EB01BE447EE135493CA38 /* Pods-MoviaBox.release.xcconfig */,
|
|
||||||
0538826A0638D33FEF3A2E38 /* Pods-ThimraTV.debug.xcconfig */,
|
0538826A0638D33FEF3A2E38 /* Pods-ThimraTV.debug.xcconfig */,
|
||||||
A1174E10BCF2C606F7818792 /* Pods-ThimraTV.release.xcconfig */,
|
A1174E10BCF2C606F7818792 /* Pods-ThimraTV.release.xcconfig */,
|
||||||
);
|
);
|
||||||
@ -1275,6 +1269,7 @@
|
|||||||
1BB91CC82E04FD6A00A2C715 /* SPIAPOrderModel.swift */,
|
1BB91CC82E04FD6A00A2C715 /* SPIAPOrderModel.swift */,
|
||||||
1BB91CC92E04FD6A00A2C715 /* SPIAPVerifyModel.swift */,
|
1BB91CC92E04FD6A00A2C715 /* SPIAPVerifyModel.swift */,
|
||||||
1BB91CCA2E04FD6A00A2C715 /* SPWaitRestoreModel.swift */,
|
1BB91CCA2E04FD6A00A2C715 /* SPWaitRestoreModel.swift */,
|
||||||
|
1B222BCE2E2B80DD002F5A68 /* SPPayTemplateRequest.swift */,
|
||||||
);
|
);
|
||||||
path = SPIAPManager;
|
path = SPIAPManager;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1882,6 +1877,7 @@
|
|||||||
1BB91D872E04FD6A00A2C715 /* SPAboutUsViewController.swift in Sources */,
|
1BB91D872E04FD6A00A2C715 /* SPAboutUsViewController.swift in Sources */,
|
||||||
1BB91D882E04FD6A00A2C715 /* SPDeleteAccountViewController.swift in Sources */,
|
1BB91D882E04FD6A00A2C715 /* SPDeleteAccountViewController.swift in Sources */,
|
||||||
1BB91D892E04FD6A00A2C715 /* SPFeedbackViewController.swift in Sources */,
|
1BB91D892E04FD6A00A2C715 /* SPFeedbackViewController.swift in Sources */,
|
||||||
|
1B222BCF2E2B80DD002F5A68 /* SPPayTemplateRequest.swift in Sources */,
|
||||||
1BB91D8A2E04FD6A00A2C715 /* SPLanguageViewController.swift in Sources */,
|
1BB91D8A2E04FD6A00A2C715 /* SPLanguageViewController.swift in Sources */,
|
||||||
1BB91D8B2E04FD6A00A2C715 /* SPMineViewController.swift in Sources */,
|
1BB91D8B2E04FD6A00A2C715 /* SPMineViewController.swift in Sources */,
|
||||||
1BB91D8C2E04FD6A00A2C715 /* SPSettingsViewController.swift in Sources */,
|
1BB91D8C2E04FD6A00A2C715 /* SPSettingsViewController.swift in Sources */,
|
||||||
|
@ -23,7 +23,7 @@ class SPVideoAPI: NSObject {
|
|||||||
var param = SPNetworkParameters(path: "/getVideoDetails")
|
var param = SPNetworkParameters(path: "/getVideoDetails")
|
||||||
param.method = .get
|
param.method = .get
|
||||||
param.parameters = parameters
|
param.parameters = parameters
|
||||||
param.isLoding = true
|
param.isLoding = false
|
||||||
param.isToast = false
|
param.isToast = false
|
||||||
|
|
||||||
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPVideoDetailModel>) in
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPVideoDetailModel>) in
|
||||||
|
@ -14,15 +14,20 @@ class SPWalletAPI: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///获取支付模版
|
///获取支付模版
|
||||||
static func requestPayTemplate(completer: ((_ model: SPPayTemplateModel?) -> Void)?) {
|
static func requestPayTemplate(isLoding: Bool = false, isToast: Bool = true, completer: ((_ model: SPPayTemplateModel?) -> Void)?) {
|
||||||
|
|
||||||
var param = SPNetworkParameters(path: "/paySettingsV3")
|
var param = SPNetworkParameters(path: "/paySettingsV3")
|
||||||
param.method = .get
|
param.method = .get
|
||||||
|
param.isToast = isToast
|
||||||
|
param.isLoding = false
|
||||||
|
|
||||||
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPPayTemplateModel>) in
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPPayTemplateModel>) in
|
||||||
completer?(response.data)
|
completer?(response.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///创建内购订单
|
///创建内购订单
|
||||||
static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, completer: ((_ orderModel: SPIAPOrderModel?) -> Void)?) {
|
static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, completer: ((_ orderModel: SPIAPOrderModel?) -> Void)?) {
|
||||||
var param = SPNetworkParameters(path: "/createOrder")
|
var param = SPNetworkParameters(path: "/createOrder")
|
||||||
|
@ -90,7 +90,7 @@ extension SPApi: TargetType {
|
|||||||
"system-type" : "ios",
|
"system-type" : "ios",
|
||||||
"idfa" : SPAPPTool.getIdfa(),
|
"idfa" : SPAPPTool.getIdfa(),
|
||||||
"model" : UIDevice.sp_machineModelName(),
|
"model" : UIDevice.sp_machineModelName(),
|
||||||
// "security" : "false",
|
"device-gaid" : JXUUID.idfv()
|
||||||
]
|
]
|
||||||
//登录信息
|
//登录信息
|
||||||
dic["authorization"] = userToken
|
dic["authorization"] = userToken
|
||||||
|
@ -28,6 +28,8 @@ class SPMineViewController: SPViewController {
|
|||||||
private var needShowRewardedAd: Bool = false
|
private var needShowRewardedAd: Bool = false
|
||||||
|
|
||||||
weak var vipAlertView: SPVipAlertView?
|
weak var vipAlertView: SPVipAlertView?
|
||||||
|
|
||||||
|
private var payTemplateRequest: SPPayTemplateRequest?
|
||||||
//MARK: UI 属性
|
//MARK: UI 属性
|
||||||
private lazy var headerView: SPMineHeaderView = {
|
private lazy var headerView: SPMineHeaderView = {
|
||||||
let view = SPMineHeaderView()
|
let view = SPMineHeaderView()
|
||||||
@ -101,7 +103,10 @@ extension SPMineViewController {
|
|||||||
guard SPLoginManager.manager.userInfo?.is_vip != true else { return }
|
guard SPLoginManager.manager.userInfo?.is_vip != true else { return }
|
||||||
guard SPVipAlertView.isShowAlert else { return }
|
guard SPVipAlertView.isShowAlert else { return }
|
||||||
|
|
||||||
SPWalletAPI.requestPayTemplate { model in
|
self.payTemplateRequest = SPPayTemplateRequest()
|
||||||
|
self.payTemplateRequest?.requestProducts(isToast: false) { [weak self] model in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
guard let list = model?.list_sub_vip, list.count > 0 else { return }
|
guard let list = model?.list_sub_vip, list.count > 0 else { return }
|
||||||
if !self.isDidAppear { return }
|
if !self.isDidAppear { return }
|
||||||
if self.vipAlertView != nil { return }
|
if self.vipAlertView != nil { return }
|
||||||
|
@ -23,7 +23,9 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
|||||||
var activityId: String?
|
var activityId: String?
|
||||||
var playHistoryModel: SPShortModel?
|
var playHistoryModel: SPShortModel?
|
||||||
|
|
||||||
private var detailModel: SPVideoDetailModel?
|
// private var detailModel: SPVideoDetailModel?
|
||||||
|
|
||||||
|
private var detailDataArr: [Any] = []
|
||||||
|
|
||||||
///上一次上报播放时长的节点
|
///上一次上报播放时长的节点
|
||||||
private var lastUploadTime: Int = 0
|
private var lastUploadTime: Int = 0
|
||||||
@ -160,6 +162,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
|||||||
|
|
||||||
self.pause()
|
self.pause()
|
||||||
let view = SPPlayerDetailRecommandView()
|
let view = SPPlayerDetailRecommandView()
|
||||||
|
view.currentVideoInfo = self.viewModel.currentPlayer?.videoInfo
|
||||||
view.clickCloseButton = { [weak self] in
|
view.clickCloseButton = { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self._handleBack()
|
self._handleBack()
|
||||||
@ -246,12 +249,16 @@ extension SPPlayerDetailViewController {
|
|||||||
extension SPPlayerDetailViewController {
|
extension SPPlayerDetailViewController {
|
||||||
|
|
||||||
private func onEpisode() {
|
private func onEpisode() {
|
||||||
|
guard detailDataArr.count > 0 else { return }
|
||||||
|
guard let detailModel = detailDataArr[self.viewModel.currentIndexPath.section] as? SPVideoDetailModel else { return }
|
||||||
|
|
||||||
|
|
||||||
let view = SPEpisodeView()
|
let view = SPEpisodeView()
|
||||||
view.dataArr = detailModel?.episodeList ?? []
|
view.dataArr = detailModel.episodeList ?? []
|
||||||
view.shortModel = detailModel?.shortPlayInfo
|
view.shortModel = detailModel.shortPlayInfo
|
||||||
view.currentIndex = self.viewModel.currentIndexPath.row
|
view.currentIndex = self.viewModel.currentIndexPath.row
|
||||||
view.didSelectedIndex = { [weak self] (index) in
|
view.didSelectedIndex = { [weak self] (index) in
|
||||||
self?.scrollToItem(indexPath: IndexPath(row: index, section: 0), animated: false)
|
self?.scrollToItem(indexPath: IndexPath(row: index, section: self?.viewModel.currentIndexPath.section ?? 0), animated: false)
|
||||||
}
|
}
|
||||||
view.present(in: nil)
|
view.present(in: nil)
|
||||||
self.episodeView = view
|
self.episodeView = view
|
||||||
@ -320,7 +327,7 @@ extension SPPlayerDetailViewController {
|
|||||||
}
|
}
|
||||||
///网络切换通知
|
///网络切换通知
|
||||||
@objc private func reachabilityDidChangeNotification() {
|
@objc private func reachabilityDidChangeNotification() {
|
||||||
if SPNetworkReachabilityManager.manager.isReachable == true && self.detailModel == nil {
|
if SPNetworkReachabilityManager.manager.isReachable == true && self.detailDataArr.isEmpty {
|
||||||
self.requestDetailData()
|
self.requestDetailData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,6 +351,8 @@ extension SPPlayerDetailViewController {
|
|||||||
//MARK: -------------- SPPlayerListViewControllerDataSource --------------
|
//MARK: -------------- SPPlayerListViewControllerDataSource --------------
|
||||||
extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SPPlayerListViewControllerDelegate {
|
extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SPPlayerListViewControllerDelegate {
|
||||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell {
|
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell {
|
||||||
|
let detailModel = self.detailDataArr[indexPath.section] as? SPVideoDetailModel
|
||||||
|
|
||||||
if let cell = oldCell as? SPPlayerDetailCell {
|
if let cell = oldCell as? SPPlayerDetailCell {
|
||||||
cell.shortModel = detailModel?.shortPlayInfo
|
cell.shortModel = detailModel?.shortPlayInfo
|
||||||
cell.videoInfo = detailModel?.episodeList?[indexPath.row]
|
cell.videoInfo = detailModel?.episodeList?[indexPath.row]
|
||||||
@ -371,15 +380,40 @@ extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SP
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int, oldNumber: Int) -> Int {
|
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int, oldNumber: Int) -> Int {
|
||||||
return detailModel?.episodeList?.count ?? 0
|
if let model = self.detailDataArr[section] as? SPVideoDetailModel {
|
||||||
|
return model.episodeList?.count ?? 0
|
||||||
|
} else {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) {
|
func sp_playerListViewController(_ viewController: SPPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) {
|
||||||
self.episodeView?.currentIndex = indexPath.row
|
// self.episodeView?.currentIndex = indexPath.row
|
||||||
let videoInfo = detailModel?.episodeList?[indexPath.row]
|
|
||||||
|
if let detailModel = self.detailDataArr[indexPath.section] as? SPVideoDetailModel {
|
||||||
|
let videoInfo = detailModel.episodeList?[indexPath.row]
|
||||||
|
|
||||||
|
titleLabel.text = detailModel.shortPlayInfo?.name
|
||||||
|
episodeLabel.text = "\(videoInfo?.episode ?? "0")/\(detailModel.shortPlayInfo?.episode_total ?? 0)"
|
||||||
|
} else {
|
||||||
|
titleLabel.text = ""
|
||||||
|
episodeLabel.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sp_numberOfSections(in viewController: SPPlayerListViewController) -> Int {
|
||||||
|
return self.detailDataArr.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func sp_shouldAutoScrollNextEpisode(_ viewController: SPPlayerListViewController) -> Bool {
|
||||||
|
if episodeView != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func sp_playerViewControllerLoadMoreData(playerViewController: SPPlayerListViewController) {
|
||||||
|
|
||||||
titleLabel.text = detailModel?.shortPlayInfo?.name
|
|
||||||
episodeLabel.text = "\(videoInfo?.episode ?? "0")/\(detailModel?.shortPlayInfo?.episode_total ?? 0)"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +436,10 @@ extension SPPlayerDetailViewController {
|
|||||||
recommandTimer = nil
|
recommandTimer = nil
|
||||||
recommandTimer = Timer.scheduledTimer(timeInterval: 6, target: YYWeakProxy(target: self), selector: #selector(handleRecommandTimer), userInfo: nil, repeats: false)
|
recommandTimer = Timer.scheduledTimer(timeInterval: 6, target: YYWeakProxy(target: self), selector: #selector(handleRecommandTimer), userInfo: nil, repeats: false)
|
||||||
|
|
||||||
|
|
||||||
|
SPHUD.show(containerView: self.view)
|
||||||
SPVideoAPI.requestVideoDetail(videoId: videoId, shortPlayId: shortPlayId, activityId: activityId) { [weak self] model, code, msg in
|
SPVideoAPI.requestVideoDetail(videoId: videoId, shortPlayId: shortPlayId, activityId: activityId) { [weak self] model, code, msg in
|
||||||
|
SPHUD.dismiss()
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if code == 10014 {
|
if code == 10014 {
|
||||||
self.navigationController?.popViewController(animated: true)
|
self.navigationController?.popViewController(animated: true)
|
||||||
@ -410,16 +447,19 @@ extension SPPlayerDetailViewController {
|
|||||||
}
|
}
|
||||||
guard let model = model else { return }
|
guard let model = model else { return }
|
||||||
|
|
||||||
self.detailModel = model
|
self.detailDataArr.removeAll()
|
||||||
|
self.detailDataArr.append(model)
|
||||||
|
|
||||||
|
|
||||||
self.reloadData { [weak self] in
|
self.reloadData { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let indexPath = indexPath, indexPath.row < (model.episodeList?.count ?? 0) {
|
if let indexPath = indexPath, indexPath.row < (model.episodeList?.count ?? 0) {
|
||||||
self.scrollToItem(indexPath: indexPath, animated: false)
|
self.scrollToItem(indexPath: indexPath, animated: false)
|
||||||
|
|
||||||
} else if let videoInfo = self.detailModel?.video_info {
|
} else if let videoInfo = model.video_info {
|
||||||
var row: Int?
|
var row: Int?
|
||||||
self.detailModel?.episodeList?.enumerated().forEach({
|
model.episodeList?.enumerated().forEach({
|
||||||
if $1.id == videoInfo.id {
|
if $1.id == videoInfo.id {
|
||||||
row = $0
|
row = $0
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,14 @@ import AVKit
|
|||||||
@objc optional func sp_playerViewControllerShouldLoadMoreData(playerViewController: SPPlayerListViewController) -> Bool
|
@objc optional func sp_playerViewControllerShouldLoadMoreData(playerViewController: SPPlayerListViewController) -> Bool
|
||||||
///加载更多数据
|
///加载更多数据
|
||||||
@objc optional func sp_playerViewControllerLoadMoreData(playerViewController: SPPlayerListViewController)
|
@objc optional func sp_playerViewControllerLoadMoreData(playerViewController: SPPlayerListViewController)
|
||||||
///向上加载更多数据
|
|
||||||
@objc optional func sp_playerViewControllerLoadUpMoreData(playerViewController: SPPlayerListViewController)
|
|
||||||
|
|
||||||
///当前展示的发生变化
|
///当前展示的发生变化
|
||||||
@objc optional func sp_playerListViewController(_ viewController: SPPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath)
|
@objc optional func sp_playerListViewController(_ viewController: SPPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath)
|
||||||
|
|
||||||
// @objc optional func sp_playerListViewController(_ viewController: SPPlayerListViewController, didScrollFromIndex fromIndex: Int, toIndex: Int)
|
///即将自动滑至下一级
|
||||||
///新页面展示完成
|
@objc optional func sp_shouldAutoScrollNextEpisode(_ viewController: SPPlayerListViewController) -> Bool
|
||||||
// @objc optional func yd_playerViewController(playerListViewController: BCListPlayerViewController, didShowPlayerPage playerViewController: YDBasePlayerViewController)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc protocol SPPlayerListViewControllerDataSource {
|
@objc protocol SPPlayerListViewControllerDataSource {
|
||||||
@ -35,7 +34,7 @@ import AVKit
|
|||||||
|
|
||||||
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int, oldNumber: Int) -> Int
|
func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, numberOfItemsInSection section: Int, oldNumber: Int) -> Int
|
||||||
|
|
||||||
|
@objc optional func sp_numberOfSections(in viewController: SPPlayerListViewController) -> Int
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +58,6 @@ class SPPlayerListViewController: SPViewController {
|
|||||||
///自动下一级
|
///自动下一级
|
||||||
var autoNextEpisode = false
|
var autoNextEpisode = false
|
||||||
|
|
||||||
///是否为首次播放
|
|
||||||
private(set) var isFirstPlay = true
|
|
||||||
|
|
||||||
private(set) var viewModel = SPPlayerListViewModel()
|
private(set) var viewModel = SPPlayerListViewModel()
|
||||||
|
|
||||||
|
|
||||||
@ -173,20 +169,10 @@ class SPPlayerListViewController: SPViewController {
|
|||||||
|
|
||||||
self.viewModel.isPlaying = true
|
self.viewModel.isPlaying = true
|
||||||
|
|
||||||
if getDataCount() - self.viewModel.currentIndexPath.row <= 2 {
|
if (self.collectionView.contentSize.height - self.collectionView.contentOffset.y) / self.contentSize.height <= 3 {
|
||||||
self.loadMoreData()
|
self.loadMoreData()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFirstPlay {
|
|
||||||
isFirstPlay = false
|
|
||||||
let offset = self.collectionView.contentOffset.y + 0.2
|
|
||||||
self.collectionView.setContentOffset(CGPoint(x: 0, y: offset), animated: false)
|
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
let offset = self.collectionView.contentOffset.y
|
|
||||||
self.collectionView.setContentOffset(CGPoint(x: 0, y: floor(offset)), animated: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pause() {
|
func pause() {
|
||||||
@ -210,7 +196,8 @@ class SPPlayerListViewController: SPViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getDataCount() -> Int {
|
func getDataCount() -> Int {
|
||||||
return self.collectionView(self.collectionView, numberOfItemsInSection: 0)
|
return Int(self.collectionView.contentSize.height / self.contentSize.height)
|
||||||
|
// return self.collectionView(self.collectionView, numberOfItemsInSection: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrollToItem(indexPath: IndexPath, animated: Bool = true, completer: (() -> Void)? = nil) {
|
func scrollToItem(indexPath: IndexPath, animated: Bool = true, completer: (() -> Void)? = nil) {
|
||||||
@ -232,10 +219,19 @@ class SPPlayerListViewController: SPViewController {
|
|||||||
|
|
||||||
///当前播放完成 子类可重写
|
///当前播放完成 子类可重写
|
||||||
func currentPlayFinish() {
|
func currentPlayFinish() {
|
||||||
if self.autoNextEpisode {
|
|
||||||
scrollToNextEpisode()
|
|
||||||
}
|
|
||||||
self.viewModel.currentPlayer?.videoInfo?.play_seconds = 0
|
self.viewModel.currentPlayer?.videoInfo?.play_seconds = 0
|
||||||
|
|
||||||
|
var autoNextEpisode = self.autoNextEpisode
|
||||||
|
|
||||||
|
if let result = self.delegate?.sp_shouldAutoScrollNextEpisode?(self) {
|
||||||
|
autoNextEpisode = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if autoNextEpisode {
|
||||||
|
scrollToNextEpisode()
|
||||||
|
} else {
|
||||||
|
viewModel.currentPlayer?.replay()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///当前播放进度变更 子类可重写
|
///当前播放进度变更 子类可重写
|
||||||
@ -356,6 +352,10 @@ extension SPPlayerListViewController: UICollectionViewDelegate, UICollectionView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||||
|
return self.dataSource?.sp_numberOfSections?(in: self) ?? 1
|
||||||
|
}
|
||||||
|
|
||||||
//滑动停止
|
//滑动停止
|
||||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
scrollDidEnd(scrollView)
|
scrollDidEnd(scrollView)
|
||||||
@ -415,13 +415,6 @@ extension SPPlayerListViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadUpMoreData() {
|
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
|
||||||
// guard let self = self else { return }
|
|
||||||
// }
|
|
||||||
self.delegate?.sp_playerViewControllerLoadUpMoreData?(playerViewController: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func didChangeIndexPathForVisible() {
|
private func didChangeIndexPathForVisible() {
|
||||||
self.delegate?.sp_playerListViewController?(self, didChangeIndexPathForVisible: self.viewModel.currentIndexPath)
|
self.delegate?.sp_playerListViewController?(self, didChangeIndexPathForVisible: self.viewModel.currentIndexPath)
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ class SPPlayBuyView: HWPanModalContentView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var payTemplateRequest: SPPayTemplateRequest?
|
||||||
|
|
||||||
//MARK: UI属性
|
//MARK: UI属性
|
||||||
private lazy var bgView: UIImageView = {
|
private lazy var bgView: UIImageView = {
|
||||||
let view = UIImageView(image: UIImage(named: "buy_bg_image_01"))
|
let view = UIImageView(image: UIImage(named: "buy_bg_image_01"))
|
||||||
@ -246,27 +248,28 @@ extension SPPlayBuyView {
|
|||||||
|
|
||||||
///请求支付模版
|
///请求支付模版
|
||||||
private func requestPayTemplate() {
|
private func requestPayTemplate() {
|
||||||
SPWalletAPI.requestPayTemplate { [weak self] templateModel in
|
|
||||||
|
self.payTemplateRequest = SPPayTemplateRequest()
|
||||||
|
self.payTemplateRequest?.requestProducts { [weak self] model in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.stackView.removeAllArrangedSubview()
|
self.stackView.removeAllArrangedSubview()
|
||||||
|
|
||||||
if let sort = templateModel?.sort, sort.count > 0 {
|
if let sort = model?.sort, sort.count > 0 {
|
||||||
sort.forEach {
|
sort.forEach {
|
||||||
if $0 == .vip {
|
if $0 == .vip {
|
||||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
self.addMemberView(list: model?.list_sub_vip)
|
||||||
} else if $0 == .coin {
|
} else if $0 == .coin {
|
||||||
self.addCoinView(list: templateModel?.list_coins)
|
self.addCoinView(list: model?.list_coins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
self.addMemberView(list: model?.list_sub_vip)
|
||||||
self.addCoinView(list: templateModel?.list_coins)
|
self.addCoinView(list: model?.list_coins)
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
self.panModalSetNeedsLayoutUpdate()
|
self.panModalSetNeedsLayoutUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,14 @@ class SPPlayerDetailRecommandView: HWPanModalContentView {
|
|||||||
var clickCloseButton: (() -> Void)?
|
var clickCloseButton: (() -> Void)?
|
||||||
var clickPlayButton: ((_ model: SPShortModel) -> Void)?
|
var clickPlayButton: ((_ model: SPShortModel) -> Void)?
|
||||||
|
|
||||||
|
var currentVideoInfo: SPVideoInfoModel? {
|
||||||
|
didSet {
|
||||||
|
if SPLoginManager.manager.userInfo?.user_level == .ad {
|
||||||
|
bannerAd.videoInfo = currentVideoInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private var _currentCell: SPPlayerDetailRecommandCell?
|
private var _currentCell: SPPlayerDetailRecommandCell?
|
||||||
private var currentCell: SPPlayerDetailRecommandCell? {
|
private var currentCell: SPPlayerDetailRecommandCell? {
|
||||||
|
@ -9,6 +9,8 @@ import UIKit
|
|||||||
|
|
||||||
class SPStoreViewController: SPViewController {
|
class SPStoreViewController: SPViewController {
|
||||||
|
|
||||||
|
private var payTemplateRequest: SPPayTemplateRequest?
|
||||||
|
|
||||||
//MARK: UI属性
|
//MARK: UI属性
|
||||||
private lazy var scrollView: SPScrollView = {
|
private lazy var scrollView: SPScrollView = {
|
||||||
let scrollView = SPScrollView()
|
let scrollView = SPScrollView()
|
||||||
@ -184,21 +186,23 @@ extension SPStoreViewController {
|
|||||||
|
|
||||||
///请求支付模版
|
///请求支付模版
|
||||||
private func requestPayTemplate() {
|
private func requestPayTemplate() {
|
||||||
SPWalletAPI.requestPayTemplate { [weak self] templateModel in
|
|
||||||
|
self.payTemplateRequest = SPPayTemplateRequest()
|
||||||
|
self.payTemplateRequest?.requestProducts { [weak self] model in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.stackView.removeAllArrangedSubview()
|
self.stackView.removeAllArrangedSubview()
|
||||||
|
|
||||||
if let sort = templateModel?.sort, sort.count > 0 {
|
if let sort = model?.sort, sort.count > 0 {
|
||||||
sort.forEach {
|
sort.forEach {
|
||||||
if $0 == .vip {
|
if $0 == .vip {
|
||||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
self.addMemberView(list: model?.list_sub_vip)
|
||||||
} else if $0 == .coin {
|
} else if $0 == .coin {
|
||||||
self.addCoinView(list: templateModel?.list_coins)
|
self.addCoinView(list: model?.list_coins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.addMemberView(list: templateModel?.list_sub_vip)
|
self.addMemberView(list: model?.list_sub_vip)
|
||||||
self.addCoinView(list: templateModel?.list_coins)
|
self.addCoinView(list: model?.list_coins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ class SPApplovinBannerAd: NSObject, SPBannerAd {
|
|||||||
|
|
||||||
var delegate: (any SPBannerAdDelegate)?
|
var delegate: (any SPBannerAdDelegate)?
|
||||||
|
|
||||||
|
///是否加载过
|
||||||
|
private var isLoaded = false
|
||||||
|
|
||||||
private(set) lazy var _adView: MAAdView = {
|
private(set) lazy var _adView: MAAdView = {
|
||||||
let view = MAAdView(adUnitIdentifier: adUnitID)
|
let view = MAAdView(adUnitIdentifier: adUnitID)
|
||||||
view.frame = .init(x: 0, y: 0, width: size.width, height: size.height)
|
view.frame = .init(x: 0, y: 0, width: size.width, height: size.height)
|
||||||
@ -53,13 +56,19 @@ extension SPApplovinBannerAd: MAAdViewAdDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func didLoad(_ ad: MAAd) {
|
func didLoad(_ ad: MAAd) {
|
||||||
|
if !isLoaded {
|
||||||
|
isLoaded = true
|
||||||
self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
|
self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
|
func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
|
||||||
|
if !isLoaded {
|
||||||
|
isLoaded = true
|
||||||
let nsError = NSError(domain: error.message, code: error.code.rawValue)
|
let nsError = NSError(domain: error.message, code: error.code.rawValue)
|
||||||
self.delegate?.bannerAd?(bannerAd: self, didLoadFail: nsError)
|
self.delegate?.bannerAd?(bannerAd: self, didLoadFail: nsError)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func didDisplay(_ ad: MAAd) {
|
func didDisplay(_ ad: MAAd) {
|
||||||
self.delegate?.bannerAdDidShow?(bannerAd: self)
|
self.delegate?.bannerAdDidShow?(bannerAd: self)
|
||||||
|
@ -57,6 +57,8 @@ class SPBannerAdManager: NSObject {
|
|||||||
|
|
||||||
weak var delegate: SPBannerAdManagerDelegate?
|
weak var delegate: SPBannerAdManagerDelegate?
|
||||||
|
|
||||||
|
var videoInfo: SPVideoInfoModel?
|
||||||
|
|
||||||
private lazy var bannerAd: SPBannerAd = {
|
private lazy var bannerAd: SPBannerAd = {
|
||||||
let ad = SPAdmobBannerAd()
|
let ad = SPAdmobBannerAd()
|
||||||
ad.delegate = self
|
ad.delegate = self
|
||||||
@ -122,6 +124,8 @@ extension SPBannerAdManager {
|
|||||||
model.ad_platform_key = SPAdPlatformKey(rawValue: bannerAd.adPlatformKey)
|
model.ad_platform_key = SPAdPlatformKey(rawValue: bannerAd.adPlatformKey)
|
||||||
model.error_msg = errorMsg
|
model.error_msg = errorMsg
|
||||||
model.scene = .banner
|
model.scene = .banner
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ class SPHUD: NSObject {
|
|||||||
SVProgressHUD.setDefaultMaskType(.clear)
|
SVProgressHUD.setDefaultMaskType(.clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func show(status: String? = nil) {
|
static func show(status: String? = nil, containerView: UIView? = nil) {
|
||||||
|
SVProgressHUD.setContainerView(containerView)
|
||||||
SVProgressHUD.setDefaultMaskType(.clear)
|
SVProgressHUD.setDefaultMaskType(.clear)
|
||||||
// SVProgressHUD.show()
|
|
||||||
SVProgressHUD.show(withStatus: status)
|
SVProgressHUD.show(withStatus: status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
class SPIAPManager: NSObject {
|
class SPIAPManager: NSObject {
|
||||||
typealias CompletionHandler = ((_ finish: Bool) -> Void)
|
typealias CompletionHandler = ((_ finish: Bool) -> Void)
|
||||||
@ -18,6 +19,8 @@ class SPIAPManager: NSObject {
|
|||||||
///成功回调
|
///成功回调
|
||||||
private var completionHandler: CompletionHandler?
|
private var completionHandler: CompletionHandler?
|
||||||
|
|
||||||
|
private var productListHandler: ((_ products: [SKProduct]) -> Void)?
|
||||||
|
|
||||||
private lazy var iapManager: JXIAPManager = {
|
private lazy var iapManager: JXIAPManager = {
|
||||||
let manager = JXIAPManager()
|
let manager = JXIAPManager()
|
||||||
manager.delegate = self
|
manager.delegate = self
|
||||||
@ -119,6 +122,17 @@ class SPIAPManager: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requestProductList(productIdArr: [String], completer: ((_ products: [SKProduct]) -> Void)?) {
|
||||||
|
self.productListHandler = completer
|
||||||
|
self.iapManager.requestProductList(productIdArr: productIdArr)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func getProductId(templateId: String?) -> String? {
|
||||||
|
guard let templateId = templateId else { return nil }
|
||||||
|
return SPIAPManager.IAPPrefix + templateId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: -------------- JXIAPManagerDelegate --------------
|
//MARK: -------------- JXIAPManagerDelegate --------------
|
||||||
@ -182,6 +196,13 @@ extension SPIAPManager: JXIAPManagerDelegate {
|
|||||||
self.completionHandler?(false)
|
self.completionHandler?(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func jx_iapPayGotProducts(products: [SKProduct]) {
|
||||||
|
|
||||||
|
self.productListHandler?(products)
|
||||||
|
|
||||||
|
self.productListHandler = nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SPIAPManager {
|
extension SPIAPManager {
|
||||||
|
101
ThimraTV/Libs/SPIAPManager/SPPayTemplateRequest.swift
Normal file
101
ThimraTV/Libs/SPIAPManager/SPPayTemplateRequest.swift
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
//
|
||||||
|
// SPPayTemplateRequest.swift
|
||||||
|
// ThimraTV
|
||||||
|
//
|
||||||
|
// Created by 长沙佳儿 on 2025/7/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import StoreKit
|
||||||
|
|
||||||
|
class SPPayTemplateRequest: NSObject {
|
||||||
|
|
||||||
|
private var oldTemplateModel: SPPayTemplateModel?
|
||||||
|
|
||||||
|
private var completerBlock: ((_ model: SPPayTemplateModel?) -> Void)?
|
||||||
|
|
||||||
|
private var isLoding = false
|
||||||
|
private var isToast = false
|
||||||
|
|
||||||
|
|
||||||
|
func requestProducts(isLoding: Bool = false, isToast: Bool = true, completer: ((_ model: SPPayTemplateModel?) -> Void)?) {
|
||||||
|
self.completerBlock = completer
|
||||||
|
self.isLoding = isLoding
|
||||||
|
self.isToast = isToast
|
||||||
|
|
||||||
|
if isLoding {
|
||||||
|
SPHUD.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
SPWalletAPI.requestPayTemplate(isToast: isToast) { [weak self] model in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let model = model else {
|
||||||
|
if isLoding {
|
||||||
|
SPHUD.dismiss()
|
||||||
|
}
|
||||||
|
self.completerBlock?(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.oldTemplateModel = model
|
||||||
|
|
||||||
|
var productIdArr: [String] = []
|
||||||
|
model.list_sub_vip?.forEach { item in
|
||||||
|
productIdArr.append(SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
|
||||||
|
}
|
||||||
|
model.list_coins?.forEach { item in
|
||||||
|
productIdArr.append(SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
let set = Set(productIdArr)
|
||||||
|
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||||
|
productsRequest.delegate = self
|
||||||
|
productsRequest.start()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- SKProductsRequestDelegate --------------
|
||||||
|
extension SPPayTemplateRequest: SKProductsRequestDelegate {
|
||||||
|
|
||||||
|
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||||
|
if isLoding {
|
||||||
|
SPHUD.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let templateModel = self.oldTemplateModel else { return }
|
||||||
|
let products = response.products
|
||||||
|
|
||||||
|
var newCoinList: [SPPayTemplateItem] = []
|
||||||
|
var newVipList: [SPPayTemplateItem] = []
|
||||||
|
|
||||||
|
templateModel.list_coins?.forEach { item in
|
||||||
|
let productId = SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
|
||||||
|
for product in products {
|
||||||
|
if productId == product.productIdentifier {
|
||||||
|
item.price = product.price.stringValue
|
||||||
|
item.currency = product.priceLocale.currencySymbol
|
||||||
|
newCoinList.append(item)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templateModel.list_sub_vip?.forEach { item in
|
||||||
|
let productId = SPIAPManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
|
||||||
|
for product in products {
|
||||||
|
if productId == product.productIdentifier {
|
||||||
|
item.price = product.price.stringValue
|
||||||
|
item.currency = product.priceLocale.currencySymbol
|
||||||
|
newVipList.append(item)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templateModel.list_coins = newCoinList
|
||||||
|
templateModel.list_sub_vip = newVipList
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.completerBlock?(templateModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import StoreKit
|
|||||||
|
|
||||||
@objc protocol JXIAPManagerDelegate {
|
@objc protocol JXIAPManagerDelegate {
|
||||||
/// 获取到可购买商品列表
|
/// 获取到可购买商品列表
|
||||||
@objc optional func jx_iapPayGotProducts(productIds: [String])
|
@objc optional func jx_iapPayGotProducts(products: [SKProduct])
|
||||||
/// 购买成功
|
/// 购买成功
|
||||||
@objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String?)
|
@objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String?)
|
||||||
/// 购买失败
|
/// 购买失败
|
||||||
@ -34,19 +34,29 @@ import StoreKit
|
|||||||
case cancelled
|
case cancelled
|
||||||
///没有商品
|
///没有商品
|
||||||
case noProduct
|
case noProduct
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class JXIAPManager: NSObject {
|
class JXIAPManager: NSObject {
|
||||||
|
|
||||||
|
enum OperationType {
|
||||||
|
case idle
|
||||||
|
case buy
|
||||||
|
case request
|
||||||
|
}
|
||||||
|
|
||||||
static let manager: JXIAPManager = JXIAPManager()
|
static let manager: JXIAPManager = JXIAPManager()
|
||||||
|
|
||||||
weak var delegate: JXIAPManagerDelegate?
|
weak var delegate: JXIAPManagerDelegate?
|
||||||
|
|
||||||
|
///当前操作状态
|
||||||
|
private var operationType = OperationType.idle
|
||||||
|
|
||||||
private var payment: SKPayment?
|
private var payment: SKPayment?
|
||||||
|
|
||||||
private var product: SKProduct?
|
private var product: SKProduct?
|
||||||
private var productId: String?
|
private var productId: String?
|
||||||
|
|
||||||
|
private var discount: SKPaymentDiscount?
|
||||||
private var orderId: String?
|
private var orderId: String?
|
||||||
private var applicationUsername: String? {
|
private var applicationUsername: String? {
|
||||||
get {
|
get {
|
||||||
@ -80,10 +90,47 @@ class JXIAPManager: NSObject {
|
|||||||
SKPaymentQueue.default().add(self)
|
SKPaymentQueue.default().add(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(productId: String, orderId: String) {
|
func showCodeRedemption() {
|
||||||
|
SKPaymentQueue.default().presentCodeRedemptionSheet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestProductList(productIdArr: [String]) {
|
||||||
|
// guard self.operationType == .idle else { return }
|
||||||
|
self.operationType = .request
|
||||||
|
|
||||||
|
let set = Set(productIdArr)
|
||||||
|
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||||
|
productsRequest.delegate = self
|
||||||
|
productsRequest.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ///发起购买
|
||||||
|
// func buyProduct(product: SKProduct, orderId: String, discount: SKPaymentDiscount? = nil) {
|
||||||
|
// guard self.operationType == .idle else { return }
|
||||||
|
// self.operationType = .buy
|
||||||
|
//
|
||||||
|
// self.product = product
|
||||||
|
// self.orderId = orderId
|
||||||
|
//
|
||||||
|
// // 要购买商品,开个小票
|
||||||
|
// let payment = SKMutablePayment(product: product)
|
||||||
|
// payment.applicationUsername = applicationUsername
|
||||||
|
// if let discount = discount {
|
||||||
|
// payment.paymentDiscount = discount
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 去收银台排队,准备购买
|
||||||
|
// SKPaymentQueue.default().add(payment)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func start(productId: String, orderId: String, discount: SKPaymentDiscount? = nil) {
|
||||||
|
guard self.operationType == .idle else { return }
|
||||||
|
self.operationType = .buy
|
||||||
|
|
||||||
self.product = nil
|
self.product = nil
|
||||||
self.productId = productId
|
self.productId = productId
|
||||||
self.orderId = orderId
|
self.orderId = orderId
|
||||||
|
self.discount = discount
|
||||||
|
|
||||||
let set = Set([productId])
|
let set = Set([productId])
|
||||||
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||||
@ -98,9 +145,13 @@ class JXIAPManager: NSObject {
|
|||||||
// 要购买商品,开个小票
|
// 要购买商品,开个小票
|
||||||
let payment = SKMutablePayment(product: product)
|
let payment = SKMutablePayment(product: product)
|
||||||
payment.applicationUsername = applicationUsername
|
payment.applicationUsername = applicationUsername
|
||||||
spLog(message: payment.applicationUsername)
|
if let discount = self.discount {
|
||||||
|
payment.paymentDiscount = discount
|
||||||
|
self.discount = nil
|
||||||
|
}
|
||||||
|
|
||||||
self.payment = payment
|
self.payment = payment
|
||||||
|
|
||||||
// 去收银台排队,准备购买
|
// 去收银台排队,准备购买
|
||||||
SKPaymentQueue.default().add(payment)
|
SKPaymentQueue.default().add(payment)
|
||||||
}
|
}
|
||||||
@ -110,6 +161,13 @@ class JXIAPManager: NSObject {
|
|||||||
//MARK: -------------- SKProductsRequestDelegate --------------
|
//MARK: -------------- SKProductsRequestDelegate --------------
|
||||||
extension JXIAPManager: SKProductsRequestDelegate {
|
extension JXIAPManager: SKProductsRequestDelegate {
|
||||||
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||||
|
if self.operationType == .request {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.delegate?.jx_iapPayGotProducts?(products: response.products)
|
||||||
|
}
|
||||||
|
self.operationType = .idle
|
||||||
|
} else if self.operationType == .buy {
|
||||||
|
|
||||||
guard let product = response.products.first else {
|
guard let product = response.products.first else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if let productId = self.productId {
|
if let productId = self.productId {
|
||||||
@ -123,6 +181,8 @@ extension JXIAPManager: SKProductsRequestDelegate {
|
|||||||
|
|
||||||
self.buyProduct()
|
self.buyProduct()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: -------------- SKPaymentTransactionObserver --------------
|
//MARK: -------------- SKPaymentTransactionObserver --------------
|
||||||
@ -131,7 +191,6 @@ extension JXIAPManager: SKPaymentTransactionObserver {
|
|||||||
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||||
|
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
spLog(message: "transactionState = \(transaction.transactionState)")
|
|
||||||
switch transaction.transactionState {
|
switch transaction.transactionState {
|
||||||
case .purchased:
|
case .purchased:
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@ -171,33 +230,21 @@ extension JXIAPManager: SKPaymentTransactionObserver {
|
|||||||
|
|
||||||
extension JXIAPManager {
|
extension JXIAPManager {
|
||||||
private func completeTransaction(transaction: SKPaymentTransaction) {
|
private func completeTransaction(transaction: SKPaymentTransaction) {
|
||||||
//自动续费
|
|
||||||
// if let _ = transaction.original, transaction.payment.applicationUsername == nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//重新开通自动续费
|
|
||||||
// if let _ = transaction.original, transaction.payment.applicationUsername != nil {
|
|
||||||
// self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
spLog(message: "transactionDate = \(String(describing: transaction.transactionDate))")
|
|
||||||
spLog(message: "nowDate = \(Date())")
|
|
||||||
spLog(message: "productIdentifier = \(transaction.payment.productIdentifier)")
|
|
||||||
|
|
||||||
guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return }
|
|
||||||
self.productId = nil
|
|
||||||
|
|
||||||
guard let receiptURL = Bundle.main.appStoreReceiptURL else { return }
|
guard let receiptURL = Bundle.main.appStoreReceiptURL else { return }
|
||||||
let receiptData = NSData(contentsOf: receiptURL)
|
let receiptData = NSData(contentsOf: receiptURL)
|
||||||
guard let encodeStr = receiptData?.base64EncodedString(options: .endLineWithLineFeed) else { return }
|
guard let encodeStr = receiptData?.base64EncodedString(options: .endLineWithLineFeed) else { return }
|
||||||
guard let transactionIdentifier = transaction.transactionIdentifier else { return }
|
guard let transactionIdentifier = transaction.transactionIdentifier else { return }
|
||||||
|
|
||||||
|
|
||||||
|
guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return }
|
||||||
|
self.operationType = .idle
|
||||||
|
self.productId = nil
|
||||||
self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier)
|
self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func failedTransaction(transaction: SKPaymentTransaction) {
|
private func failedTransaction(transaction: SKPaymentTransaction) {
|
||||||
|
self.operationType = .idle
|
||||||
let error = transaction.error as? SKError
|
let error = transaction.error as? SKError
|
||||||
guard let productId = self.productId else { return }
|
guard let productId = self.productId else { return }
|
||||||
self.productId = nil
|
self.productId = nil
|
||||||
|
2
ThimraTV/Thirdparty/JXUUID/JXUUID.h
vendored
2
ThimraTV/Thirdparty/JXUUID/JXUUID.h
vendored
@ -15,6 +15,6 @@
|
|||||||
/**
|
/**
|
||||||
重新安装app后,会发生变化
|
重新安装app后,会发生变化
|
||||||
*/
|
*/
|
||||||
+ (nonnull NSString *)systemUUID;
|
+ (nonnull NSString *)idfv;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
2
ThimraTV/Thirdparty/JXUUID/JXUUID.m
vendored
2
ThimraTV/Thirdparty/JXUUID/JXUUID.m
vendored
@ -39,7 +39,7 @@ static NSString *const uuidKey = @"com.JXUUID";
|
|||||||
return idfa;
|
return idfa;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (nonnull NSString *)systemUUID
|
+ (nonnull NSString *)idfv
|
||||||
{
|
{
|
||||||
return [UIDevice currentDevice].identifierForVendor.UUIDString;
|
return [UIDevice currentDevice].identifierForVendor.UUIDString;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user