From b45f9c23fed9b84565977741f56636af38973030 Mon Sep 17 00:00:00 2001 From: Davide Date: Wed, 20 Nov 2024 18:05:47 +0100 Subject: [PATCH] Rearrange targets for unit testing (#900) - Move and rename entities - Split protocols and default extensions - Move in-app entities to CommonIAP target --- .../xcschemes/Library-Package.xcscheme | 28 ++++++++++ Passepartout/Library/Package.resolved | 3 +- Passepartout/Library/Package.swift | 25 ++++++++- .../DefaultModuleViewFactory.swift | 0 .../AppUIMain/Business/MacSettingsModel.swift | 1 + ... => ProviderEntityViewProviding+VPN.swift} | 2 +- .../Extensions/DNSModule+Extensions.swift | 3 +- .../HTTPProxyModule+Extensions.swift | 3 +- .../Extensions/IPModule+Extensions.swift | 3 +- .../OnDemandModule+Extensions.swift | 3 +- .../Extensions/OpenVPNModule+Extensions.swift | 5 +- .../WireGuardModule+Extensions.swift | 5 +- .../Sources/AppUIMainWrapper/Dummy.swift | 0 .../Sources/AppUITVWrapper/Dummy.swift | 0 .../IAP => CommonIAP/Domain}/AppFeature.swift | 9 ---- .../Domain/AppFeatureProviding.swift | 30 +++++++++++ .../Domain/AppFeatureRequiring.swift | 30 +++++++++++ .../IAP => CommonIAP/Domain}/AppProduct.swift | 2 - .../Domain}/AppUserLevel.swift | 6 ++- .../Domain}/PaywallReason.swift | 0 .../Extensions/AppFeature+Full.swift | 37 ++++++++++++++ .../AppFeatureProviding+Levels.swift} | 28 +++++----- .../AppFeatureProviding+Products.swift} | 28 ++-------- .../Extensions}/AppProduct+Donations.swift | 0 .../Extensions}/AppProduct+Features.swift | 0 .../Extensions}/AppProduct+Providers.swift | 0 .../Strategy}/AppProductHelper.swift | 0 .../Strategy}/AppReceiptReader.swift | 0 .../Strategy/FakeAppProductHelper.swift} | 8 +-- .../Strategy/FakeAppReceiptReader.swift} | 4 +- .../{IAP => Business}/IAPManager.swift | 7 --- .../Domain/ProfileAttributes.swift | 1 - .../AppFeatureRequiring+Modules.swift} | 44 +--------------- .../AppFeatureRequiring+Profile.swift | 51 +++++++++++++++++++ .../AppFeatureRequiring+Providers.swift | 33 ++++++++++++ .../IAPManager+Verify.swift | 0 .../Module+ModuleType.swift | 0 ...> PassepartoutConfiguration+Logging.swift} | 2 +- .../Sources/CommonLibrary/Shared.swift | 1 + .../FallbackReceiptReader.swift | 1 - .../Extensions/Bundle+Extensions.swift | 12 +++++ .../Views}/AppWindow.swift | 0 .../PassepartoutImplementations/Dummy.swift | 25 --------- .../Extensions/ModuleBuilder+Previews.swift} | 4 +- .../Extensions/ModuleDraftEditing+UI.swift | 34 +++++++++++++ .../Extensions/ProfileManager+Editing.swift} | 4 +- ...nnelInstallationProviding+Extensions.swift | 50 ++++++++++++++++++ .../UILibrary/Mock/AppContext+Mock.swift | 4 +- .../Protocols/ModuleDraftEditing.swift | 10 +--- .../Protocols/ModuleViewFactory.swift | 2 +- .../Protocols/ModuleViewProviding.swift | 2 +- .../ProviderEntityViewProviding.swift | 2 +- .../TunnelInstallationProviding.swift | 23 --------- .../CommonLibraryTests/IAPManagerTests.swift | 43 ++++++++-------- 54 files changed, 410 insertions(+), 208 deletions(-) rename Passepartout/Library/Sources/AppUIMain/{Protocols => Business}/DefaultModuleViewFactory.swift (100%) rename Passepartout/Library/Sources/AppUIMain/Extensions/{ProviderEntityViewProviding+Extensions.swift => ProviderEntityViewProviding+VPN.swift} (97%) create mode 100644 Passepartout/Library/Sources/AppUIMainWrapper/Dummy.swift create mode 100644 Passepartout/Library/Sources/AppUITVWrapper/Dummy.swift rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Domain}/AppFeature.swift (88%) create mode 100644 Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureProviding.swift create mode 100644 Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureRequiring.swift rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Domain}/AppProduct.swift (97%) rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Domain}/AppUserLevel.swift (93%) rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Domain}/PaywallReason.swift (100%) create mode 100644 Passepartout/Library/Sources/CommonIAP/Extensions/AppFeature+Full.swift rename Passepartout/Library/Sources/{CommonLibrary/Extensions/Bundle+Extensions.swift => CommonIAP/Extensions/AppFeatureProviding+Levels.swift} (61%) rename Passepartout/Library/Sources/{CommonLibrary/IAP/AppFeatureProviding.swift => CommonIAP/Extensions/AppFeatureProviding+Products.swift} (76%) rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Extensions}/AppProduct+Donations.swift (100%) rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Extensions}/AppProduct+Features.swift (100%) rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Extensions}/AppProduct+Providers.swift (100%) rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Strategy}/AppProductHelper.swift (100%) rename Passepartout/Library/Sources/{CommonLibrary/IAP => CommonIAP/Strategy}/AppReceiptReader.swift (100%) rename Passepartout/Library/Sources/{CommonLibrary/Mock/MockAppProductHelper.swift => CommonIAP/Strategy/FakeAppProductHelper.swift} (92%) rename Passepartout/Library/Sources/{CommonLibrary/Mock/MockAppReceiptReader.swift => CommonIAP/Strategy/FakeAppReceiptReader.swift} (96%) rename Passepartout/Library/Sources/CommonLibrary/{IAP => Business}/IAPManager.swift (97%) rename Passepartout/Library/Sources/CommonLibrary/{IAP/AppFeatureRequiring.swift => Extensions/AppFeatureRequiring+Modules.swift} (61%) create mode 100644 Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Profile.swift create mode 100644 Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Providers.swift rename Passepartout/Library/Sources/CommonLibrary/{IAP => Extensions}/IAPManager+Verify.swift (100%) rename Passepartout/Library/Sources/CommonLibrary/{Domain => Extensions}/Module+ModuleType.swift (100%) rename Passepartout/Library/Sources/CommonLibrary/Extensions/{PassepartoutConfiguration+Extensions.swift => PassepartoutConfiguration+Logging.swift} (98%) rename Passepartout/Library/Sources/CommonLibrary/{IAP => Strategy}/FallbackReceiptReader.swift (99%) rename Passepartout/Library/Sources/{AppUIMain/Views/AppMenu/macOS => CommonUtils/Views}/AppWindow.swift (100%) rename Passepartout/Library/Sources/{AppUIMain/Extensions/EditableModule+Previews.swift => UILibrary/Extensions/ModuleBuilder+Previews.swift} (90%) create mode 100644 Passepartout/Library/Sources/UILibrary/Extensions/ModuleDraftEditing+UI.swift rename Passepartout/Library/Sources/{AppUIMain/Extensions/ProfileManager+Extensions.swift => UILibrary/Extensions/ProfileManager+Editing.swift} (92%) create mode 100644 Passepartout/Library/Sources/UILibrary/Extensions/TunnelInstallationProviding+Extensions.swift rename Passepartout/Library/Sources/{AppUIMain => UILibrary}/Protocols/ModuleDraftEditing.swift (88%) rename Passepartout/Library/Sources/{AppUIMain => UILibrary}/Protocols/ModuleViewFactory.swift (95%) rename Passepartout/Library/Sources/{AppUIMain => UILibrary}/Protocols/ModuleViewProviding.swift (96%) rename Passepartout/Library/Sources/{AppUIMain => UILibrary}/Protocols/ProviderEntityViewProviding.swift (96%) diff --git a/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/Library-Package.xcscheme b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/Library-Package.xcscheme index 2ffe8ff0..d7706a69 100644 --- a/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/Library-Package.xcscheme +++ b/Passepartout/Library/.swiftpm/xcode/xcshareddata/xcschemes/Library-Package.xcscheme @@ -105,6 +105,34 @@ ReferencedContainer = "container:"> + + + + + + + + some View { + public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { DNSView(editor: editor, module: self) } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift index 9593b339..0b331206 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/HTTPProxyModule+Extensions.swift @@ -25,9 +25,10 @@ import PassepartoutKit import SwiftUI +import UILibrary extension HTTPProxyModule.Builder: ModuleViewProviding { - func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { + public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { HTTPProxyView(editor: editor, module: self) } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/IPModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/IPModule+Extensions.swift index 2c0d0326..62b381f4 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/IPModule+Extensions.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/IPModule+Extensions.swift @@ -25,9 +25,10 @@ import PassepartoutKit import SwiftUI +import UILibrary extension IPModule.Builder: ModuleViewProviding { - func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { + public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { IPView(editor: editor, module: self) } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OnDemandModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OnDemandModule+Extensions.swift index 574f61eb..49d21ae7 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OnDemandModule+Extensions.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OnDemandModule+Extensions.swift @@ -25,9 +25,10 @@ import PassepartoutKit import SwiftUI +import UILibrary extension OnDemandModule.Builder: ModuleViewProviding { - func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { + public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { OnDemandView(editor: editor, module: self) } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift index 1d01fa91..c65128cd 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/OpenVPNModule+Extensions.swift @@ -26,15 +26,16 @@ import CommonUtils import PassepartoutKit import SwiftUI +import UILibrary extension OpenVPNModule.Builder: ModuleViewProviding { - func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { + public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { OpenVPNView(editor: editor, module: self, impl: impl as? OpenVPNModule.Implementation) } } extension OpenVPNModule: ProviderEntityViewProviding { - func providerEntityView( + public func providerEntityView( with provider: SerializedProvider, errorHandler: ErrorHandler, onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/WireGuardModule+Extensions.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/WireGuardModule+Extensions.swift index 712710b6..70c9a5fb 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/WireGuardModule+Extensions.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Modules/Extensions/WireGuardModule+Extensions.swift @@ -26,15 +26,16 @@ import CommonUtils import PassepartoutKit import SwiftUI +import UILibrary extension WireGuardModule.Builder: ModuleViewProviding { - func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { + public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View { WireGuardView(editor: editor, module: self, impl: impl as? WireGuardModule.Implementation) } } extension WireGuardModule: ProviderEntityViewProviding { - func providerEntityView( + public func providerEntityView( with provider: SerializedProvider, errorHandler: ErrorHandler, onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void diff --git a/Passepartout/Library/Sources/AppUIMainWrapper/Dummy.swift b/Passepartout/Library/Sources/AppUIMainWrapper/Dummy.swift new file mode 100644 index 00000000..e69de29b diff --git a/Passepartout/Library/Sources/AppUITVWrapper/Dummy.swift b/Passepartout/Library/Sources/AppUITVWrapper/Dummy.swift new file mode 100644 index 00000000..e69de29b diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppFeature.swift b/Passepartout/Library/Sources/CommonIAP/Domain/AppFeature.swift similarity index 88% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppFeature.swift rename to Passepartout/Library/Sources/CommonIAP/Domain/AppFeature.swift index 8b1a4508..1558c606 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/AppFeature.swift +++ b/Passepartout/Library/Sources/CommonIAP/Domain/AppFeature.swift @@ -41,15 +41,6 @@ public enum AppFeature: String, CaseIterable { case routing case sharing - - public static let fullV2Features: [AppFeature] = [ - .dns, - .httpProxy, - .onDemand, - .providers, - .routing, - .sharing - ] } extension AppFeature: Identifiable { diff --git a/Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureProviding.swift b/Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureProviding.swift new file mode 100644 index 00000000..dcab6cf4 --- /dev/null +++ b/Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureProviding.swift @@ -0,0 +1,30 @@ +// +// AppFeatureProviding.swift +// Passepartout +// +// Created by Davide De Rosa on 10/11/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation + +protocol AppFeatureProviding { + var features: [AppFeature] { get } +} diff --git a/Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureRequiring.swift b/Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureRequiring.swift new file mode 100644 index 00000000..f0f80b3a --- /dev/null +++ b/Passepartout/Library/Sources/CommonIAP/Domain/AppFeatureRequiring.swift @@ -0,0 +1,30 @@ +// +// AppFeatureRequiring.swift +// Passepartout +// +// Created by Davide De Rosa on 11/17/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation + +public protocol AppFeatureRequiring { + var features: Set { get } +} diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct.swift b/Passepartout/Library/Sources/CommonIAP/Domain/AppProduct.swift similarity index 97% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct.swift rename to Passepartout/Library/Sources/CommonIAP/Domain/AppProduct.swift index cf2d18d0..9cd5f942 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct.swift +++ b/Passepartout/Library/Sources/CommonIAP/Domain/AppProduct.swift @@ -23,9 +23,7 @@ // along with Passepartout. If not, see . // -import CommonUtils import Foundation -import PassepartoutKit public struct AppProduct: RawRepresentable, Hashable, Sendable { public let rawValue: String diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppUserLevel.swift b/Passepartout/Library/Sources/CommonIAP/Domain/AppUserLevel.swift similarity index 93% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppUserLevel.swift rename to Passepartout/Library/Sources/CommonIAP/Domain/AppUserLevel.swift index 17dc8af4..b2c6d4df 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/AppUserLevel.swift +++ b/Passepartout/Library/Sources/CommonIAP/Domain/AppUserLevel.swift @@ -35,8 +35,10 @@ public enum AppUserLevel: Int, Sendable { case fullV2 = 2 case subscriber = 3 +} - var isFullVersion: Bool { +extension AppUserLevel { + public var isFullVersion: Bool { switch self { case .fullV2, .subscriber: return true @@ -46,7 +48,7 @@ public enum AppUserLevel: Int, Sendable { } } - var isRestricted: Bool { + public var isRestricted: Bool { switch self { case .undefined, .beta: return true diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/PaywallReason.swift b/Passepartout/Library/Sources/CommonIAP/Domain/PaywallReason.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/IAP/PaywallReason.swift rename to Passepartout/Library/Sources/CommonIAP/Domain/PaywallReason.swift diff --git a/Passepartout/Library/Sources/CommonIAP/Extensions/AppFeature+Full.swift b/Passepartout/Library/Sources/CommonIAP/Extensions/AppFeature+Full.swift new file mode 100644 index 00000000..24ae0d26 --- /dev/null +++ b/Passepartout/Library/Sources/CommonIAP/Extensions/AppFeature+Full.swift @@ -0,0 +1,37 @@ +// +// AppFeature+Full.swift +// Passepartout +// +// Created by Davide De Rosa on 11/20/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation + +extension AppFeature { + public static let fullV2Features: [AppFeature] = [ + .dns, + .httpProxy, + .onDemand, + .providers, + .routing, + .sharing + ] +} diff --git a/Passepartout/Library/Sources/CommonLibrary/Extensions/Bundle+Extensions.swift b/Passepartout/Library/Sources/CommonIAP/Extensions/AppFeatureProviding+Levels.swift similarity index 61% rename from Passepartout/Library/Sources/CommonLibrary/Extensions/Bundle+Extensions.swift rename to Passepartout/Library/Sources/CommonIAP/Extensions/AppFeatureProviding+Levels.swift index b3764df4..1cfccf86 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Extensions/Bundle+Extensions.swift +++ b/Passepartout/Library/Sources/CommonIAP/Extensions/AppFeatureProviding+Levels.swift @@ -1,8 +1,8 @@ // -// Bundle+Extensions.swift +// AppFeatureProviding+Levels.swift // Passepartout // -// Created by Davide De Rosa on 8/27/24. +// Created by Davide De Rosa on 11/20/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,16 +25,20 @@ import Foundation -extension Bundle { - public func unsafeDecode(_ type: T.Type, filename: String) -> T { - guard let jsonURL = url(forResource: filename, withExtension: "json") else { - fatalError("Unable to find \(filename).json in bundle") - } - do { - let data = try Data(contentsOf: jsonURL) - return try JSONDecoder().decode(type, from: data) - } catch { - fatalError("Unable to decode \(filename).json: \(error)") +extension AppUserLevel: AppFeatureProviding { + public var features: [AppFeature] { + switch self { + case .beta: + return [.interactiveLogin, .sharing] + + case .fullV2: + return AppFeature.fullV2Features + + case .subscriber: + return AppFeature.allCases + + default: + return [] } } } diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppFeatureProviding.swift b/Passepartout/Library/Sources/CommonIAP/Extensions/AppFeatureProviding+Products.swift similarity index 76% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppFeatureProviding.swift rename to Passepartout/Library/Sources/CommonIAP/Extensions/AppFeatureProviding+Products.swift index 9622d6e6..f26f9de3 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/AppFeatureProviding.swift +++ b/Passepartout/Library/Sources/CommonIAP/Extensions/AppFeatureProviding+Products.swift @@ -1,8 +1,8 @@ // -// AppFeatureProviding.swift +// AppFeatureProviding+Products.swift // Passepartout // -// Created by Davide De Rosa on 10/11/24. +// Created by Davide De Rosa on 11/20/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -25,30 +25,8 @@ import Foundation -protocol AppFeatureProviding { - var features: [AppFeature] { get } -} - -extension AppUserLevel: AppFeatureProviding { - var features: [AppFeature] { - switch self { - case .beta: - return [.interactiveLogin, .sharing] - - case .fullV2: - return AppFeature.fullV2Features - - case .subscriber: - return AppFeature.allCases - - default: - return [] - } - } -} - extension AppProduct: AppFeatureProviding { - var features: [AppFeature] { + public var features: [AppFeature] { switch self { // MARK: Current diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct+Donations.swift b/Passepartout/Library/Sources/CommonIAP/Extensions/AppProduct+Donations.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct+Donations.swift rename to Passepartout/Library/Sources/CommonIAP/Extensions/AppProduct+Donations.swift diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct+Features.swift b/Passepartout/Library/Sources/CommonIAP/Extensions/AppProduct+Features.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct+Features.swift rename to Passepartout/Library/Sources/CommonIAP/Extensions/AppProduct+Features.swift diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct+Providers.swift b/Passepartout/Library/Sources/CommonIAP/Extensions/AppProduct+Providers.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppProduct+Providers.swift rename to Passepartout/Library/Sources/CommonIAP/Extensions/AppProduct+Providers.swift diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppProductHelper.swift b/Passepartout/Library/Sources/CommonIAP/Strategy/AppProductHelper.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppProductHelper.swift rename to Passepartout/Library/Sources/CommonIAP/Strategy/AppProductHelper.swift diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppReceiptReader.swift b/Passepartout/Library/Sources/CommonIAP/Strategy/AppReceiptReader.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppReceiptReader.swift rename to Passepartout/Library/Sources/CommonIAP/Strategy/AppReceiptReader.swift diff --git a/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppProductHelper.swift b/Passepartout/Library/Sources/CommonIAP/Strategy/FakeAppProductHelper.swift similarity index 92% rename from Passepartout/Library/Sources/CommonLibrary/Mock/MockAppProductHelper.swift rename to Passepartout/Library/Sources/CommonIAP/Strategy/FakeAppProductHelper.swift index 83853f8a..c693d8fc 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppProductHelper.swift +++ b/Passepartout/Library/Sources/CommonIAP/Strategy/FakeAppProductHelper.swift @@ -1,5 +1,5 @@ // -// MockAppProductHelper.swift +// FakeAppProductHelper.swift // Passepartout // // Created by Davide De Rosa on 12/19/23. @@ -27,12 +27,12 @@ import Combine import CommonUtils import Foundation -public actor MockAppProductHelper: AppProductHelper { +public actor FakeAppProductHelper: AppProductHelper { private let build: Int public private(set) var products: [AppProduct: InAppProduct] - public nonisolated let receiptReader: MockAppReceiptReader + public nonisolated let receiptReader: FakeAppReceiptReader private nonisolated let didUpdateSubject: PassthroughSubject @@ -40,7 +40,7 @@ public actor MockAppProductHelper: AppProductHelper { public init(build: Int = .max) { self.build = build products = [:] - receiptReader = MockAppReceiptReader() + receiptReader = FakeAppReceiptReader() didUpdateSubject = PassthroughSubject() } diff --git a/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppReceiptReader.swift b/Passepartout/Library/Sources/CommonIAP/Strategy/FakeAppReceiptReader.swift similarity index 96% rename from Passepartout/Library/Sources/CommonLibrary/Mock/MockAppReceiptReader.swift rename to Passepartout/Library/Sources/CommonIAP/Strategy/FakeAppReceiptReader.swift index 4e162975..40cc2ed6 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Mock/MockAppReceiptReader.swift +++ b/Passepartout/Library/Sources/CommonIAP/Strategy/FakeAppReceiptReader.swift @@ -1,5 +1,5 @@ // -// MockAppReceiptReader.swift +// FakeAppReceiptReader.swift // Passepartout // // Created by Davide De Rosa on 12/19/23. @@ -26,7 +26,7 @@ import CommonUtils import Foundation -public actor MockAppReceiptReader: AppReceiptReader { +public actor FakeAppReceiptReader: AppReceiptReader { private var localReceipt: InAppReceipt? public init(receipt localReceipt: InAppReceipt? = nil) { diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/IAPManager.swift b/Passepartout/Library/Sources/CommonLibrary/Business/IAPManager.swift similarity index 97% rename from Passepartout/Library/Sources/CommonLibrary/IAP/IAPManager.swift rename to Passepartout/Library/Sources/CommonLibrary/Business/IAPManager.swift index 0589f9d6..ed1f4509 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/IAPManager.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Business/IAPManager.swift @@ -128,13 +128,6 @@ extension IAPManager { features.allSatisfy(eligibleFeatures.contains) } - public func isEligible(forProvider providerId: ProviderID) -> Bool { - if providerId == .oeck { - return true - } - return isEligible(for: .providers) - } - public func isEligibleForFeedback() -> Bool { #if os(tvOS) false diff --git a/Passepartout/Library/Sources/CommonLibrary/Domain/ProfileAttributes.swift b/Passepartout/Library/Sources/CommonLibrary/Domain/ProfileAttributes.swift index 8b559464..da25e388 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Domain/ProfileAttributes.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Domain/ProfileAttributes.swift @@ -68,7 +68,6 @@ extension ProfileAttributes: CustomDebugStringConvertible { // MARK: - ProfileUserInfoTransformable -// FIXME: #570, test user info encoding/decoding with JSONSerialization extension ProfileAttributes: ProfileUserInfoTransformable { public var userInfo: [String: AnyHashable]? { do { diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/AppFeatureRequiring.swift b/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Modules.swift similarity index 61% rename from Passepartout/Library/Sources/CommonLibrary/IAP/AppFeatureRequiring.swift rename to Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Modules.swift index c53ec26d..d251ee55 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/AppFeatureRequiring.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Modules.swift @@ -1,8 +1,8 @@ // -// AppFeatureRequiring.swift +// AppFeatureRequiring+Modules.swift // Passepartout // -// Created by Davide De Rosa on 11/17/24. +// Created by Davide De Rosa on 11/20/24. // Copyright (c) 2024 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn @@ -26,38 +26,6 @@ import Foundation import PassepartoutKit -public protocol AppFeatureRequiring { - var features: Set { get } -} - -// MARK: - Profile - -extension Profile: AppFeatureRequiring { - public var features: Set { - let builders = activeModules.compactMap { module in - guard let builder = module.moduleBuilder() else { - fatalError("Cannot produce ModuleBuilder from Module: \(module)") - } - return builder - } - return builders.features - } -} - -extension Array: AppFeatureRequiring where Element == any ModuleBuilder { - public var features: Set { - let requirements = compactMap { builder in - guard let requiring = builder as? AppFeatureRequiring else { - fatalError("ModuleBuilder does not implement AppFeatureRequiring: \(builder)") - } - return requiring - } - return Set(requirements.flatMap(\.features)) - } -} - -// MARK: - Modules - extension DNSModule.Builder: AppFeatureRequiring { public var features: Set { [.dns] @@ -103,11 +71,3 @@ extension WireGuardModule.Builder: AppFeatureRequiring { providerId?.features ?? [] } } - -// MARK: - Providers - -extension ProviderID: AppFeatureRequiring { - public var features: Set { - self != .oeck ? [.providers] : [] - } -} diff --git a/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Profile.swift b/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Profile.swift new file mode 100644 index 00000000..608ac00d --- /dev/null +++ b/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Profile.swift @@ -0,0 +1,51 @@ +// +// AppFeatureRequiring+Profile.swift +// Passepartout +// +// Created by Davide De Rosa on 11/20/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit + +extension Profile: AppFeatureRequiring { + public var features: Set { + let builders = activeModules.compactMap { module in + guard let builder = module.moduleBuilder() else { + fatalError("Cannot produce ModuleBuilder from Module: \(module)") + } + return builder + } + return builders.features + } +} + +extension Array: AppFeatureRequiring where Element == any ModuleBuilder { + public var features: Set { + let requirements = compactMap { builder in + guard let requiring = builder as? AppFeatureRequiring else { + fatalError("ModuleBuilder does not implement AppFeatureRequiring: \(builder)") + } + return requiring + } + return Set(requirements.flatMap(\.features)) + } +} diff --git a/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Providers.swift b/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Providers.swift new file mode 100644 index 00000000..d7e51d5e --- /dev/null +++ b/Passepartout/Library/Sources/CommonLibrary/Extensions/AppFeatureRequiring+Providers.swift @@ -0,0 +1,33 @@ +// +// AppFeatureRequiring+Providers.swift +// Passepartout +// +// Created by Davide De Rosa on 11/20/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutKit + +extension ProviderID: AppFeatureRequiring { + public var features: Set { + self != .oeck ? [.providers] : [] + } +} diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/IAPManager+Verify.swift b/Passepartout/Library/Sources/CommonLibrary/Extensions/IAPManager+Verify.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/IAP/IAPManager+Verify.swift rename to Passepartout/Library/Sources/CommonLibrary/Extensions/IAPManager+Verify.swift diff --git a/Passepartout/Library/Sources/CommonLibrary/Domain/Module+ModuleType.swift b/Passepartout/Library/Sources/CommonLibrary/Extensions/Module+ModuleType.swift similarity index 100% rename from Passepartout/Library/Sources/CommonLibrary/Domain/Module+ModuleType.swift rename to Passepartout/Library/Sources/CommonLibrary/Extensions/Module+ModuleType.swift diff --git a/Passepartout/Library/Sources/CommonLibrary/Extensions/PassepartoutConfiguration+Extensions.swift b/Passepartout/Library/Sources/CommonLibrary/Extensions/PassepartoutConfiguration+Logging.swift similarity index 98% rename from Passepartout/Library/Sources/CommonLibrary/Extensions/PassepartoutConfiguration+Extensions.swift rename to Passepartout/Library/Sources/CommonLibrary/Extensions/PassepartoutConfiguration+Logging.swift index 19c24d26..c9666c2c 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Extensions/PassepartoutConfiguration+Extensions.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Extensions/PassepartoutConfiguration+Logging.swift @@ -1,5 +1,5 @@ // -// PassepartoutConfiguration+Extensions.swift +// PassepartoutConfiguration+Logging.swift // Passepartout // // Created by Davide De Rosa on 10/4/24. diff --git a/Passepartout/Library/Sources/CommonLibrary/Shared.swift b/Passepartout/Library/Sources/CommonLibrary/Shared.swift index cc591493..edbb4e39 100644 --- a/Passepartout/Library/Sources/CommonLibrary/Shared.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Shared.swift @@ -23,6 +23,7 @@ // along with Passepartout. If not, see . // +@_exported import CommonIAP import Foundation import PassepartoutKit diff --git a/Passepartout/Library/Sources/CommonLibrary/IAP/FallbackReceiptReader.swift b/Passepartout/Library/Sources/CommonLibrary/Strategy/FallbackReceiptReader.swift similarity index 99% rename from Passepartout/Library/Sources/CommonLibrary/IAP/FallbackReceiptReader.swift rename to Passepartout/Library/Sources/CommonLibrary/Strategy/FallbackReceiptReader.swift index 02ef5219..943a1302 100644 --- a/Passepartout/Library/Sources/CommonLibrary/IAP/FallbackReceiptReader.swift +++ b/Passepartout/Library/Sources/CommonLibrary/Strategy/FallbackReceiptReader.swift @@ -25,7 +25,6 @@ import CommonUtils import Foundation -import Kvitto import PassepartoutKit public actor FallbackReceiptReader: AppReceiptReader { diff --git a/Passepartout/Library/Sources/CommonUtils/Extensions/Bundle+Extensions.swift b/Passepartout/Library/Sources/CommonUtils/Extensions/Bundle+Extensions.swift index 071c14a8..635ef249 100644 --- a/Passepartout/Library/Sources/CommonUtils/Extensions/Bundle+Extensions.swift +++ b/Passepartout/Library/Sources/CommonUtils/Extensions/Bundle+Extensions.swift @@ -31,4 +31,16 @@ extension Bundle { .deletingLastPathComponent() .appendingPathComponent("receipt") // could be "sandboxReceipt" } + + public func unsafeDecode(_ type: T.Type, filename: String) -> T { + guard let jsonURL = url(forResource: filename, withExtension: "json") else { + fatalError("Unable to find \(filename).json in bundle") + } + do { + let data = try Data(contentsOf: jsonURL) + return try JSONDecoder().decode(type, from: data) + } catch { + fatalError("Unable to decode \(filename).json: \(error)") + } + } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/AppMenu/macOS/AppWindow.swift b/Passepartout/Library/Sources/CommonUtils/Views/AppWindow.swift similarity index 100% rename from Passepartout/Library/Sources/AppUIMain/Views/AppMenu/macOS/AppWindow.swift rename to Passepartout/Library/Sources/CommonUtils/Views/AppWindow.swift diff --git a/Passepartout/Library/Sources/PassepartoutImplementations/Dummy.swift b/Passepartout/Library/Sources/PassepartoutImplementations/Dummy.swift index 4034f54a..8b137891 100644 --- a/Passepartout/Library/Sources/PassepartoutImplementations/Dummy.swift +++ b/Passepartout/Library/Sources/PassepartoutImplementations/Dummy.swift @@ -1,26 +1 @@ -// -// Dummy.swift -// Passepartout -// -// Created by Davide De Rosa on 11/8/24. -// Copyright (c) 2024 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// -import Foundation diff --git a/Passepartout/Library/Sources/AppUIMain/Extensions/EditableModule+Previews.swift b/Passepartout/Library/Sources/UILibrary/Extensions/ModuleBuilder+Previews.swift similarity index 90% rename from Passepartout/Library/Sources/AppUIMain/Extensions/EditableModule+Previews.swift rename to Passepartout/Library/Sources/UILibrary/Extensions/ModuleBuilder+Previews.swift index 75042d7a..54189a31 100644 --- a/Passepartout/Library/Sources/AppUIMain/Extensions/EditableModule+Previews.swift +++ b/Passepartout/Library/Sources/UILibrary/Extensions/ModuleBuilder+Previews.swift @@ -29,7 +29,7 @@ import SwiftUI extension ModuleBuilder where Self: ModuleViewProviding { @MainActor - func preview(title: String = "") -> some View { + public func preview(title: String = "") -> some View { NavigationStack { moduleView(with: ProfileEditor(modules: [self]), impl: nil) .navigationTitle(title) @@ -38,7 +38,7 @@ extension ModuleBuilder where Self: ModuleViewProviding { } @MainActor - func preview(with content: (ProfileEditor, Self) -> C) -> some View { + public func preview(with content: (ProfileEditor, Self) -> C) -> some View { NavigationStack { content(ProfileEditor(modules: [self]), self) } diff --git a/Passepartout/Library/Sources/UILibrary/Extensions/ModuleDraftEditing+UI.swift b/Passepartout/Library/Sources/UILibrary/Extensions/ModuleDraftEditing+UI.swift new file mode 100644 index 00000000..ffccdd64 --- /dev/null +++ b/Passepartout/Library/Sources/UILibrary/Extensions/ModuleDraftEditing+UI.swift @@ -0,0 +1,34 @@ +// +// ModuleDraftEditing+UI.swift +// Passepartout +// +// Created by Davide De Rosa on 11/20/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import SwiftUI + +extension ModuleDraftEditing { + + @MainActor + public var draft: Binding { + editor[module] + } +} diff --git a/Passepartout/Library/Sources/AppUIMain/Extensions/ProfileManager+Extensions.swift b/Passepartout/Library/Sources/UILibrary/Extensions/ProfileManager+Editing.swift similarity index 92% rename from Passepartout/Library/Sources/AppUIMain/Extensions/ProfileManager+Extensions.swift rename to Passepartout/Library/Sources/UILibrary/Extensions/ProfileManager+Editing.swift index 8fd15a09..e4ac2e7f 100644 --- a/Passepartout/Library/Sources/AppUIMain/Extensions/ProfileManager+Extensions.swift +++ b/Passepartout/Library/Sources/UILibrary/Extensions/ProfileManager+Editing.swift @@ -1,5 +1,5 @@ // -// ProfileManager+Extensions.swift +// ProfileManager+Editing.swift // Passepartout // // Created by Davide De Rosa on 9/3/24. @@ -29,7 +29,7 @@ import PassepartoutKit @MainActor extension ProfileManager { - func removeProfiles(at offsets: IndexSet) async { + public func removeProfiles(at offsets: IndexSet) async { let idsToRemove = headers .enumerated() .filter { diff --git a/Passepartout/Library/Sources/UILibrary/Extensions/TunnelInstallationProviding+Extensions.swift b/Passepartout/Library/Sources/UILibrary/Extensions/TunnelInstallationProviding+Extensions.swift new file mode 100644 index 00000000..a0bf2955 --- /dev/null +++ b/Passepartout/Library/Sources/UILibrary/Extensions/TunnelInstallationProviding+Extensions.swift @@ -0,0 +1,50 @@ +// +// TunnelInstallationProviding+Extensions.swift +// Passepartout +// +// Created by Davide De Rosa on 11/20/24. +// Copyright (c) 2024 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import CommonLibrary +import Foundation +import PassepartoutKit + +@MainActor +extension TunnelInstallationProviding { + public var installation: TunnelInstallation? { + guard let currentProfile = tunnel.currentProfile else { + return nil + } + guard let header = profileManager.headers.first(where: { + $0.id == currentProfile.id + }) else { + return nil + } + return TunnelInstallation(header: header, onDemand: currentProfile.onDemand) + } + + public var currentProfile: Profile? { + guard let id = tunnel.currentProfile?.id else { + return nil + } + return profileManager.profile(withId: id) + } +} diff --git a/Passepartout/Library/Sources/UILibrary/Mock/AppContext+Mock.swift b/Passepartout/Library/Sources/UILibrary/Mock/AppContext+Mock.swift index 3ce98774..d13fb9ff 100644 --- a/Passepartout/Library/Sources/UILibrary/Mock/AppContext+Mock.swift +++ b/Passepartout/Library/Sources/UILibrary/Mock/AppContext+Mock.swift @@ -34,8 +34,8 @@ extension AppContext { public static func mock(withRegistry registry: Registry) -> AppContext { let iapManager = IAPManager( - inAppHelper: MockAppProductHelper(), - receiptReader: MockAppReceiptReader(), + inAppHelper: FakeAppProductHelper(), + receiptReader: FakeAppReceiptReader(), unrestrictedFeatures: [ .interactiveLogin, .onDemand diff --git a/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleDraftEditing.swift b/Passepartout/Library/Sources/UILibrary/Protocols/ModuleDraftEditing.swift similarity index 88% rename from Passepartout/Library/Sources/AppUIMain/Protocols/ModuleDraftEditing.swift rename to Passepartout/Library/Sources/UILibrary/Protocols/ModuleDraftEditing.swift index c50b5365..fa949cae 100644 --- a/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleDraftEditing.swift +++ b/Passepartout/Library/Sources/UILibrary/Protocols/ModuleDraftEditing.swift @@ -26,18 +26,10 @@ import PassepartoutKit import SwiftUI -protocol ModuleDraftEditing { +public protocol ModuleDraftEditing { associatedtype Draft: ModuleBuilder var editor: ProfileEditor { get } var module: Draft { get } } - -extension ModuleDraftEditing { - - @MainActor - var draft: Binding { - editor[module] - } -} diff --git a/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewFactory.swift b/Passepartout/Library/Sources/UILibrary/Protocols/ModuleViewFactory.swift similarity index 95% rename from Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewFactory.swift rename to Passepartout/Library/Sources/UILibrary/Protocols/ModuleViewFactory.swift index bbb15084..620b3c33 100644 --- a/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewFactory.swift +++ b/Passepartout/Library/Sources/UILibrary/Protocols/ModuleViewFactory.swift @@ -26,7 +26,7 @@ import Foundation import SwiftUI -protocol ModuleViewFactory: AnyObject { +public protocol ModuleViewFactory: AnyObject { associatedtype Content: View @MainActor diff --git a/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewProviding.swift b/Passepartout/Library/Sources/UILibrary/Protocols/ModuleViewProviding.swift similarity index 96% rename from Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewProviding.swift rename to Passepartout/Library/Sources/UILibrary/Protocols/ModuleViewProviding.swift index 36c2a7ac..e0b07b86 100644 --- a/Passepartout/Library/Sources/AppUIMain/Protocols/ModuleViewProviding.swift +++ b/Passepartout/Library/Sources/UILibrary/Protocols/ModuleViewProviding.swift @@ -26,7 +26,7 @@ import PassepartoutKit import SwiftUI -protocol ModuleViewProviding { +public protocol ModuleViewProviding { associatedtype Content: View @MainActor diff --git a/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift b/Passepartout/Library/Sources/UILibrary/Protocols/ProviderEntityViewProviding.swift similarity index 96% rename from Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift rename to Passepartout/Library/Sources/UILibrary/Protocols/ProviderEntityViewProviding.swift index d63892e3..18e10713 100644 --- a/Passepartout/Library/Sources/AppUIMain/Protocols/ProviderEntityViewProviding.swift +++ b/Passepartout/Library/Sources/UILibrary/Protocols/ProviderEntityViewProviding.swift @@ -27,7 +27,7 @@ import CommonUtils import PassepartoutKit import SwiftUI -protocol ProviderEntityViewProviding { +public protocol ProviderEntityViewProviding { associatedtype EntityContent: View @MainActor diff --git a/Passepartout/Library/Sources/UILibrary/Protocols/TunnelInstallationProviding.swift b/Passepartout/Library/Sources/UILibrary/Protocols/TunnelInstallationProviding.swift index 5f5bf499..d895846f 100644 --- a/Passepartout/Library/Sources/UILibrary/Protocols/TunnelInstallationProviding.swift +++ b/Passepartout/Library/Sources/UILibrary/Protocols/TunnelInstallationProviding.swift @@ -25,32 +25,9 @@ import CommonLibrary import Foundation -import PassepartoutKit public protocol TunnelInstallationProviding { var profileManager: ProfileManager { get } var tunnel: ExtendedTunnel { get } } - -@MainActor -extension TunnelInstallationProviding { - public var installation: TunnelInstallation? { - guard let currentProfile = tunnel.currentProfile else { - return nil - } - guard let header = profileManager.headers.first(where: { - $0.id == currentProfile.id - }) else { - return nil - } - return TunnelInstallation(header: header, onDemand: currentProfile.onDemand) - } - - public var currentProfile: Profile? { - guard let id = tunnel.currentProfile?.id else { - return nil - } - return profileManager.profile(withId: id) - } -} diff --git a/Passepartout/Library/Tests/CommonLibraryTests/IAPManagerTests.swift b/Passepartout/Library/Tests/CommonLibraryTests/IAPManagerTests.swift index 219c5668..53968a29 100644 --- a/Passepartout/Library/Tests/CommonLibraryTests/IAPManagerTests.swift +++ b/Passepartout/Library/Tests/CommonLibraryTests/IAPManagerTests.swift @@ -23,13 +23,14 @@ // along with Passepartout. If not, see . // +@testable import CommonIAP @testable import CommonLibrary import CommonUtils import Foundation import XCTest final class IAPManagerTests: XCTestCase { -// private let inApp = MockAppProductHelper() +// private let inApp = FakeAppProductHelper() private let olderBuildNumber = 500 @@ -44,7 +45,7 @@ extension IAPManagerTests { // MARK: Build products func test_givenBuildProducts_whenOlder_thenFullVersion() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt(withBuild: olderBuildNumber, identifiers: []) let sut = IAPManager(receiptReader: reader) { build in if build <= self.defaultBuildNumber { @@ -57,7 +58,7 @@ extension IAPManagerTests { } func test_givenBuildProducts_whenNewer_thenFreeVersion() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt(withBuild: newerBuildNumber, products: []) let sut = IAPManager(receiptReader: reader) { build in if build <= self.defaultBuildNumber { @@ -72,7 +73,7 @@ extension IAPManagerTests { // MARK: Eligibility func test_givenPurchasedFeature_whenReloadReceipt_thenIsEligible() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(receiptReader: reader) XCTAssertFalse(sut.isEligible(for: AppFeature.fullV2Features)) @@ -85,7 +86,7 @@ extension IAPManagerTests { } func test_givenPurchasedFeatures_thenIsOnlyEligibleForFeatures() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt(withBuild: defaultBuildNumber, products: [ .Features.networkSettings ]) @@ -101,7 +102,7 @@ extension IAPManagerTests { } func test_givenPurchasedAndCancelledFeature_thenIsNotEligible() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt( withBuild: defaultBuildNumber, products: [.Full.allPlatforms], @@ -114,7 +115,7 @@ extension IAPManagerTests { } func test_givenFreeVersion_thenIsNotEligibleForAnyFeature() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt(withBuild: defaultBuildNumber, products: []) let sut = IAPManager(receiptReader: reader) @@ -126,7 +127,7 @@ extension IAPManagerTests { } func test_givenFreeVersion_thenIsNotEligibleForAppleTV() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt(withBuild: defaultBuildNumber, products: []) let sut = IAPManager(receiptReader: reader) @@ -135,7 +136,7 @@ extension IAPManagerTests { } func test_givenFullV2Version_thenIsEligibleForAnyFeatureExceptExcluded() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Full.allPlatforms]) let sut = IAPManager(receiptReader: reader) @@ -155,7 +156,7 @@ extension IAPManagerTests { } func test_givenAppleTV_thenIsEligibleForAppleTV() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Features.appleTV]) let sut = IAPManager(receiptReader: reader) @@ -164,7 +165,7 @@ extension IAPManagerTests { } func test_givenPlatformVersion_thenIsFullVersionForPlatform() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(receiptReader: reader) #if os(macOS) @@ -179,7 +180,7 @@ extension IAPManagerTests { } func test_givenPlatformVersion_thenIsNotFullVersionForOtherPlatform() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(receiptReader: reader) #if os(macOS) @@ -196,7 +197,7 @@ extension IAPManagerTests { // MARK: App level func test_givenBetaApp_thenIsRestricted() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .beta, receiptReader: reader) await sut.reloadReceipt() @@ -204,7 +205,7 @@ extension IAPManagerTests { } func test_givenBetaApp_thenIsNotEligibleForAllFeatures() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .beta, receiptReader: reader) await sut.reloadReceipt() @@ -212,7 +213,7 @@ extension IAPManagerTests { } func test_givenBetaApp_thenIsEligibleForUserLevelFeatures() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .beta, receiptReader: reader) let eligible = AppUserLevel.beta.features @@ -222,7 +223,7 @@ extension IAPManagerTests { } func test_givenBetaApp_thenIsEligibleForUnrestrictedFeature() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .beta, receiptReader: reader, unrestrictedFeatures: [.onDemand]) var eligible = AppUserLevel.beta.features @@ -233,7 +234,7 @@ extension IAPManagerTests { } func test_givenFullV2App_thenIsFullVersion() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .fullV2, receiptReader: reader) await sut.reloadReceipt() @@ -241,7 +242,7 @@ extension IAPManagerTests { } func test_givenSubscriberApp_thenIsFullVersion() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .subscriber, receiptReader: reader) await sut.reloadReceipt() @@ -249,7 +250,7 @@ extension IAPManagerTests { } func test_givenFullV2App_thenIsEligibleForAnyFeatureExceptExcluded() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .fullV2, receiptReader: reader) await sut.reloadReceipt() @@ -268,7 +269,7 @@ extension IAPManagerTests { } func test_givenSubscriberApp_thenIsEligibleForAnyFeature() async { - let reader = MockAppReceiptReader() + let reader = FakeAppReceiptReader() let sut = IAPManager(customUserLevel: .subscriber, receiptReader: reader) await sut.reloadReceipt() @@ -288,7 +289,7 @@ private extension IAPManager { ) { self.init( customUserLevel: customUserLevel, - inAppHelper: MockAppProductHelper(), + inAppHelper: FakeAppProductHelper(), receiptReader: receiptReader, unrestrictedFeatures: unrestrictedFeatures, productsAtBuild: productsAtBuild