Merge branch 'drop-single-features'
This commit is contained in:
commit
c3c3339878
|
@ -67,7 +67,7 @@ extension UIViewController {
|
||||||
func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) {
|
func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) {
|
||||||
let nav = StoryboardScene.Purchase.initialScene.instantiate()
|
let nav = StoryboardScene.Purchase.initialScene.instantiate()
|
||||||
let vc = nav.topViewController as? PurchaseViewController
|
let vc = nav.topViewController as? PurchaseViewController
|
||||||
vc?.feature = product
|
// vc?.feature = product
|
||||||
vc?.delegate = delegate
|
vc?.delegate = delegate
|
||||||
|
|
||||||
// enforce pre iOS 13 behavior
|
// enforce pre iOS 13 behavior
|
||||||
|
|
|
@ -230,7 +230,7 @@ extension WizardProviderViewController: AccountViewControllerDelegate {
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
extension WizardProviderViewController: PurchaseViewControllerDelegate {
|
extension WizardProviderViewController: PurchaseViewControllerDelegate {
|
||||||
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product) {
|
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product?) {
|
||||||
guard let metadata = selectedMetadata else {
|
guard let metadata = selectedMetadata else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,13 @@ import Convenience
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
protocol PurchaseViewControllerDelegate: class {
|
protocol PurchaseViewControllerDelegate: class {
|
||||||
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product)
|
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product?)
|
||||||
}
|
}
|
||||||
|
|
||||||
class PurchaseViewController: UITableViewController, StrongTableHost {
|
class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
private var isLoading = true
|
private var isLoading = true
|
||||||
|
|
||||||
var feature: Product!
|
var feature: Product?
|
||||||
|
|
||||||
weak var delegate: PurchaseViewControllerDelegate?
|
weak var delegate: PurchaseViewControllerDelegate?
|
||||||
|
|
||||||
|
@ -66,29 +66,27 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_iOS) {
|
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_iOS) {
|
||||||
self.skPlatformVersion = skPlatformVersion
|
self.skPlatformVersion = skPlatformVersion
|
||||||
rows.append(.platformVersion)
|
rows.append(.platformVersion)
|
||||||
|
|
||||||
|
let bullets: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS, .fullVersion_macOS]).map {
|
||||||
|
return $0.localizedTitle
|
||||||
|
}.sortedCaseInsensitive()
|
||||||
|
platformVersionExtra = bullets.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
if let skFullVersion = pm.product(withIdentifier: .fullVersion) {
|
if !pm.hasPurchased(.fullVersion_macOS), let skFullVersion = pm.product(withIdentifier: .fullVersion) {
|
||||||
self.skFullVersion = skFullVersion
|
self.skFullVersion = skFullVersion
|
||||||
rows.append(.fullVersion)
|
rows.append(.fullVersion)
|
||||||
|
|
||||||
|
let bullets: [String] = ProductManager.shared.featureProducts(including: [.fullVersion_iOS, .fullVersion_macOS]).map {
|
||||||
|
return $0.localizedTitle
|
||||||
|
}.sortedCaseInsensitive()
|
||||||
|
fullVersionExtra = bullets.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
if let skFeature = pm.product(withIdentifier: feature) {
|
if let feature = feature, let skFeature = pm.product(withIdentifier: feature) {
|
||||||
self.skFeature = skFeature
|
self.skFeature = skFeature
|
||||||
rows.append(.feature)
|
rows.append(.feature)
|
||||||
}
|
}
|
||||||
rows.append(.restore)
|
rows.append(.restore)
|
||||||
model.set(rows, forSection: .products)
|
model.set(rows, forSection: .products)
|
||||||
|
|
||||||
let platformBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS, .fullVersion_macOS]).map {
|
|
||||||
return "- \($0.localizedTitle)"
|
|
||||||
}.sortedCaseInsensitive()
|
|
||||||
let platformBullets = platformBulletsList.joined(separator: "\n")
|
|
||||||
platformVersionExtra = "- \(L10n.Core.Purchase.Cells.FullVersion.extraDescription(platformBullets))"
|
|
||||||
|
|
||||||
let fullBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS]).map {
|
|
||||||
return "- \($0.localizedTitle)"
|
|
||||||
}.sortedCaseInsensitive()
|
|
||||||
let fullBullets = fullBulletsList.joined(separator: "\n")
|
|
||||||
fullVersionExtra = "- \(L10n.Core.Purchase.Cells.FullVersion.extraDescription(fullBullets))"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIViewController
|
// MARK: UIViewController
|
||||||
|
@ -96,10 +94,6 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
guard let _ = feature else {
|
|
||||||
fatalError("No feature set for purchase")
|
|
||||||
}
|
|
||||||
|
|
||||||
title = L10n.Core.Purchase.title
|
title = L10n.Core.Purchase.title
|
||||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close))
|
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close))
|
||||||
|
|
||||||
|
@ -168,7 +162,7 @@ class PurchaseViewController: UITableViewController, StrongTableHost {
|
||||||
guard let weakSelf = self else {
|
guard let weakSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let product = weakSelf.feature.matchesStoreKitProduct(skProduct) ? weakSelf.feature! : .fullVersion
|
let product = Product(rawValue: skProduct.productIdentifier)
|
||||||
weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product)
|
weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ extension NSAlert {
|
||||||
extension NSViewController {
|
extension NSViewController {
|
||||||
func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) {
|
func presentPurchaseScreen(forProduct product: Product, delegate: PurchaseViewControllerDelegate? = nil) {
|
||||||
let vc = StoryboardScene.Purchase.initialScene.instantiate()
|
let vc = StoryboardScene.Purchase.initialScene.instantiate()
|
||||||
vc.feature = product
|
// vc.feature = product
|
||||||
vc.delegate = delegate
|
vc.delegate = delegate
|
||||||
presentAsModalWindow(vc)
|
presentAsModalWindow(vc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import Convenience
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
protocol PurchaseViewControllerDelegate: class {
|
protocol PurchaseViewControllerDelegate: class {
|
||||||
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product)
|
func purchaseController(_ purchaseController: PurchaseViewController, didPurchase product: Product?)
|
||||||
}
|
}
|
||||||
|
|
||||||
class PurchaseViewController: NSViewController {
|
class PurchaseViewController: NSViewController {
|
||||||
|
@ -52,7 +52,7 @@ class PurchaseViewController: NSViewController {
|
||||||
|
|
||||||
@IBOutlet private weak var buttonRestore: NSButton!
|
@IBOutlet private weak var buttonRestore: NSButton!
|
||||||
|
|
||||||
var feature: Product!
|
var feature: Product?
|
||||||
|
|
||||||
weak var delegate: PurchaseViewControllerDelegate?
|
weak var delegate: PurchaseViewControllerDelegate?
|
||||||
|
|
||||||
|
@ -74,27 +74,25 @@ class PurchaseViewController: NSViewController {
|
||||||
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_macOS) {
|
if let skPlatformVersion = pm.product(withIdentifier: .fullVersion_macOS) {
|
||||||
self.skPlatformVersion = skPlatformVersion
|
self.skPlatformVersion = skPlatformVersion
|
||||||
rows.append(.platformVersion)
|
rows.append(.platformVersion)
|
||||||
|
|
||||||
|
let bullets: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_iOS, .fullVersion_macOS, .siriShortcuts]).map {
|
||||||
|
return $0.localizedTitle
|
||||||
|
}.sortedCaseInsensitive()
|
||||||
|
platformVersionExtra = bullets.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
if let skFullVersion = pm.product(withIdentifier: .fullVersion) {
|
if !pm.hasPurchased(.fullVersion_iOS), let skFullVersion = pm.product(withIdentifier: .fullVersion) {
|
||||||
self.skFullVersion = skFullVersion
|
self.skFullVersion = skFullVersion
|
||||||
rows.append(.fullVersion)
|
rows.append(.fullVersion)
|
||||||
|
|
||||||
|
let bullets: [String] = ProductManager.shared.featureProducts(including: [.fullVersion_iOS, .fullVersion_macOS]).map {
|
||||||
|
return $0.localizedTitle
|
||||||
|
}.sortedCaseInsensitive()
|
||||||
|
fullVersionExtra = bullets.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
if let skFeature = pm.product(withIdentifier: feature) {
|
if let feature = feature, let skFeature = pm.product(withIdentifier: feature) {
|
||||||
self.skFeature = skFeature
|
self.skFeature = skFeature
|
||||||
rows.append(.feature)
|
rows.append(.feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
let platformBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.siriShortcuts, .fullVersion, .fullVersion_iOS, .fullVersion_macOS]).map {
|
|
||||||
return $0.localizedTitle
|
|
||||||
}.sortedCaseInsensitive()
|
|
||||||
let platformBullets = platformBulletsList.joined(separator: "\n")
|
|
||||||
platformVersionExtra = L10n.Core.Purchase.Cells.FullVersion.extraDescription(platformBullets)
|
|
||||||
|
|
||||||
let fullBulletsList: [String] = ProductManager.shared.featureProducts(excluding: [.fullVersion, .fullVersion_macOS]).map {
|
|
||||||
return $0.localizedTitle
|
|
||||||
}.sortedCaseInsensitive()
|
|
||||||
let fullBullets = fullBulletsList.joined(separator: "\n")
|
|
||||||
fullVersionExtra = L10n.Core.Purchase.Cells.FullVersion.extraDescription(fullBullets)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: NSViewController
|
// MARK: NSViewController
|
||||||
|
@ -108,10 +106,6 @@ class PurchaseViewController: NSViewController {
|
||||||
buttonPurchase.title = L10n.Core.Purchase.title
|
buttonPurchase.title = L10n.Core.Purchase.title
|
||||||
buttonRestore.title = L10n.Core.Purchase.Cells.Restore.title
|
buttonRestore.title = L10n.Core.Purchase.Cells.Restore.title
|
||||||
|
|
||||||
guard let _ = feature else {
|
|
||||||
fatalError("No feature set for purchase")
|
|
||||||
}
|
|
||||||
|
|
||||||
tableView.usesAutomaticRowHeights = true
|
tableView.usesAutomaticRowHeights = true
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
@ -197,7 +191,7 @@ class PurchaseViewController: NSViewController {
|
||||||
guard let weakSelf = self else {
|
guard let weakSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let product = weakSelf.feature.matchesStoreKitProduct(skProduct) ? weakSelf.feature! : .fullVersion
|
let product = Product(rawValue: skProduct.productIdentifier)
|
||||||
weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product)
|
weakSelf.delegate?.purchaseController(weakSelf, didPurchase: product)
|
||||||
|
|
||||||
self?.dismiss(nil)
|
self?.dismiss(nil)
|
||||||
|
|
|
@ -108,7 +108,7 @@ class TrustedNetworksViewController: NSViewController, ProfileCustomization {
|
||||||
|
|
||||||
@IBAction private func toggleTrustEthernet(_ sender: Any?) {
|
@IBAction private func toggleTrustEthernet(_ sender: Any?) {
|
||||||
do {
|
do {
|
||||||
try ProductManager.shared.verifyEligibleForTrustedNetworks()
|
try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
|
||||||
} catch {
|
} catch {
|
||||||
checkTrustEthernet.state = .off
|
checkTrustEthernet.state = .off
|
||||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||||
|
@ -132,7 +132,7 @@ class TrustedNetworksViewController: NSViewController, ProfileCustomization {
|
||||||
override func shouldPerformSegue(withIdentifier identifier: NSStoryboardSegue.Identifier, sender: Any?) -> Bool {
|
override func shouldPerformSegue(withIdentifier identifier: NSStoryboardSegue.Identifier, sender: Any?) -> Bool {
|
||||||
if identifier == StoryboardSegue.Service.trustedNetworkAddSegueIdentifier.rawValue {
|
if identifier == StoryboardSegue.Service.trustedNetworkAddSegueIdentifier.rawValue {
|
||||||
do {
|
do {
|
||||||
try ProductManager.shared.verifyEligibleForTrustedNetworks()
|
try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
|
||||||
} catch {
|
} catch {
|
||||||
presentPurchaseScreen(forProduct: .trustedNetworks)
|
presentPurchaseScreen(forProduct: .trustedNetworks)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -554,7 +554,7 @@ public class ConnectionService: Codable {
|
||||||
|
|
||||||
var rules: [NEOnDemandRule] = []
|
var rules: [NEOnDemandRule] = []
|
||||||
do {
|
do {
|
||||||
try ProductManager.shared.verifyEligibleForTrustedNetworks()
|
try ProductManager.shared.verifyEligible(forFeature: .trustedNetworks)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if profile.trustedNetworks.includesMobile {
|
if profile.trustedNetworks.includesMobile {
|
||||||
let rule = policyRule(for: profile)
|
let rule = policyRule(for: profile)
|
||||||
|
|
|
@ -66,6 +66,8 @@ public struct Product: RawRepresentable, Equatable, Hashable {
|
||||||
|
|
||||||
// MARK: Features
|
// MARK: Features
|
||||||
|
|
||||||
|
public static let allProviders = Product(featureId: "all_providers")
|
||||||
|
|
||||||
public static let trustedNetworks = Product(featureId: "trusted_networks")
|
public static let trustedNetworks = Product(featureId: "trusted_networks")
|
||||||
|
|
||||||
public static let siriShortcuts = Product(featureId: "siri")
|
public static let siriShortcuts = Product(featureId: "siri")
|
||||||
|
@ -77,6 +79,7 @@ public struct Product: RawRepresentable, Equatable, Hashable {
|
||||||
public static let fullVersion = Product(featureId: "full_multi_version")
|
public static let fullVersion = Product(featureId: "full_multi_version")
|
||||||
|
|
||||||
public static let allFeatures: [Product] = [
|
public static let allFeatures: [Product] = [
|
||||||
|
.allProviders,
|
||||||
.trustedNetworks,
|
.trustedNetworks,
|
||||||
.siriShortcuts,
|
.siriShortcuts,
|
||||||
.fullVersion_iOS,
|
.fullVersion_iOS,
|
||||||
|
|
|
@ -126,6 +126,21 @@ public class ProductManager: NSObject {
|
||||||
return inApp.product(withIdentifier: identifier)
|
return inApp.product(withIdentifier: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func featureProducts(including: [Product]) -> [SKProduct] {
|
||||||
|
return inApp.products.filter {
|
||||||
|
guard let p = Product(rawValue: $0.productIdentifier) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard including.contains(p) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard p.isFeature else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func featureProducts(excluding: [Product]) -> [SKProduct] {
|
public func featureProducts(excluding: [Product]) -> [SKProduct] {
|
||||||
return inApp.products.filter {
|
return inApp.products.filter {
|
||||||
guard let p = Product(rawValue: $0.productIdentifier) else {
|
guard let p = Product(rawValue: $0.productIdentifier) else {
|
||||||
|
@ -159,29 +174,30 @@ public class ProductManager: NSObject {
|
||||||
|
|
||||||
// MARK: In-app eligibility
|
// MARK: In-app eligibility
|
||||||
|
|
||||||
public func isFullVersion() -> Bool {
|
private func isCurrentPlatformVersion() -> Bool {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if (isBeta && cfg.isBetaFullVersion) || purchasedFeatures.contains(.fullVersion_iOS) {
|
return purchasedFeatures.contains(.fullVersion_iOS)
|
||||||
return true
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
if (isBeta && cfg.isBetaFullVersion) || purchasedFeatures.contains(.fullVersion_macOS) {
|
return purchasedFeatures.contains(.fullVersion_macOS)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func isFullVersion() -> Bool {
|
||||||
|
if isBeta && cfg.isBetaFullVersion {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if isCurrentPlatformVersion() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return purchasedFeatures.contains(.fullVersion)
|
return purchasedFeatures.contains(.fullVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isEligible(forFeature feature: Product) -> Bool {
|
private func isEligible(forFeature feature: Product) -> Bool {
|
||||||
|
#if os(iOS)
|
||||||
return isFullVersion() || purchasedFeatures.contains(feature)
|
return isFullVersion() || purchasedFeatures.contains(feature)
|
||||||
}
|
#else
|
||||||
|
return isFullVersion()
|
||||||
private func isEligible(forProvider metadata: Infrastructure.Metadata) -> Bool {
|
#endif
|
||||||
return isFullVersion() || purchasedFeatures.contains(metadata.product)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func isEligibleForTrustedNetworks() -> Bool {
|
|
||||||
return isFullVersion() || purchasedFeatures.contains(.trustedNetworks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEligibleForFeedback() -> Bool {
|
public func isEligibleForFeedback() -> Bool {
|
||||||
|
@ -211,23 +227,13 @@ public class ProductManager: NSObject {
|
||||||
throw ProductError.beta
|
throw ProductError.beta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
guard isFullVersion() || purchasedFeatures.contains(metadata.product) else {
|
guard isEligible(forFeature: metadata.product) else {
|
||||||
throw ProductError.uneligible
|
throw ProductError.uneligible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func verifyEligibleForTrustedNetworks() throws {
|
public func hasPurchased(_ product: Product) -> Bool {
|
||||||
if isBeta {
|
return purchasedFeatures.contains(product)
|
||||||
if cfg.isBetaFullVersion {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard !cfg.locksBetaFeatures else {
|
|
||||||
throw ProductError.beta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
guard isEligibleForTrustedNetworks() else {
|
|
||||||
throw ProductError.uneligible
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isCancelledPurchase(_ product: Product) -> Bool {
|
public func isCancelledPurchase(_ product: Product) -> Bool {
|
||||||
|
|
Loading…
Reference in New Issue