diff --git a/Passepartout/App/iOS/AppDelegate.swift b/Passepartout/App/iOS/AppDelegate.swift index e9fb4920..e36aac87 100644 --- a/Passepartout/App/iOS/AppDelegate.swift +++ b/Passepartout/App/iOS/AppDelegate.swift @@ -162,7 +162,7 @@ extension UISplitViewController { extension AppDelegate { func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { - guard ProductManager.shared.isEligible(forFeature: .siriShortcuts) else { + guard (try? ProductManager.shared.isEligible(forFeature: .siriShortcuts)) ?? false else { return false } guard let interaction = userActivity.interaction else { diff --git a/Passepartout/App/iOS/Global/Macros.swift b/Passepartout/App/iOS/Global/Macros.swift index a84bddf4..9b5fcc4a 100644 --- a/Passepartout/App/iOS/Global/Macros.swift +++ b/Passepartout/App/iOS/Global/Macros.swift @@ -75,6 +75,12 @@ extension UIViewController { present(nav, animated: true, completion: nil) } + + func presentBetaFeatureUnavailable(_ title: String) { + let alert = UIAlertController.asAlert(title, "The requested feature is unavailable in beta.") + alert.addCancelAction("OK") + present(alert, animated: true, completion: nil) + } } func visitURL(_ url: URL) { diff --git a/Passepartout/App/iOS/Global/ProductManager+App.swift b/Passepartout/App/iOS/Global/ProductManager+App.swift index f751095b..364f4ecd 100644 --- a/Passepartout/App/iOS/Global/ProductManager+App.swift +++ b/Passepartout/App/iOS/Global/ProductManager+App.swift @@ -46,7 +46,7 @@ extension ProductManager { // review features and potentially revert them if they were used (Siri is handled in AppDelegate) log.debug("Checking 'Trusted networks'") - if !isEligible(forFeature: .trustedNetworks) { + if !((try? isEligible(forFeature: .trustedNetworks)) ?? false) { // reset trusted networks for ALL profiles (must load first) for key in service.allProfileKeys() { @@ -76,7 +76,7 @@ extension ProductManager { guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else { continue } - if !isEligible(forProvider: metadata) { + if !((try? isEligible(forProvider: metadata)) ?? false) { service.removeProfile(ProfileKey(name)) log.debug("\tRefunded provider: \(name)") anyRefund = true diff --git a/Passepartout/App/iOS/Scenes/Organizer/OrganizerViewController.swift b/Passepartout/App/iOS/Scenes/Organizer/OrganizerViewController.swift index 56a0e735..cb81072e 100644 --- a/Passepartout/App/iOS/Scenes/Organizer/OrganizerViewController.swift +++ b/Passepartout/App/iOS/Scenes/Organizer/OrganizerViewController.swift @@ -241,8 +241,13 @@ class OrganizerViewController: UITableViewController, StrongTableHost { } private func addShortcuts() { - guard ProductManager.shared.isEligible(forFeature: .siriShortcuts) else { - presentPurchaseScreen(forProduct: .siriShortcuts) + do { + guard try ProductManager.shared.isEligible(forFeature: .siriShortcuts) else { + presentPurchaseScreen(forProduct: .siriShortcuts) + return + } + } catch { + presentBetaFeatureUnavailable("Siri") return } perform(segue: StoryboardSegue.Organizer.siriShortcutsSegueIdentifier) diff --git a/Passepartout/App/iOS/Scenes/Organizer/WizardProviderViewController.swift b/Passepartout/App/iOS/Scenes/Organizer/WizardProviderViewController.swift index 907d7206..d48d9b8f 100644 --- a/Passepartout/App/iOS/Scenes/Organizer/WizardProviderViewController.swift +++ b/Passepartout/App/iOS/Scenes/Organizer/WizardProviderViewController.swift @@ -70,11 +70,16 @@ class WizardProviderViewController: UITableViewController, StrongTableHost { private func tryNext(withMetadata metadata: Infrastructure.Metadata, purchaseIfNecessary: Bool) { selectedMetadata = metadata - guard ProductManager.shared.isEligible(forProvider: metadata) else { - guard purchaseIfNecessary else { + do { + guard try ProductManager.shared.isEligible(forProvider: metadata) else { + guard purchaseIfNecessary else { + return + } + presentPurchaseScreen(forProduct: metadata.product, delegate: self) return } - presentPurchaseScreen(forProduct: metadata.product, delegate: self) + } catch { + presentBetaFeatureUnavailable("Providers") return } diff --git a/Passepartout/App/iOS/Scenes/ServiceViewController.swift b/Passepartout/App/iOS/Scenes/ServiceViewController.swift index dafa9b2a..1b76562d 100644 --- a/Passepartout/App/iOS/Scenes/ServiceViewController.swift +++ b/Passepartout/App/iOS/Scenes/ServiceViewController.swift @@ -377,11 +377,19 @@ class ServiceViewController: UIViewController, StrongTableHost { } private func trustMobileNetwork(cell: ToggleTableViewCell) { - guard ProductManager.shared.isEligible(forFeature: .trustedNetworks) else { + do { + guard try ProductManager.shared.isEligible(forFeature: .trustedNetworks) else { + delay { + cell.setOn(false, animated: true) + } + presentPurchaseScreen(forProduct: .trustedNetworks) + return + } + } catch { delay { cell.setOn(false, animated: true) } - presentPurchaseScreen(forProduct: .trustedNetworks) + presentBetaFeatureUnavailable("Trusted networks") return } @@ -394,8 +402,13 @@ class ServiceViewController: UIViewController, StrongTableHost { } private func trustCurrentWiFi() { - guard ProductManager.shared.isEligible(forFeature: .trustedNetworks) else { - presentPurchaseScreen(forProduct: .trustedNetworks) + do { + guard try ProductManager.shared.isEligible(forFeature: .trustedNetworks) else { + presentPurchaseScreen(forProduct: .trustedNetworks) + return + } + } catch { + presentBetaFeatureUnavailable("Trusted networks") return } @@ -447,14 +460,22 @@ class ServiceViewController: UIViewController, StrongTableHost { } private func toggleTrustWiFi(cell: ToggleTableViewCell, at row: Int) { - guard ProductManager.shared.isEligible(forFeature: .trustedNetworks) else { + do { + guard try ProductManager.shared.isEligible(forFeature: .trustedNetworks) else { + delay { + cell.setOn(false, animated: true) + } + presentPurchaseScreen(forProduct: .trustedNetworks) + return + } + } catch { delay { cell.setOn(false, animated: true) } - presentPurchaseScreen(forProduct: .trustedNetworks) + presentBetaFeatureUnavailable("Trusted networks") return } - + if cell.isOn { trustedNetworks.enableWifi(at: row) } else { diff --git a/Passepartout/Core/Sources/Model/ProductManager.swift b/Passepartout/Core/Sources/Model/ProductManager.swift index 8c622d7a..47cefd6a 100644 --- a/Passepartout/Core/Sources/Model/ProductManager.swift +++ b/Passepartout/Core/Sources/Model/ProductManager.swift @@ -32,6 +32,10 @@ import TunnelKit private let log = SwiftyBeaver.self +public enum ProductError: Error { + case beta +} + public class ProductManager: NSObject { public struct Configuration { public let isBetaFullVersion: Bool @@ -155,16 +159,16 @@ public class ProductManager: NSObject { return purchasedFeatures.contains(.fullVersion) } - public func isEligible(forFeature feature: Product) -> Bool { + public func isEligible(forFeature feature: Product) throws -> Bool { guard !isBeta else { - return false + throw ProductError.beta } return isFullVersion() || purchasedFeatures.contains(feature) } - public func isEligible(forProvider metadata: Infrastructure.Metadata) -> Bool { + public func isEligible(forProvider metadata: Infrastructure.Metadata) throws -> Bool { guard !isBeta else { - return false + throw ProductError.beta } return isFullVersion() || purchasedFeatures.contains(metadata.product) }