Granularize app features (#671)

Split .networkSettings and add .sharing for #668
This commit is contained in:
Davide 2024-10-03 12:13:03 +02:00 committed by GitHub
parent 63b0199a39
commit 0917e47ea3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 94 additions and 38 deletions

View File

@ -71,6 +71,7 @@
/* Begin PBXFileReference section */
0E06D18F2B87629100176E1D /* Passepartout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Passepartout.app; sourceTree = BUILT_PRODUCTS_DIR; };
0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
0E7D0EAD2CAEA47700A2F28D /* Passepartout.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Passepartout.xctestplan; sourceTree = "<group>"; };
0E7E3D5B2B9345FD002BBDB4 /* App.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassepartoutApp.swift; sourceTree = "<group>"; };
@ -152,6 +153,7 @@
isa = PBXGroup;
children = (
0E8D852F2C328CA1005493DE /* Config.xcconfig */,
0E7D0EAD2CAEA47700A2F28D /* Passepartout.xctestplan */,
0E7E3D5A2B9345FD002BBDB4 /* App */,
0EDE56E82CABE40D0082D21C /* Intents */,
0E7E3D612B9345FD002BBDB4 /* Shared */,

View File

@ -26,8 +26,13 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:Passepartout/Passepartout.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"

View File

@ -25,24 +25,33 @@
import Foundation
public enum AppFeature: String, CaseIterable {
public enum AppFeature: String {
case appleTV
case interactiveLogin
case dns
case networkSettings
case httpProxy
case interactiveLogin
case onDemand
case providers
case routing
case sharing
case siri
public static let allCases: [AppFeature] = [
public static let fullVersionFeaturesV2: [AppFeature] = [
.dns,
.httpProxy,
.interactiveLogin,
.networkSettings,
.onDemand,
.providers,
.routing,
.sharing,
.siri
]
}

View File

@ -81,7 +81,7 @@ extension AppProduct: AppFeatureProviding {
return [.appleTV]
case .Features.networkSettings:
return [.networkSettings]
return [.dns, .httpProxy, .routing]
case .Features.siriShortcuts:
return [.siri]
@ -90,18 +90,18 @@ extension AppProduct: AppFeatureProviding {
return [.onDemand]
case .Full.allPlatforms:
return AppFeature.allCases
return AppFeature.fullVersionFeaturesV2
case .Full.iOS:
#if os(iOS)
return AppFeature.allCases
return AppFeature.fullVersionFeaturesV2
#else
return []
#endif
case .Full.macOS:
#if os(macOS)
return AppFeature.allCases
return AppFeature.fullVersionFeaturesV2
#else
return []
#endif

View File

@ -29,10 +29,10 @@ extension AppUserLevel: AppFeatureProviding {
var features: [AppFeature] {
switch self {
case .fullVersion:
return AppFeature.allCases
return AppFeature.fullVersionFeaturesV2
case .fullVersionPlusTV:
var list = AppFeature.allCases
var list = AppFeature.fullVersionFeaturesV2
list.append(.appleTV)
return list

View File

@ -103,11 +103,17 @@ private extension ProfileCoordinator {
private extension ProfileCoordinator {
func onNewModule(_ moduleType: ModuleType) {
switch moduleType {
case .onDemand:
break
case .dns:
paywallReason = iapManager.paywallReason(forFeature: .dns)
default:
paywallReason = iapManager.paywallReason(forFeature: .networkSettings)
case .httpProxy:
paywallReason = iapManager.paywallReason(forFeature: .httpProxy)
case .ip:
paywallReason = iapManager.paywallReason(forFeature: .routing)
case .onDemand, .openVPN, .wireGuard:
break
}
guard paywallReason == nil else {
return

View File

@ -62,7 +62,7 @@ extension ConnectionObserverTests {
XCTAssertEqual(sut.dataCount, nil)
try await tunnel.install(profile, connect: true, title: \.name)
try await Task.sleep(for: .milliseconds(200))
try await Task.sleep(for: .milliseconds(300))
XCTAssertEqual(sut.dataCount, dataCount)
}
}

View File

@ -52,7 +52,7 @@ extension IAPManagerTests {
return []
}
await sut.reloadReceipt()
XCTAssertTrue(sut.isEligible(for: AppFeature.allCases))
XCTAssertTrue(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
}
func test_givenBuildProducts_whenNewer_thenFreeVersion() async {
@ -65,7 +65,7 @@ extension IAPManagerTests {
return []
}
await sut.reloadReceipt()
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
}
// MARK: Eligibility
@ -74,13 +74,13 @@ extension IAPManagerTests {
let reader = MockReceiptReader()
let sut = IAPManager(receiptReader: reader)
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Full.allPlatforms])
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
await sut.reloadReceipt()
XCTAssertTrue(sut.isEligible(for: AppFeature.allCases))
XCTAssertTrue(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
}
func test_givenPurchasedFeatures_thenIsOnlyEligibleForFeatures() async {
@ -92,10 +92,13 @@ extension IAPManagerTests {
let sut = IAPManager(receiptReader: reader)
await sut.reloadReceipt()
XCTAssertTrue(sut.isEligible(for: .siri))
XCTAssertTrue(sut.isEligible(for: .networkSettings))
XCTAssertTrue(sut.isEligible(for: .dns))
XCTAssertTrue(sut.isEligible(for: .httpProxy))
XCTAssertFalse(sut.isEligible(for: .onDemand))
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertTrue(sut.isEligible(for: .routing))
XCTAssertFalse(sut.isEligible(for: .sharing))
XCTAssertTrue(sut.isEligible(for: .siri))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
}
func test_givenPurchasedAndCancelledFeature_thenIsNotEligible() async {
@ -108,7 +111,7 @@ extension IAPManagerTests {
let sut = IAPManager(receiptReader: reader)
await sut.reloadReceipt()
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
}
func test_givenFreeVersion_thenIsNotEligibleForAnyFeature() async {
@ -118,7 +121,7 @@ extension IAPManagerTests {
await sut.reloadReceipt()
XCTAssertFalse(sut.userLevel.isFullVersion)
AppFeature.allCases.forEach {
AppFeature.fullVersionFeaturesV2.forEach {
XCTAssertFalse(sut.isEligible(for: $0))
}
}
@ -138,7 +141,7 @@ extension IAPManagerTests {
let sut = IAPManager(receiptReader: reader)
await sut.reloadReceipt()
AppFeature.allCases.forEach {
AppFeature.fullVersionFeaturesV2.forEach {
XCTAssertTrue(sut.isEligible(for: $0))
}
XCTAssertFalse(sut.isEligible(for: .appleTV))
@ -160,11 +163,11 @@ extension IAPManagerTests {
#if os(macOS)
await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Full.macOS, .Features.networkSettings])
await sut.reloadReceipt()
XCTAssertTrue(sut.isEligible(for: AppFeature.allCases))
XCTAssertTrue(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
#else
await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Full.iOS, .Features.networkSettings])
await sut.reloadReceipt()
XCTAssertTrue(sut.isEligible(for: AppFeature.allCases))
XCTAssertTrue(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
#endif
}
@ -175,11 +178,11 @@ extension IAPManagerTests {
#if os(macOS)
await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Full.iOS, .Features.networkSettings])
await sut.reloadReceipt()
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
#else
await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Full.macOS, .Features.networkSettings])
await sut.reloadReceipt()
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
#endif
}
@ -264,7 +267,7 @@ extension IAPManagerTests {
let sut = IAPManager(customUserLevel: .beta, receiptReader: reader)
await sut.reloadReceipt()
XCTAssertFalse(sut.isEligible(for: AppFeature.allCases))
XCTAssertFalse(sut.isEligible(for: AppFeature.fullVersionFeaturesV2))
}
func test_givenBetaApp_thenIsEligibleForUnrestrictedFeature() async {
@ -272,7 +275,7 @@ extension IAPManagerTests {
let sut = IAPManager(customUserLevel: .beta, receiptReader: reader, unrestrictedFeatures: [.onDemand])
await sut.reloadReceipt()
AppFeature.allCases.forEach {
AppFeature.fullVersionFeaturesV2.forEach {
if $0 == .onDemand {
XCTAssertTrue(sut.isEligible(for: $0))
} else {
@ -302,7 +305,7 @@ extension IAPManagerTests {
let sut = IAPManager(customUserLevel: .fullVersion, receiptReader: reader)
await sut.reloadReceipt()
AppFeature.allCases.forEach {
AppFeature.fullVersionFeaturesV2.forEach {
XCTAssertTrue(sut.isEligible(for: $0))
}
XCTAssertFalse(sut.isEligible(for: .appleTV))
@ -313,7 +316,7 @@ extension IAPManagerTests {
let sut = IAPManager(customUserLevel: .fullVersionPlusTV, receiptReader: reader)
await sut.reloadReceipt()
AppFeature.allCases.forEach {
AppFeature.fullVersionFeaturesV2.forEach {
XCTAssertTrue(sut.isEligible(for: $0))
}
XCTAssertTrue(sut.isEligible(for: .appleTV))

View File

@ -0,0 +1,31 @@
{
"configurations" : [
{
"id" : "880EB747-73AE-45F8-B6D3-95D06B161AB1",
"name" : "Configuration 1",
"options" : {
}
}
],
"defaultOptions" : {
"testTimeoutsEnabled" : true
},
"testTargets" : [
{
"target" : {
"containerPath" : "container:Passepartout\/Library",
"identifier" : "AppLibraryTests",
"name" : "AppLibraryTests"
}
},
{
"target" : {
"containerPath" : "container:Passepartout\/Library",
"identifier" : "AppUITests",
"name" : "AppUITests"
}
}
],
"version" : 1
}

View File

@ -44,7 +44,7 @@ extension IAPManager {
customUserLevel: customUserLevel,
receiptReader: KvittoReceiptReader(),
// FIXME: #662, omit unrestrictedFeatures on release!
unrestrictedFeatures: [.interactiveLogin],
unrestrictedFeatures: [.interactiveLogin, .sharing],
productsAtBuild: productsAtBuild
)