Move ProductManager to Core

This commit is contained in:
Davide De Rosa 2020-12-25 21:04:56 +01:00
parent a6f59f72bb
commit 1213212332
11 changed files with 148 additions and 463 deletions

View File

@ -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
}
}

View File

@ -24,6 +24,7 @@
//
import UIKit
import PassepartoutCore
extension UIView {
static func get<T: UIView>() -> T {

View File

@ -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 <http://www.gnu.org/licenses/>.
//
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
}
}

View File

@ -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 <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)
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
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
//
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<Product>
private var purchasedAppBuild: Int?
private var purchasedFeatures: Set<Product>
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
}
}

View File

@ -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)

View File

@ -25,6 +25,7 @@
import UIKit
import StoreKit
import PassepartoutCore
import SwiftyBeaver
import Convenience

View File

@ -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 = "<group>"; };
0E23B4A12298559800304C30 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
0E24273B225950450064A1A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/About.storyboard; sourceTree = "<group>"; };
0E24273F225951B00064A1A3 /* ProductManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductManager.swift; sourceTree = "<group>"; };
0E242741225956AC0064A1A3 /* DonationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationViewController.swift; sourceTree = "<group>"; };
0E2AC24422EC3AC10037B4B0 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
0E2B493F20FCFF990094784C /* Theme+Titles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+Titles.swift"; sourceTree = "<group>"; };
@ -183,12 +183,11 @@
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+Flags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppConstants+Flags.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>"; };
0E3262D8235EE8DA00B5E470 /* HostImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostImporter.swift; sourceTree = "<group>"; };
0E3419AC2350815E00419E18 /* Product.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Product.swift; sourceTree = "<group>"; };
0E3586FD225BD34800509A4D /* ActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityTableViewCell.swift; sourceTree = "<group>"; };
0E36D24C2240234B006AF062 /* ShortcutsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsAddViewController.swift; sourceTree = "<group>"; };
0E36D25B224034AD006AF062 /* ShortcutsConnectToViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsConnectToViewController.swift; sourceTree = "<group>"; };
@ -277,6 +276,9 @@
0EBE3AA3213DC1B000BFA2F5 /* HostConnectionProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HostConnectionProfile.swift; sourceTree = "<group>"; };
0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderConnectionProfile.swift; 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>"; };
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 = "<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>"; };
@ -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 */,

View File

@ -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'

View File

@ -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

@ -1 +1 @@
Subproject commit 4ec43fd23982c52b45511fc3b394766148492049
Subproject commit 51a2835ffe9eeb5005cc4c673ace075eb5a00e3f