Share ProductManager in Core with conditionals
In order to share purchase review logic. Refactor verification of product eligibility
This commit is contained in:
parent
21e9f5c8cc
commit
7d2ece0256
|
@ -25,7 +25,6 @@
|
|||
0E294AA225AE2B0B00CB4908 /* Descriptible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E294A8225AE29D100CB4908 /* Descriptible.swift */; };
|
||||
0E2AC24522EC3AC10037B4B0 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0E2AC24422EC3AC10037B4B0 /* Settings.bundle */; };
|
||||
0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2B493F20FCFF990094784C /* Theme+Titles.swift */; };
|
||||
0E2EB063236D8E1E0079DB53 /* AppConstants+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2EB062236D8E1E0079DB53 /* AppConstants+App.swift */; };
|
||||
0E3152AD223F9EF500F61841 /* PassepartoutCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E31529D223F9EF500F61841 /* PassepartoutCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0E3152B0223F9EF500F61841 /* PassepartoutCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* PassepartoutCore.framework */; };
|
||||
0E3152B1223F9EF500F61841 /* PassepartoutCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0E31529B223F9EF400F61841 /* PassepartoutCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
|
@ -104,12 +103,10 @@
|
|||
0E52037D259F593B00CBAB56 /* SwiftGen+Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F9C259F41690022DFB8 /* SwiftGen+Strings.swift */; };
|
||||
0E52037E259F593B00CBAB56 /* SwiftGen+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569FA5259F41690022DFB8 /* SwiftGen+Segues.swift */; };
|
||||
0E52037F259F593B00CBAB56 /* Theme+Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F9A259F41690022DFB8 /* Theme+Views.swift */; };
|
||||
0E520380259F593B00CBAB56 /* AppConstants+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569FA3259F41690022DFB8 /* AppConstants+App.swift */; };
|
||||
0E520381259F593B00CBAB56 /* NSTextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F94259F41690022DFB8 /* NSTextView+Search.swift */; };
|
||||
0E520382259F593B00CBAB56 /* SwiftGen+Scenes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F9D259F41690022DFB8 /* SwiftGen+Scenes.swift */; };
|
||||
0E520383259F593B00CBAB56 /* SwiftGen+Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F98259F41690022DFB8 /* SwiftGen+Assets.swift */; };
|
||||
0E520385259F593B00CBAB56 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = 0E569FA4259F41690022DFB8 /* Credits.html */; };
|
||||
0E520386259F593B00CBAB56 /* ProductManager+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F9E259F41690022DFB8 /* ProductManager+App.swift */; };
|
||||
0E520387259F593B00CBAB56 /* TextInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F97259F41690022DFB8 /* TextInputViewController.swift */; };
|
||||
0E520388259F593B00CBAB56 /* HostImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E569F93259F41690022DFB8 /* HostImporter.swift */; };
|
||||
0E52038F259F593F00CBAB56 /* App.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E569F8D259F41690022DFB8 /* App.strings */; };
|
||||
|
@ -207,7 +204,6 @@
|
|||
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */; };
|
||||
0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; };
|
||||
0EBE3A79213C4E5500BFA2F5 /* OrganizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */; };
|
||||
0ECA7E2225967BB90095F369 /* ProductManager+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA7E2125967BB90095F369 /* ProductManager+App.swift */; };
|
||||
0ECC60DE2256B68A0020BEAC /* SwiftGen+Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */; };
|
||||
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB109224FECEA00E9E551 /* DataUnit.swift */; };
|
||||
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEE44F20E1182E00A6BB43 /* Theme+Cells.swift */; };
|
||||
|
@ -376,7 +372,6 @@
|
|||
0E2C54C4230056EF00F59453 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Core.strings"; sourceTree = "<group>"; };
|
||||
0E2C54C52300570200F59453 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/App.strings"; sourceTree = "<group>"; };
|
||||
0E2D11B9217DBEDE0096822C /* ConnectionService+Configurations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConnectionService+Configurations.swift"; sourceTree = "<group>"; };
|
||||
0E2EB062236D8E1E0079DB53 /* AppConstants+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppConstants+App.swift"; sourceTree = "<group>"; };
|
||||
0E31529B223F9EF400F61841 /* PassepartoutCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PassepartoutCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E31529D223F9EF500F61841 /* PassepartoutCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PassepartoutCore.h; sourceTree = "<group>"; };
|
||||
0E31529E223F9EF500F61841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -474,9 +469,7 @@
|
|||
0E569F9A259F41690022DFB8 /* Theme+Views.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Theme+Views.swift"; sourceTree = "<group>"; };
|
||||
0E569F9C259F41690022DFB8 /* SwiftGen+Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Strings.swift"; sourceTree = "<group>"; };
|
||||
0E569F9D259F41690022DFB8 /* SwiftGen+Scenes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Scenes.swift"; sourceTree = "<group>"; };
|
||||
0E569F9E259F41690022DFB8 /* ProductManager+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ProductManager+App.swift"; sourceTree = "<group>"; };
|
||||
0E569FA1259F41690022DFB8 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
0E569FA3259F41690022DFB8 /* AppConstants+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppConstants+App.swift"; sourceTree = "<group>"; };
|
||||
0E569FA4259F41690022DFB8 /* Credits.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
|
||||
0E569FA5259F41690022DFB8 /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = "<group>"; };
|
||||
0E569FA6259F41690022DFB8 /* Macros.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Macros.swift; sourceTree = "<group>"; };
|
||||
|
@ -544,7 +537,6 @@
|
|||
0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderConnectionProfile.swift; sourceTree = "<group>"; };
|
||||
0EBE8D2E25C076F900798607 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/App.strings; sourceTree = "<group>"; };
|
||||
0EC7F20420E24308004EA58E /* DebugLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugLog.swift; sourceTree = "<group>"; };
|
||||
0ECA7E2125967BB90095F369 /* ProductManager+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ProductManager+App.swift"; sourceTree = "<group>"; };
|
||||
0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Assets.swift"; sourceTree = "<group>"; };
|
||||
0ECEB105224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
0ECEB106224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Organizer.storyboard; sourceTree = "<group>"; };
|
||||
|
@ -871,12 +863,10 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0E569FA4259F41690022DFB8 /* Credits.html */,
|
||||
0E569FA3259F41690022DFB8 /* AppConstants+App.swift */,
|
||||
0E569F93259F41690022DFB8 /* HostImporter.swift */,
|
||||
0E569F8F259F41690022DFB8 /* IssueReporter.swift */,
|
||||
0E569FA6259F41690022DFB8 /* Macros.swift */,
|
||||
0E569F94259F41690022DFB8 /* NSTextView+Search.swift */,
|
||||
0E569F9E259F41690022DFB8 /* ProductManager+App.swift */,
|
||||
0E569F98259F41690022DFB8 /* SwiftGen+Assets.swift */,
|
||||
0E569F9D259F41690022DFB8 /* SwiftGen+Scenes.swift */,
|
||||
0E569FA5259F41690022DFB8 /* SwiftGen+Segues.swift */,
|
||||
|
@ -1062,11 +1052,9 @@
|
|||
0EDE8DEC20C93E3B004C739C /* Global */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0E2EB062236D8E1E0079DB53 /* AppConstants+App.swift */,
|
||||
0E3262D8235EE8DA00B5E470 /* HostImporter.swift */,
|
||||
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
||||
0E4FD7F020D58618002221FF /* Macros.swift */,
|
||||
0ECA7E2125967BB90095F369 /* ProductManager+App.swift */,
|
||||
0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */,
|
||||
0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */,
|
||||
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */,
|
||||
|
@ -1909,12 +1897,10 @@
|
|||
0E520346259F58FE00CBAB56 /* TrustedNetworksViewController.swift in Sources */,
|
||||
0E520338259F58F500CBAB56 /* ProviderServiceView.swift in Sources */,
|
||||
0E520347259F58FE00CBAB56 /* ProxyViewController.swift in Sources */,
|
||||
0E520380259F593B00CBAB56 /* AppConstants+App.swift in Sources */,
|
||||
0E52037B259F593B00CBAB56 /* IssueReporter.swift in Sources */,
|
||||
0E520335259F58F500CBAB56 /* HostServiceView.swift in Sources */,
|
||||
0E520333259F58F500CBAB56 /* OrganizerProfileTableView.swift in Sources */,
|
||||
0E52037F259F593B00CBAB56 /* Theme+Views.swift in Sources */,
|
||||
0E520386259F593B00CBAB56 /* ProductManager+App.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -2002,7 +1988,6 @@
|
|||
0ED31C2C20CF2D6F0027975F /* ProviderPoolViewController.swift in Sources */,
|
||||
0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */,
|
||||
0E05C5D520D1645F006EE732 /* SettingTableViewCell.swift in Sources */,
|
||||
0E2EB063236D8E1E0079DB53 /* AppConstants+App.swift in Sources */,
|
||||
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */,
|
||||
0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */,
|
||||
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */,
|
||||
|
@ -2013,7 +1998,6 @@
|
|||
0E57F63E20C83FC5008323CF /* ServiceViewController.swift in Sources */,
|
||||
0E36D24D2240234B006AF062 /* ShortcutsAddViewController.swift in Sources */,
|
||||
0E57F63C20C83FC5008323CF /* AppDelegate.swift in Sources */,
|
||||
0ECA7E2225967BB90095F369 /* ProductManager+App.swift in Sources */,
|
||||
0ED31C2920CF2A340027975F /* AccountViewController.swift in Sources */,
|
||||
0E158ADA20E11B0B00C85A82 /* EndpointViewController.swift in Sources */,
|
||||
0E1D72B4213C118500BA1586 /* ConfigurationViewController.swift in Sources */,
|
||||
|
|
|
@ -162,7 +162,9 @@ extension UISplitViewController {
|
|||
|
||||
extension AppDelegate {
|
||||
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||
guard (try? ProductManager.shared.isEligible(forFeature: .siriShortcuts)) ?? false else {
|
||||
do {
|
||||
try ProductManager.shared.verifyEligible(forFeature: .siriShortcuts)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
guard let interaction = userActivity.interaction else {
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
//
|
||||
// AppConstants+App.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 11/2/19.
|
||||
// Copyright (c) 2021 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassepartoutCore
|
||||
|
||||
extension AppConstants {
|
||||
struct Rating {
|
||||
static let eventCount = 3
|
||||
}
|
||||
|
||||
struct InApp {
|
||||
static var isBetaFullVersion: Bool {
|
||||
return ProcessInfo.processInfo.environment["FULL_VERSION"] != nil
|
||||
}
|
||||
|
||||
static let lastFullVersionBuild = 2016
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
//
|
||||
// ProductManager+App.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 4/6/19.
|
||||
// Copyright (c) 2021 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassepartoutCore
|
||||
import TunnelKit
|
||||
import SwiftyBeaver
|
||||
|
||||
private let log = SwiftyBeaver.self
|
||||
|
||||
extension ProductManager {
|
||||
static let shared = ProductManager(
|
||||
Configuration(
|
||||
isBetaFullVersion: AppConstants.InApp.isBetaFullVersion,
|
||||
lastFullVersionBuild: AppConstants.InApp.lastFullVersionBuild
|
||||
)
|
||||
)
|
||||
|
||||
public func reviewPurchases() {
|
||||
let service = TransientStore.shared.service
|
||||
reloadReceipt(andNotify: false)
|
||||
let isFullVersion = (try? isEligible(forFeature: .fullVersion)) ?? false
|
||||
var anyRefund = false
|
||||
|
||||
// review features and potentially revert them if they were used (Siri is handled in AppDelegate)
|
||||
|
||||
log.debug("Checking 'Trusted networks'")
|
||||
if isCancelledPurchase(.fullVersion) || (!isFullVersion && isCancelledPurchase(.trustedNetworks)) {
|
||||
|
||||
// reset trusted networks for ALL profiles (must load first)
|
||||
for key in service.allProfileKeys() {
|
||||
guard let profile = service.profile(withKey: key) else {
|
||||
continue
|
||||
}
|
||||
#if os(iOS)
|
||||
if profile.trustedNetworks.includesMobile || !profile.trustedNetworks.includedWiFis.isEmpty {
|
||||
profile.trustedNetworks.includesMobile = false
|
||||
profile.trustedNetworks.includedWiFis.removeAll()
|
||||
anyRefund = true
|
||||
}
|
||||
#else
|
||||
if !profile.trustedNetworks.includedWiFis.isEmpty {
|
||||
profile.trustedNetworks.includedWiFis.removeAll()
|
||||
anyRefund = true
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if anyRefund {
|
||||
log.debug("\tRefunded")
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Checking providers")
|
||||
for name in service.providerNames() {
|
||||
guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else {
|
||||
continue
|
||||
}
|
||||
if isCancelledPurchase(.fullVersion) || (!isFullVersion && isCancelledPurchase(metadata.product)) {
|
||||
service.removeProfile(ProfileKey(name))
|
||||
log.debug("\tRefunded provider: \(name)")
|
||||
anyRefund = true
|
||||
}
|
||||
}
|
||||
|
||||
guard anyRefund else {
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// save reverts and remove fraud VPN profile
|
||||
TransientStore.shared.serialize(withProfiles: true)
|
||||
VPN.shared.uninstall(completionHandler: nil)
|
||||
|
||||
NotificationCenter.default.post(name: ProductManager.didReviewPurchases, object: nil)
|
||||
}
|
||||
}
|
|
@ -242,13 +242,13 @@ class OrganizerViewController: UITableViewController, StrongTableHost {
|
|||
|
||||
private func addShortcuts() {
|
||||
do {
|
||||
guard try ProductManager.shared.isEligible(forFeature: .siriShortcuts) else {
|
||||
presentPurchaseScreen(forProduct: .siriShortcuts)
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
try ProductManager.shared.verifyEligible(forFeature: .siriShortcuts)
|
||||
} catch ProductError.beta {
|
||||
presentBetaFeatureUnavailable("Siri")
|
||||
return
|
||||
} catch {
|
||||
presentPurchaseScreen(forProduct: .siriShortcuts)
|
||||
return
|
||||
}
|
||||
perform(segue: StoryboardSegue.Organizer.siriShortcutsSegueIdentifier)
|
||||
}
|
||||
|
|
|
@ -71,15 +71,15 @@ class WizardProviderViewController: UITableViewController, StrongTableHost {
|
|||
selectedMetadata = metadata
|
||||
|
||||
do {
|
||||
guard try ProductManager.shared.isEligible(forProvider: metadata) else {
|
||||
guard purchaseIfNecessary else {
|
||||
return
|
||||
}
|
||||
presentPurchaseScreen(forProduct: metadata.product, delegate: self)
|
||||
try ProductManager.shared.verifyEligible(forProvider: metadata)
|
||||
} catch ProductError.beta {
|
||||
presentBetaFeatureUnavailable("Providers")
|
||||
return
|
||||
} catch {
|
||||
guard purchaseIfNecessary else {
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
presentBetaFeatureUnavailable("Providers")
|
||||
presentPurchaseScreen(forProduct: metadata.product, delegate: self)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -378,19 +378,19 @@ class ServiceViewController: UIViewController, StrongTableHost {
|
|||
|
||||
private func trustMobileNetwork(cell: ToggleTableViewCell) {
|
||||
do {
|
||||
guard try ProductManager.shared.isEligible(forFeature: .trustedNetworks) else {
|
||||
delay {
|
||||
cell.setOn(false, animated: true)
|
||||
}
|
||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
|
||||
} catch ProductError.beta {
|
||||
delay {
|
||||
cell.setOn(false, animated: true)
|
||||
}
|
||||
presentBetaFeatureUnavailable("Trusted networks")
|
||||
return
|
||||
} catch {
|
||||
delay {
|
||||
cell.setOn(false, animated: true)
|
||||
}
|
||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||
return
|
||||
}
|
||||
|
||||
if #available(iOS 12, *) {
|
||||
|
@ -403,13 +403,13 @@ class ServiceViewController: UIViewController, StrongTableHost {
|
|||
|
||||
private func trustCurrentWiFi() {
|
||||
do {
|
||||
guard try ProductManager.shared.isEligible(forFeature: .trustedNetworks) else {
|
||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
|
||||
} catch ProductError.beta {
|
||||
presentBetaFeatureUnavailable("Trusted networks")
|
||||
return
|
||||
} catch {
|
||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||
return
|
||||
}
|
||||
|
||||
if #available(iOS 13, *) {
|
||||
|
@ -461,19 +461,19 @@ class ServiceViewController: UIViewController, StrongTableHost {
|
|||
|
||||
private func toggleTrustWiFi(cell: ToggleTableViewCell, at row: Int) {
|
||||
do {
|
||||
guard try ProductManager.shared.isEligible(forFeature: .trustedNetworks) else {
|
||||
delay {
|
||||
cell.setOn(false, animated: true)
|
||||
}
|
||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
|
||||
} catch ProductError.beta {
|
||||
delay {
|
||||
cell.setOn(false, animated: true)
|
||||
}
|
||||
presentBetaFeatureUnavailable("Trusted networks")
|
||||
return
|
||||
} catch {
|
||||
delay {
|
||||
cell.setOn(false, animated: true)
|
||||
}
|
||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||
return
|
||||
}
|
||||
|
||||
if cell.isOn {
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// AppConstants+App.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 11/4/19.
|
||||
// Copyright (c) 2021 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassepartoutCore
|
||||
|
||||
extension AppConstants {
|
||||
struct Rating {
|
||||
static let eventCount = 10
|
||||
}
|
||||
|
||||
struct InApp {
|
||||
static var isBetaFullVersion: Bool {
|
||||
guard !ProcessInfo.processInfo.arguments.contains("FULL_VERSION") else {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static let lastFullVersionBuild = 0
|
||||
|
||||
static let limitedNumberOfHosts = 2
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// ProductManager+App.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/25/20.
|
||||
// Copyright (c) 2021 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassepartoutCore
|
||||
|
||||
extension ProductManager {
|
||||
static let shared = ProductManager(
|
||||
Configuration(
|
||||
isBetaFullVersion: AppConstants.InApp.isBetaFullVersion,
|
||||
lastFullVersionBuild: AppConstants.InApp.lastFullVersionBuild
|
||||
)
|
||||
)
|
||||
}
|
|
@ -342,4 +342,31 @@ public class AppConstants {
|
|||
)
|
||||
]
|
||||
}
|
||||
|
||||
public struct Rating {
|
||||
#if os(iOS)
|
||||
public static let eventCount = 3
|
||||
#else
|
||||
public static let eventCount = 10
|
||||
#endif
|
||||
}
|
||||
|
||||
struct InApp {
|
||||
#if os(iOS)
|
||||
static var isBetaFullVersion: Bool {
|
||||
return ProcessInfo.processInfo.environment["FULL_VERSION"] != nil
|
||||
}
|
||||
|
||||
static let lastFullVersionBuild = 2016
|
||||
#else
|
||||
static var isBetaFullVersion: Bool {
|
||||
guard !ProcessInfo.processInfo.arguments.contains("FULL_VERSION") else {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static let lastFullVersionBuild = 0
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ import TunnelKit
|
|||
private let log = SwiftyBeaver.self
|
||||
|
||||
public enum ProductError: Error {
|
||||
case uneligible
|
||||
|
||||
case beta
|
||||
}
|
||||
|
||||
|
@ -161,25 +163,23 @@ public class ProductManager: NSObject {
|
|||
#endif
|
||||
return purchasedFeatures.contains(.fullVersion)
|
||||
}
|
||||
|
||||
public func isEligible(forFeature feature: Product) throws -> Bool {
|
||||
if isBeta {
|
||||
guard cfg.isBetaFullVersion else {
|
||||
throw ProductError.beta
|
||||
}
|
||||
}
|
||||
|
||||
private func isEligible(forFeature feature: Product) -> Bool {
|
||||
return isFullVersion() || purchasedFeatures.contains(feature)
|
||||
}
|
||||
|
||||
public func isEligible(forProvider metadata: Infrastructure.Metadata) throws -> Bool {
|
||||
if isBeta {
|
||||
guard cfg.isBetaFullVersion else {
|
||||
throw ProductError.beta
|
||||
}
|
||||
}
|
||||
private func isEligible(forProvider metadata: Infrastructure.Metadata) -> Bool {
|
||||
return isFullVersion() || purchasedFeatures.contains(metadata.product)
|
||||
}
|
||||
|
||||
private func isEligibleForTrustedNetworks() -> Bool {
|
||||
#if os(iOS)
|
||||
return isFullVersion() || purchasedFeatures.contains(.trustedNetworks)
|
||||
#else
|
||||
return isFullVersion()
|
||||
#endif
|
||||
}
|
||||
|
||||
public func isEligibleForFeedback() -> Bool {
|
||||
#if os(iOS)
|
||||
return isBeta || !purchasedFeatures.isEmpty
|
||||
|
@ -188,6 +188,39 @@ public class ProductManager: NSObject {
|
|||
#endif
|
||||
}
|
||||
|
||||
public func verifyEligible(forFeature feature: Product) throws {
|
||||
if isBeta {
|
||||
guard cfg.isBetaFullVersion else {
|
||||
throw ProductError.beta
|
||||
}
|
||||
}
|
||||
guard isEligible(forFeature: feature) else {
|
||||
throw ProductError.uneligible
|
||||
}
|
||||
}
|
||||
|
||||
public func verifyEligible(forProvider metadata: Infrastructure.Metadata) throws {
|
||||
if isBeta {
|
||||
guard cfg.isBetaFullVersion else {
|
||||
throw ProductError.beta
|
||||
}
|
||||
}
|
||||
guard isFullVersion() || purchasedFeatures.contains(metadata.product) else {
|
||||
throw ProductError.uneligible
|
||||
}
|
||||
}
|
||||
|
||||
public func verifyEligibleForTrustedNetworks() throws {
|
||||
if isBeta {
|
||||
guard cfg.isBetaFullVersion else {
|
||||
throw ProductError.beta
|
||||
}
|
||||
}
|
||||
guard isEligibleForTrustedNetworks() else {
|
||||
throw ProductError.uneligible
|
||||
}
|
||||
}
|
||||
|
||||
public func isCancelledPurchase(_ product: Product) -> Bool {
|
||||
return cancelledPurchases.contains(product)
|
||||
}
|
||||
|
@ -271,3 +304,81 @@ extension ProductManager: SKRequestDelegate {
|
|||
restoreCompletionHandler = nil
|
||||
}
|
||||
}
|
||||
|
||||
extension ProductManager {
|
||||
public static let shared = ProductManager(
|
||||
Configuration(
|
||||
isBetaFullVersion: AppConstants.InApp.isBetaFullVersion,
|
||||
lastFullVersionBuild: AppConstants.InApp.lastFullVersionBuild
|
||||
)
|
||||
)
|
||||
|
||||
public func reviewPurchases() {
|
||||
let service = TransientStore.shared.service
|
||||
reloadReceipt(andNotify: false)
|
||||
let isEligibleForFullVersion = isFullVersion()
|
||||
let hasCancelledFullVersion: Bool
|
||||
let hasCancelledTrustedNetworks: Bool
|
||||
var anyRefund = false
|
||||
|
||||
#if os(iOS)
|
||||
hasCancelledFullVersion = !isEligibleForFullVersion && (isCancelledPurchase(.fullVersion) || isCancelledPurchase(.fullVersion_iOS))
|
||||
hasCancelledTrustedNetworks = !isEligibleForFullVersion && isCancelledPurchase(.trustedNetworks)
|
||||
#else
|
||||
hasCancelledFullVersion = !isEligibleForFullVersion && (isCancelledPurchase(.fullVersion) || isCancelledPurchase(.fullVersion_macOS))
|
||||
hasCancelledTrustedNetworks = false
|
||||
#endif
|
||||
|
||||
// review features and potentially revert them if they were used (Siri is handled in AppDelegate)
|
||||
|
||||
log.debug("Checking 'Trusted networks'")
|
||||
if hasCancelledFullVersion || hasCancelledTrustedNetworks {
|
||||
|
||||
// reset trusted networks for ALL profiles (must load first)
|
||||
for key in service.allProfileKeys() {
|
||||
guard let profile = service.profile(withKey: key) else {
|
||||
continue
|
||||
}
|
||||
#if os(iOS)
|
||||
if profile.trustedNetworks.includesMobile || !profile.trustedNetworks.includedWiFis.isEmpty {
|
||||
profile.trustedNetworks.includesMobile = false
|
||||
profile.trustedNetworks.includedWiFis.removeAll()
|
||||
anyRefund = true
|
||||
}
|
||||
#else
|
||||
if !profile.trustedNetworks.includedWiFis.isEmpty {
|
||||
profile.trustedNetworks.includedWiFis.removeAll()
|
||||
anyRefund = true
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if anyRefund {
|
||||
log.debug("\tRefunded")
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Checking providers")
|
||||
for name in service.providerNames() {
|
||||
guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else {
|
||||
continue
|
||||
}
|
||||
if hasCancelledFullVersion || (!isEligibleForFullVersion && isCancelledPurchase(metadata.product)) {
|
||||
service.removeProfile(ProfileKey(name))
|
||||
log.debug("\tRefunded provider: \(name)")
|
||||
anyRefund = true
|
||||
}
|
||||
}
|
||||
|
||||
guard anyRefund else {
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// save reverts and remove fraud VPN profile
|
||||
TransientStore.shared.serialize(withProfiles: true)
|
||||
VPN.shared.uninstall(completionHandler: nil)
|
||||
|
||||
NotificationCenter.default.post(name: ProductManager.didReviewPurchases, object: nil)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue