Move ProductManager to Core
This commit is contained in:
parent
a6f59f72bb
commit
1213212332
|
@ -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
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import PassepartoutCore
|
||||
|
||||
extension UIView {
|
||||
static func get<T: UIView>() -> T {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
import PassepartoutCore
|
||||
import SwiftyBeaver
|
||||
import Convenience
|
||||
|
||||
|
|
|
@ -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 */,
|
||||
|
|
2
Podfile
2
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'
|
||||
|
||||
|
|
10
Podfile.lock
10
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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4ec43fd23982c52b45511fc3b394766148492049
|
||||
Subproject commit 51a2835ffe9eeb5005cc4c673ace075eb5a00e3f
|
Loading…
Reference in New Issue