From adbebd23c86f666c9de3aedb336b2d4d745a2b1e Mon Sep 17 00:00:00 2001 From: True Zhuanjia Date: Mon, 9 Feb 2026 22:43:02 +0800 Subject: [PATCH] temporarily disable pencil in-app-purchase for iOS14 and lower OS --- VoidLink/Input/PressureCurve.swift | 26 ++++--- VoidLink/Localization/Localizable.xcstrings | 29 ++++++++ VoidLink/Utility/IAPManager.swift | 59 +++++++-------- .../ViewControllers/SettingsViewController.m | 72 +++++++++++-------- 4 files changed, 116 insertions(+), 70 deletions(-) diff --git a/VoidLink/Input/PressureCurve.swift b/VoidLink/Input/PressureCurve.swift index 7023a715..ee18e5e2 100644 --- a/VoidLink/Input/PressureCurve.swift +++ b/VoidLink/Input/PressureCurve.swift @@ -658,6 +658,15 @@ class PressureCurveViewController: UIViewController { if interruption != .learnMore { resetTapped() } + if interruption == .lowOSVersion { + AlertControllerUtil.showAlert( + in: self, + title: "", + message: SwiftLocalizationHelper.localizedString(forKey:"PencilProPackLowOSVersionTip"), + withCancel: false, + buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "OK"), + countdown: 0) + } } @objc private func pressureRangeTest() { @@ -715,18 +724,13 @@ class PressureCurveViewController: UIViewController { } @objc private func saveTapped() { - if #available(iOS 15.0, *) { - IAPManager.checkPurchaseInfo(.PencilProPack) { info in - if info.valid { - self.persistCurve() - } - else{ - IAPManager.inAppPurchaseAction(viewController: self, product: .PencilProPack) - } + IAPManager.checkPurchaseInfo(.PencilProPack) { info in + if info.valid { + self.persistCurve() + } + else{ + IAPManager.inAppPurchaseAction(viewController: self, product: .PencilProPack) } - } - else { - return } } diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 53f16dab..fc57f8b2 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -5056,6 +5056,35 @@ "PencilProPackDescription" : { "extractionState" : "manual" }, + "PencilProPackLowOSVersionTip" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pencil Pro Pack is coming soon on iOS 12–14." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pencil Pro Pack 即将支持 iOS 12–14。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pencil Pro Pack 即將支援 iOS 12–14。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pencil Pro Pack 即將支援 iOS 12–14。" + } + } + } + }, "PencilProPackURL" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/Utility/IAPManager.swift b/VoidLink/Utility/IAPManager.swift index 404290a2..ed02c8a3 100644 --- a/VoidLink/Utility/IAPManager.swift +++ b/VoidLink/Utility/IAPManager.swift @@ -19,6 +19,7 @@ import StoreKit case unlockNow case restore case learnMore + case lowOSVersion } @objc public class PurchaseInfo: NSObject { @@ -221,21 +222,21 @@ import StoreKit await transaction.finish() NotificationCenter.default.post(name: product.purchaseSucceededNotification(), object: nil) case .unverified(_, let err): - NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow) + NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow, userInfo:["interruption": PurchaseInterruption.unlockNow.rawValue]) await MainActor.run { self.delegate?.iapManagerDidFailWithError(err) } } case .userCancelled: - NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow) + NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow, userInfo:["interruption": PurchaseInterruption.unlockNow.rawValue]) break default: - NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow) + NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow, userInfo:["interruption": PurchaseInterruption.unlockNow.rawValue]) break } } catch { await MainActor.run { - NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow) + NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.unlockNow, userInfo:["interruption": PurchaseInterruption.unlockNow.rawValue]) self.delegate?.iapManagerDidFailWithError(error) } } @@ -301,36 +302,38 @@ import StoreKit } // MARK: Utils - @available(iOS 15.0, *) @objc static public func checkPurchaseInfo( _ product: AddOnProduct, completion: @escaping (PurchaseInfo) -> Void ) { let productID = product.productId() - Task { - var status: PurchaseStatus = .notPurchased - var expiration: Date? = nil - - for await verificationResult in Transaction.currentEntitlements { - if case .verified(let transaction) = verificationResult, - transaction.productID == productID { - - // 有退款 / 撤销 - if transaction.revocationDate != nil { - status = .revoked - } else { - status = .purchased - expiration = transaction.expirationDate + if #available(iOS 15.0, *) { + Task { + var status: PurchaseStatus = .notPurchased + var expiration: Date? = nil + for await verificationResult in Transaction.currentEntitlements { + if case .verified(let transaction) = verificationResult, + transaction.productID == productID { + + // 有退款 / 撤销 + if transaction.revocationDate != nil { + status = .revoked + } else { + status = .purchased + expiration = transaction.expirationDate + } + break } - break + } + let info = PurchaseInfo(status: status, expirationDate: expiration as NSDate?) + + await MainActor.run { + completion(info) } } - - let info = PurchaseInfo(status: status, expirationDate: expiration as NSDate?) - - await MainActor.run { - completion(info) - } + } + else { + NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.lowOSVersion, userInfo:["interruption": PurchaseInterruption.lowOSVersion.rawValue]) } } @@ -365,7 +368,7 @@ import StoreKit // Fallback on earlier versions } } catch { - NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.restore) + NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.restore, userInfo:["interruption": PurchaseInterruption.restore.rawValue]) print("Restore failed: \(error)") } } @@ -375,7 +378,7 @@ import StoreKit } let learnMoreAction = UIAlertAction(title: SwiftLocalizationHelper.localizedString(forKey: "Learn More"), style: .default) { _ in - NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.learnMore) + NotificationCenter.default.post(name: product.purchaseAbortedNotification(), object: PurchaseInterruption.learnMore, userInfo:["interruption": PurchaseInterruption.learnMore.rawValue]) if let url = URL(string: product.productURL()) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 6d6434bf..a234341f 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -475,7 +475,7 @@ BOOL isCustomResolution(int resolutionSelected) { name:@"OscLayoutCloseNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(pencilProPurchaseAborted) + selector:@selector(pencilProPurchaseAborted:) name:@"PencilProPurchaseAbortedNotification" object:nil]; @@ -3381,23 +3381,39 @@ BOOL isCustomResolution(int resolutionSelected) { if(settingsViewJustExpanded) return; - if (@available(iOS 15.0, *)) { - if(sender.selectedSegmentIndex != ManualTick) return; - [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { - if(!info.valid){ - [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; - } - }]; - } + if(sender.selectedSegmentIndex != ManualTick) return; + [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { + if(!info.valid){ + [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; + } + }]; } -- (void)pencilProPurchaseAborted{ +- (void)pencilProPurchaseAborted:(NSNotification *)notification{ dispatch_async(dispatch_get_main_queue(), ^{ self.pencilTickSelector.selectedSegmentIndex = PencilTickDisabled; [self pencilTickModeChanged:self.pencilTickSelector]; + [self.pressureCurveSwitch setOn:false]; [self.doubleTapShortcutSwitch setOn:false]; [self.pencilPausesNativeTouchSwitch setOn:false]; [self.disablePencilSlideGestureSwitch setOn:false]; + + + NSNumber *value = notification.userInfo[@"interruption"]; + if (!value) return; + + PurchaseInterruption interruption = value.intValue; + + if(interruption == PurchaseInterruptionLowOSVersion){ + [AlertControllerUtil showAlertIn:self + title:@"" + message:[LocalizationHelper localizedStringForKey:@"PencilProPackLowOSVersionTip"] + withCancel:NO + buttonTitle:[LocalizationHelper localizedStringForKey:@"OK"] + countdown:0 + action:nil + completion:nil]; + } }); } @@ -3416,37 +3432,31 @@ BOOL isCustomResolution(int resolutionSelected) { - (void)doubleTapShortcutSwitchFlipped:(UISwitch* )sender{ if(sender.isOn && !settingsViewJustLoaded){ - if (@available(iOS 15.0, *)) { - [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { - if(info.valid) [PencilHandler enterDoubleTapShortcutsIn:self]; - else { - [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; - } - }]; - } + [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { + if(info.valid) [PencilHandler enterDoubleTapShortcutsIn:self]; + else { + [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; + } + }]; } } - (void)disablePencilSlideGestureSwitchFlipped:(UISwitch* )sender{ if(sender.isOn && !settingsViewJustLoaded){ - if (@available(iOS 15.0, *)) { - [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { - if(info.valid) nil; - else { - [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; - } - }]; - } + [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { + if(info.valid) nil; + else { + [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; + } + }]; } } - (void)pencilPausesNativeTouchSwitchFlipped:(UISwitch* )sender{ if(sender.isOn && !settingsViewJustLoaded){ - if (@available(iOS 15.0, *)) { - [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { - if(!info.valid) [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; - }]; - } + [IAPManager checkPurchaseInfo:AddOnProductPencilProPack completion:^(PurchaseInfo* info) { + if(!info.valid) [IAPManager inAppPurchaseActionWithViewController:self product:AddOnProductPencilProPack]; + }]; } }