diff --git a/Passepartout-iOS/Global/AppConstants+Flags.swift b/Passepartout-iOS/Global/AppConstants+App.swift similarity index 78% rename from Passepartout-iOS/Global/AppConstants+Flags.swift rename to Passepartout-iOS/Global/AppConstants+App.swift index fb939e70..21f3af30 100644 --- a/Passepartout-iOS/Global/AppConstants+Flags.swift +++ b/Passepartout-iOS/Global/AppConstants+App.swift @@ -1,5 +1,5 @@ // -// AppConstants+Flags.swift +// AppConstants+App.swift // Passepartout-iOS // // Created by Davide De Rosa on 11/2/19. @@ -31,24 +31,16 @@ extension AppConstants { static let eventCount = 3 } - struct Flags { - static var isBeta: Bool { - #if targetEnvironment(simulator) - return true - #else - return Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" - #endif - } - + struct InApp { static var isBetaFullVersion: Bool { guard !ProcessInfo.processInfo.arguments.contains("FULL_VERSION") else { return true } return false } - } - struct InApp { - public static let limitedNumberOfHosts = 2 + static let lastFullVersionBuild = 2016 + + static let limitedNumberOfHosts = 2 } } diff --git a/Passepartout-iOS/Global/Macros.swift b/Passepartout-iOS/Global/Macros.swift index 8867562f..48b4f760 100644 --- a/Passepartout-iOS/Global/Macros.swift +++ b/Passepartout-iOS/Global/Macros.swift @@ -24,6 +24,7 @@ // import UIKit +import PassepartoutCore extension UIView { static func get() -> T { diff --git a/Passepartout-iOS/Global/Product.swift b/Passepartout-iOS/Global/Product.swift deleted file mode 100644 index 9e4439ec..00000000 --- a/Passepartout-iOS/Global/Product.swift +++ /dev/null @@ -1,148 +0,0 @@ -// -// Product.swift -// Passepartout-iOS -// -// Created by Davide De Rosa on 10/11/19. -// Copyright (c) 2020 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 . -// - -import Foundation -import StoreKit -import PassepartoutCore - -struct Product: RawRepresentable, Equatable, Hashable { - private static let bundle = "com.algoritmico.ios.Passepartout" - - private static let donationsBundle = "\(bundle).donations" - - private static let featuresBundle = "\(bundle).features" - - private static let providersBundle = "\(bundle).providers" - - // MARK: Donations - - static let tinyDonation = Product(donationDescription: "Tiny") - - static let smallDonation = Product(donationDescription: "Small") - - static let mediumDonation = Product(donationDescription: "Medium") - - static let bigDonation = Product(donationDescription: "Big") - - static let hugeDonation = Product(donationDescription: "Huge") - - static let maxiDonation = Product(donationDescription: "Maxi") - - static let allDonations: [Product] = [ - .tinyDonation, - .smallDonation, - .mediumDonation, - .bigDonation, - .hugeDonation, - .maxiDonation - ] - - private init(donationDescription: String) { - self.init(rawValue: "\(Product.donationsBundle).\(donationDescription)")! - } - - // MARK: Features - - static let unlimitedHosts = Product(featureId: "unlimited_hosts") - - static let trustedNetworks = Product(featureId: "trusted_networks") - - static let siriShortcuts = Product(featureId: "siri") - - static let fullVersion = Product(featureId: "full_version") - - static let allFeatures: [Product] = [ - .unlimitedHosts, - .trustedNetworks, - .siriShortcuts, - .fullVersion - ] - - private init(featureId: String) { - self.init(rawValue: "\(Product.featuresBundle).\(featureId)")! - } - - // MARK: Providers - - static var allProviders: [Product] { - return InfrastructureFactory.shared.allMetadata.map { - return Product(providerMetadata: $0) - } - } - - fileprivate init(providerMetadata: Infrastructure.Metadata) { - self.init(rawValue: "\(Product.providersBundle).\(providerMetadata.inApp ?? providerMetadata.name)")! - } - - // MARK: All - - static var all: [Product] { - return allDonations + allFeatures + allProviders - } - - var isDonation: Bool { - return rawValue.hasPrefix(Product.donationsBundle) - } - - var isFeature: Bool { - return rawValue.hasPrefix(Product.featuresBundle) - } - - var isProvider: Bool { - return rawValue.hasPrefix(Product.providersBundle) - } - - // MARK: RawRepresentable - - let rawValue: String - - init?(rawValue: String) { - self.rawValue = rawValue - } - - // MARK: Equatable - - static func ==(lhs: Product, rhs: Product) -> Bool { - return lhs.rawValue == rhs.rawValue - } - - // MARK: Hashable - - func hash(into hasher: inout Hasher) { - rawValue.hash(into: &hasher) - } -} - -extension Infrastructure.Metadata { - var product: Product { - return Product(providerMetadata: self) - } -} - -extension Product { - func matchesStoreKitProduct(_ skProduct: SKProduct) -> Bool { - return skProduct.productIdentifier == rawValue - } -} diff --git a/Passepartout-iOS/Global/ProductManager+App.swift b/Passepartout-iOS/Global/ProductManager+App.swift new file mode 100644 index 00000000..a1f13ae9 --- /dev/null +++ b/Passepartout-iOS/Global/ProductManager+App.swift @@ -0,0 +1,117 @@ +// +// ProductManager+App.swift +// Passepartout-iOS +// +// Created by Davide De Rosa on 4/6/19. +// Copyright (c) 2020 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 . +// + +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) + var anyRefund = false + + // review features and potentially revert them if they were used (Siri is handled in AppDelegate) + + log.debug("Checking 'Trusted networks'") + if !isEligible(forFeature: .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 'Unlimited hosts'") + if !isEligible(forFeature: .unlimitedHosts) { + let ids = service.hostIds() + if ids.count > AppConstants.InApp.limitedNumberOfHosts { + for id in ids { + service.removeProfile(ProfileKey(.host, id)) + } + log.debug("\tRefunded") + anyRefund = true + } + } + + log.debug("Checking providers") + for name in service.providerNames() { + guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else { + continue + } + if !isEligible(forProvider: metadata) { + 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) + } +} + +extension ConnectionService { + var hasReachedMaximumNumberOfHosts: Bool { + let numberOfHosts = hostIds().count + return numberOfHosts >= AppConstants.InApp.limitedNumberOfHosts + } +} diff --git a/Passepartout-iOS/Global/ProductManager.swift b/Passepartout-iOS/Global/ProductManager.swift deleted file mode 100644 index 64631c11..00000000 --- a/Passepartout-iOS/Global/ProductManager.swift +++ /dev/null @@ -1,282 +0,0 @@ -// -// ProductManager.swift -// Passepartout-iOS -// -// Created by Davide De Rosa on 4/6/19. -// Copyright (c) 2020 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 . -// - -import Foundation -import StoreKit -import Convenience -import SwiftyBeaver -import Kvitto -import PassepartoutCore -import TunnelKit - -private let log = SwiftyBeaver.self - -class ProductManager: NSObject { - static let didReloadReceipt = Notification.Name("ProductManagerDidReloadReceipt") - - static let didReviewPurchases = Notification.Name("ProductManagerDidReviewPurchases") - - private static let lastFullVersionBuild = 2016 // 1.8.1 - - static let shared = ProductManager() - - private let inApp: InApp - - private var purchasedAppBuild: Int? - - private var purchasedFeatures: Set - - private var refreshRequest: SKReceiptRefreshRequest? - - private var restoreCompletionHandler: ((Error?) -> Void)? - - private override init() { - inApp = InApp() - purchasedAppBuild = nil - purchasedFeatures = [] - - super.init() - - reloadReceipt() - SKPaymentQueue.default().add(self) - } - - deinit { - SKPaymentQueue.default().remove(self) - } - - func listProducts(completionHandler: (([SKProduct]?, Error?) -> Void)?) { - let products = Product.all - guard !products.isEmpty else { - completionHandler?(nil, nil) - return - } - inApp.requestProducts(withIdentifiers: products, completionHandler: { _ in - log.debug("In-app products: \(self.inApp.products.map { $0.productIdentifier })") - - completionHandler?(self.inApp.products, nil) - }, failureHandler: { - completionHandler?(nil, $0) - }) - } - - func product(withIdentifier identifier: Product) -> SKProduct? { - return inApp.product(withIdentifier: identifier) - } - - func featureProducts(includingFullVersion: Bool) -> [SKProduct] { - return inApp.products.filter { - guard let p = Product(rawValue: $0.productIdentifier) else { - return false - } - guard includingFullVersion || p != .fullVersion else { - return false - } - guard p.isFeature else { - return false - } - return true - } - } - - func purchase(_ product: SKProduct, completionHandler: @escaping (InAppPurchaseResult, Error?) -> Void) { - inApp.purchase(product: product) { - if $0 == .success { - self.reloadReceipt() - } - completionHandler($0, $1) - } - } - - func restorePurchases(completionHandler: @escaping (Error?) -> Void) { - restoreCompletionHandler = completionHandler - refreshRequest = SKReceiptRefreshRequest() - refreshRequest?.delegate = self - refreshRequest?.start() - } - - // MARK: In-app eligibility - - private func reloadReceipt(andNotify: Bool = true) { - guard let url = Bundle.main.appStoreReceiptURL else { - log.warning("No App Store receipt found!") - return - } - guard let receipt = Receipt(contentsOfURL: url) else { - log.error("Could not parse App Store receipt!") - return - } - - if let originalAppVersion = receipt.originalAppVersion, let buildNumber = Int(originalAppVersion) { - purchasedAppBuild = buildNumber - } - purchasedFeatures.removeAll() - - if let buildNumber = purchasedAppBuild { - log.debug("Original purchased build: \(buildNumber)") - - // treat former purchases as full versions - if buildNumber <= ProductManager.lastFullVersionBuild { - purchasedFeatures.insert(.fullVersion) - } - } - if let iapReceipts = receipt.inAppPurchaseReceipts { - log.debug("In-app receipts:") - iapReceipts.forEach { - guard let pid = $0.productIdentifier, let product = Product(rawValue: pid) else { - return - } - if let cancellationDate = $0.cancellationDate { - log.debug("\t\(pid) [cancelled on: \(cancellationDate)]") - return - } - if let purchaseDate = $0.originalPurchaseDate { - log.debug("\t\(pid) [purchased on: \(purchaseDate)]") - } - purchasedFeatures.insert(product) - } - } - log.info("Purchased features: \(purchasedFeatures)") - - if andNotify { - NotificationCenter.default.post(name: ProductManager.didReloadReceipt, object: nil) - } - } - - func isFullVersion() -> Bool { - if AppConstants.Flags.isBeta && AppConstants.Flags.isBetaFullVersion { - return true - } - return purchasedFeatures.contains(.fullVersion) - } - - func isEligible(forFeature feature: Product) -> Bool { - return isFullVersion() || purchasedFeatures.contains(feature) - } - - func isEligible(forProvider metadata: Infrastructure.Metadata) -> Bool { - return isFullVersion() || purchasedFeatures.contains(metadata.product) - } - - func isEligibleForFeedback() -> Bool { - return AppConstants.Flags.isBeta || !purchasedFeatures.isEmpty - } - - // MARK: Review - - func reviewPurchases() { - let service = TransientStore.shared.service - reloadReceipt(andNotify: 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 !isEligible(forFeature: .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 profile.trustedNetworks.includesMobile || !profile.trustedNetworks.includedWiFis.isEmpty { - profile.trustedNetworks.includesMobile = false - profile.trustedNetworks.includedWiFis.removeAll() - anyRefund = true - } - } - if anyRefund { - log.debug("\tRefunded") - } - } - - log.debug("Checking 'Unlimited hosts'") - if !isEligible(forFeature: .unlimitedHosts) { - let ids = service.hostIds() - if ids.count > AppConstants.InApp.limitedNumberOfHosts { - for id in ids { - service.removeProfile(ProfileKey(.host, id)) - } - log.debug("\tRefunded") - anyRefund = true - } - } - - log.debug("Checking providers") - for name in service.providerNames() { - guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else { - continue - } - if !isEligible(forProvider: metadata) { - 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) - } -} - -extension ConnectionService { - var hasReachedMaximumNumberOfHosts: Bool { - let numberOfHosts = hostIds().count - return numberOfHosts >= AppConstants.InApp.limitedNumberOfHosts - } -} - -extension ProductManager: SKPaymentTransactionObserver { - func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { - reloadReceipt() - } -} - -extension ProductManager: SKRequestDelegate { - func requestDidFinish(_ request: SKRequest) { - reloadReceipt() - inApp.restorePurchases { [weak self] (finished, _, error) in - guard finished else { - return - } - self?.restoreCompletionHandler?(error) - self?.restoreCompletionHandler = nil - } - } - - func request(_ request: SKRequest, didFailWithError error: Error) { - restoreCompletionHandler?(error) - restoreCompletionHandler = nil - } -} diff --git a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift index 1c7980c5..53c01813 100644 --- a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift @@ -86,7 +86,7 @@ class OrganizerViewController: UITableViewController, StrongTableHost { model.set([.openAbout], forSection: .about) model.set([.uninstall], forSection: .destruction) - if AppConstants.Flags.isBeta { + if ProductManager.shared.isBeta { model.add(.test) model.setHeader("Beta", forSection: .test) model.set([.testInterfaces, .testDisplayLog, .testTermination], forSection: .test) diff --git a/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift b/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift index 756e39d6..f539c221 100644 --- a/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift +++ b/Passepartout-iOS/Scenes/Purchase/PurchaseViewController.swift @@ -25,6 +25,7 @@ import UIKit import StoreKit +import PassepartoutCore import SwiftyBeaver import Convenience diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index 573e657e..592b6a9e 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -19,11 +19,10 @@ 0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B1213BFFCF00BA1586 /* ProviderPresetViewController.swift */; }; 0E1D72B4213C118500BA1586 /* ConfigurationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */; }; 0E24273A225950450064A1A3 /* About.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E24273C225950450064A1A3 /* About.storyboard */; }; - 0E242740225951B00064A1A3 /* ProductManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E24273F225951B00064A1A3 /* ProductManager.swift */; }; 0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E242741225956AC0064A1A3 /* DonationViewController.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+Flags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2EB062236D8E1E0079DB53 /* AppConstants+Flags.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, ); }; }; @@ -52,7 +51,6 @@ 0E3152DB223FA05800F61841 /* ProfileKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E79D14021919F5600BB5FB2 /* ProfileKey.swift */; }; 0E3152DC223FA05800F61841 /* ProviderConnectionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */; }; 0E3262D9235EE8DA00B5E470 /* HostImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3262D8235EE8DA00B5E470 /* HostImporter.swift */; }; - 0E3419AD2350815E00419E18 /* Product.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3419AC2350815E00419E18 /* Product.swift */; }; 0E3586FE225BD34800509A4D /* ActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */; }; 0E36D24D2240234B006AF062 /* ShortcutsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */; }; 0E36D25822403469006AF062 /* Shortcuts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E36D25A22403469006AF062 /* Shortcuts.storyboard */; }; @@ -90,6 +88,9 @@ 0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; }; 0EB9EB7323867E7F009C0A1C /* TrustedNetworksUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB9EB7223867E7F009C0A1C /* TrustedNetworksUI.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 */; }; + 0ECA7E2D25967BF40095F369 /* Product.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA7E2525967BDB0095F369 /* Product.swift */; }; + 0ECA7E3025967BFB0095F369 /* ProductManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECA7E2625967BDB0095F369 /* ProductManager.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 */; }; @@ -174,7 +175,6 @@ 0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationViewController.swift; sourceTree = ""; }; 0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; 0E24273B225950450064A1A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/About.storyboard; sourceTree = ""; }; - 0E24273F225951B00064A1A3 /* ProductManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductManager.swift; sourceTree = ""; }; 0E242741225956AC0064A1A3 /* DonationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewController.swift; sourceTree = ""; }; 0E2AC24422EC3AC10037B4B0 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; 0E2B493F20FCFF990094784C /* Theme+Titles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Titles.swift"; sourceTree = ""; }; @@ -183,12 +183,11 @@ 0E2C54C4230056EF00F59453 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Core.strings"; sourceTree = ""; }; 0E2C54C52300570200F59453 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/App.strings"; sourceTree = ""; }; 0E2D11B9217DBEDE0096822C /* ConnectionService+Configurations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConnectionService+Configurations.swift"; sourceTree = ""; }; - 0E2EB062236D8E1E0079DB53 /* AppConstants+Flags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppConstants+Flags.swift"; sourceTree = ""; }; + 0E2EB062236D8E1E0079DB53 /* AppConstants+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppConstants+App.swift"; sourceTree = ""; }; 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 = ""; }; 0E31529E223F9EF500F61841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0E3262D8235EE8DA00B5E470 /* HostImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostImporter.swift; sourceTree = ""; }; - 0E3419AC2350815E00419E18 /* Product.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Product.swift; sourceTree = ""; }; 0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTableViewCell.swift; sourceTree = ""; }; 0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsAddViewController.swift; sourceTree = ""; }; 0E36D25B224034AD006AF062 /* ShortcutsConnectToViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConnectToViewController.swift; sourceTree = ""; }; @@ -277,6 +276,9 @@ 0EBE3AA3213DC1B000BFA2F5 /* HostConnectionProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HostConnectionProfile.swift; sourceTree = ""; }; 0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderConnectionProfile.swift; sourceTree = ""; }; 0EC7F20420E24308004EA58E /* DebugLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugLog.swift; sourceTree = ""; }; + 0ECA7E2125967BB90095F369 /* ProductManager+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ProductManager+App.swift"; sourceTree = ""; }; + 0ECA7E2525967BDB0095F369 /* Product.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Product.swift; path = Submodules/Core/Passepartout/Sources/Model/Product.swift; sourceTree = SOURCE_ROOT; }; + 0ECA7E2625967BDB0095F369 /* ProductManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProductManager.swift; path = Submodules/Core/Passepartout/Sources/Model/ProductManager.swift; sourceTree = SOURCE_ROOT; }; 0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Assets.swift"; sourceTree = ""; }; 0ECEB105224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 0ECEB106224FE51400E9E551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Organizer.storyboard; sourceTree = ""; }; @@ -515,6 +517,8 @@ 0EAC572E249426E200D0FCE0 /* GracefulVPN.swift */, 0E45E70F22BE108100F19312 /* OpenVPNOptions.swift */, 0E89DFC4213DF7AE00741BA1 /* Preferences.swift */, + 0ECA7E2525967BDB0095F369 /* Product.swift */, + 0ECA7E2625967BDB0095F369 /* ProductManager.swift */, 0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */, 0E89DFC7213E8FC500741BA1 /* SessionProxy+Communication.swift */, 0E2B494120FD16540094784C /* TransientStore.swift */, @@ -554,12 +558,11 @@ isa = PBXGroup; children = ( 0E45E6E222BD793800F19312 /* App.strings */, - 0E2EB062236D8E1E0079DB53 /* AppConstants+Flags.swift */, + 0E2EB062236D8E1E0079DB53 /* AppConstants+App.swift */, 0E3262D8235EE8DA00B5E470 /* HostImporter.swift */, 0EFD943D215BE10800529B64 /* IssueReporter.swift */, 0E4FD7F020D58618002221FF /* Macros.swift */, - 0E3419AC2350815E00419E18 /* Product.swift */, - 0E24273F225951B00064A1A3 /* ProductManager.swift */, + 0ECA7E2125967BB90095F369 /* ProductManager+App.swift */, 0ECC60DD2256B6890020BEAC /* SwiftGen+Assets.swift */, 0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */, 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */, @@ -962,6 +965,7 @@ 0E3152D9223FA05800F61841 /* HostConnectionProfile.swift in Sources */, 0E3152D6223FA05400F61841 /* TransientStore.swift in Sources */, 0E3152CC223FA04D00F61841 /* WebServices.swift in Sources */, + 0ECA7E2D25967BF40095F369 /* Product.swift in Sources */, 0E3152BB223FA03D00F61841 /* AppConstants.swift in Sources */, 0E3152CA223FA04D00F61841 /* InfrastructurePreset.swift in Sources */, 0E3152CE223FA05400F61841 /* ConnectionService.swift in Sources */, @@ -979,6 +983,7 @@ 0E3152CB223FA04D00F61841 /* Pool.swift in Sources */, 0EA8451A238C2AB500EFC500 /* Infrastructure+Metadata.swift in Sources */, 0EB9EB7323867E7F009C0A1C /* TrustedNetworksUI.swift in Sources */, + 0ECA7E3025967BFB0095F369 /* ProductManager.swift in Sources */, 0E3CAFC0229AAE770008E5C8 /* Intents.intentdefinition in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1002,20 +1007,18 @@ 0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */, 0E776642229D0DAE0023FA76 /* Intents.intentdefinition in Sources */, 0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */, - 0E242740225951B00064A1A3 /* ProductManager.swift in Sources */, 0E6268942369AD0600355F75 /* PurchaseTableViewCell.swift in Sources */, 0E1066C920E0F84A004F98B7 /* Cells.swift in Sources */, 0E4B0D6B2366E3C100C890B4 /* PurchaseViewController.swift in Sources */, 0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */, 0E05C5D620D1645F006EE732 /* SwiftGen+Scenes.swift in Sources */, 0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */, - 0E3419AD2350815E00419E18 /* Product.swift in Sources */, 0E9CDB6723604AD5006733B4 /* ServerNetworkViewController.swift in Sources */, 0E3262D9235EE8DA00B5E470 /* HostImporter.swift in Sources */, 0ED31C2C20CF2D6F0027975F /* ProviderPoolViewController.swift in Sources */, 0E2B494020FCFF990094784C /* Theme+Titles.swift in Sources */, 0E05C5D520D1645F006EE732 /* SettingTableViewCell.swift in Sources */, - 0E2EB063236D8E1E0079DB53 /* AppConstants+Flags.swift in Sources */, + 0E2EB063236D8E1E0079DB53 /* AppConstants+App.swift in Sources */, 0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */, 0E1D72B2213BFFCF00BA1586 /* ProviderPresetViewController.swift in Sources */, 0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */, @@ -1026,6 +1029,7 @@ 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 */, diff --git a/Podfile b/Podfile index c78ef228..470e7193 100644 --- a/Podfile +++ b/Podfile @@ -9,7 +9,7 @@ $tunnelkit_specs = ['Protocols/OpenVPN', 'Extra/LZO'] def shared_pods #pod_version $tunnelkit_name, $tunnelkit_specs, '~> 3.0.1' - pod_git $tunnelkit_name, $tunnelkit_specs, '4e2dca9' + pod_git $tunnelkit_name, $tunnelkit_specs, '304d021' #pod_path $tunnelkit_name, $tunnelkit_specs, '..' pod 'SSZipArchive' diff --git a/Podfile.lock b/Podfile.lock index 13ad69d9..3d9034f1 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -52,8 +52,8 @@ DEPENDENCIES: - Kvitto - MBProgressHUD - SSZipArchive - - TunnelKit/Extra/LZO (from `https://github.com/passepartoutvpn/tunnelkit`, commit `4e2dca9`) - - TunnelKit/Protocols/OpenVPN (from `https://github.com/passepartoutvpn/tunnelkit`, commit `4e2dca9`) + - TunnelKit/Extra/LZO (from `https://github.com/passepartoutvpn/tunnelkit`, commit `304d021`) + - TunnelKit/Protocols/OpenVPN (from `https://github.com/passepartoutvpn/tunnelkit`, commit `304d021`) SPEC REPOS: https://github.com/cocoapods/specs.git: @@ -69,7 +69,7 @@ EXTERNAL SOURCES: :commit: 0b09b1e :git: https://github.com/keeshux/convenience TunnelKit: - :commit: 4e2dca9 + :commit: 304d021 :git: https://github.com/passepartoutvpn/tunnelkit CHECKOUT OPTIONS: @@ -77,7 +77,7 @@ CHECKOUT OPTIONS: :commit: 0b09b1e :git: https://github.com/keeshux/convenience TunnelKit: - :commit: 4e2dca9 + :commit: 304d021 :git: https://github.com/passepartoutvpn/tunnelkit SPEC CHECKSUMS: @@ -90,6 +90,6 @@ SPEC CHECKSUMS: SwiftyBeaver: 2e8acd6fc90c6d0a27055867a290794926d57c02 TunnelKit: 4db9180956f8aaf4ab152fd0d38c6c9c63a46cf8 -PODFILE CHECKSUM: 1fd20c6db48881199527c72f0e28c9a6c3cb85dc +PODFILE CHECKSUM: c61d36f819940bcbb0684b98ec889e9bb5df918e COCOAPODS: 1.10.0 diff --git a/Submodules/Core b/Submodules/Core index 4ec43fd2..51a2835f 160000 --- a/Submodules/Core +++ b/Submodules/Core @@ -1 +1 @@ -Subproject commit 4ec43fd23982c52b45511fc3b394766148492049 +Subproject commit 51a2835ffe9eeb5005cc4c673ace075eb5a00e3f