Refactor static functions/entities in Library (#679)
Reduce the impact of hidden dependencies on BundleConfiguration and Constants.shared Fixes #656
This commit is contained in:
parent
4b0bc7f064
commit
5fb6f4f4d2
|
@ -32,8 +32,7 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
||||
"state" : {
|
||||
"revision" : "2e61214462dcf6ad9e211d8fdbd611c6755845c4",
|
||||
"version" : "0.8.0"
|
||||
"revision" : "779910e268e79f1004a95285ac2485255d88bb21"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -70,9 +70,10 @@ private extension PassepartoutApp {
|
|||
registry: context.registry
|
||||
)
|
||||
.onLoad {
|
||||
CommonLibrary.configureLogging(
|
||||
PassepartoutConfiguration.shared.configureLogging(
|
||||
to: BundleConfiguration.urlForAppLog,
|
||||
parameters: Constants.shared.log
|
||||
parameters: Constants.shared.log,
|
||||
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
|
||||
)
|
||||
AppUI.configure(with: context)
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ let package = Package(
|
|||
)
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.8.0"),
|
||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "2e61214462dcf6ad9e211d8fdbd611c6755845c4"),
|
||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.8.0"),
|
||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "779910e268e79f1004a95285ac2485255d88bb21"),
|
||||
// .package(path: "../../../passepartoutkit-source"),
|
||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.8.0"),
|
||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
|
||||
|
|
|
@ -30,8 +30,6 @@ import PassepartoutKit
|
|||
import UtilsLibrary
|
||||
|
||||
extension AppData {
|
||||
|
||||
// TODO: #656, make non-static
|
||||
public static func cdProfileRepositoryV3(
|
||||
registry: Registry,
|
||||
coder: ProfileCoder,
|
||||
|
|
|
@ -26,11 +26,7 @@
|
|||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
// TODO: #656, make non-static
|
||||
public struct AppUI {
|
||||
private init() {
|
||||
}
|
||||
|
||||
public enum AppUI {
|
||||
public static func configure(with context: AppContext) {
|
||||
assertMissingModuleImplementations()
|
||||
}
|
||||
|
@ -40,11 +36,11 @@ private extension AppUI {
|
|||
static func assertMissingModuleImplementations() {
|
||||
ModuleType.allCases.forEach { moduleType in
|
||||
let module = moduleType.newModule()
|
||||
guard module as? ModuleTypeProviding != nil else {
|
||||
fatalError("\(moduleType): does not implement ModuleTypeProviding")
|
||||
guard module is ModuleTypeProviding else {
|
||||
fatalError("\(moduleType): is not ModuleTypeProviding")
|
||||
}
|
||||
guard module as? any ModuleViewProviding != nil else {
|
||||
fatalError("\(moduleType): does not implement ModuleViewProviding")
|
||||
guard module is any ModuleViewProviding else {
|
||||
fatalError("\(moduleType): is not ModuleViewProviding")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Issue+App.swift
|
||||
// Issue+Metadata.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 9/18/24.
|
||||
|
@ -28,23 +28,35 @@ import Foundation
|
|||
import PassepartoutKit
|
||||
|
||||
extension Issue {
|
||||
struct Metadata {
|
||||
let configuration: PassepartoutConfiguration
|
||||
|
||||
// TODO: #656, make non-static
|
||||
static func with(versionString: String, purchasedProducts: Set<AppProduct>, tunnel: Tunnel) async -> Self {
|
||||
let appLog = CommonLibrary.currentLog(parameters: Constants.shared.log)
|
||||
let versionString: String
|
||||
|
||||
let purchasedProducts: Set<AppProduct>
|
||||
|
||||
let tunnel: Tunnel
|
||||
|
||||
let urlForTunnelLog: URL
|
||||
|
||||
let parameters: Constants.Log
|
||||
}
|
||||
|
||||
static func withMetadata(_ metadata: Metadata) async -> Issue {
|
||||
let appLog = metadata.configuration.currentLog(parameters: metadata.parameters)
|
||||
.joined(separator: "\n")
|
||||
.data(using: .utf8)
|
||||
|
||||
let tunnelLog: Data?
|
||||
|
||||
// live tunnel log
|
||||
if await tunnel.status != .inactive {
|
||||
tunnelLog = await tunnel.currentLog(parameters: Constants.shared.log)
|
||||
if await metadata.tunnel.status != .inactive {
|
||||
tunnelLog = await metadata.tunnel.currentLog(parameters: metadata.parameters)
|
||||
.joined(separator: "\n")
|
||||
.data(using: .utf8)
|
||||
}
|
||||
// latest persisted tunnel log
|
||||
else if let latestTunnelEntry = CommonLibrary.availableLogs(at: BundleConfiguration.urlForTunnelLog)
|
||||
else if let latestTunnelEntry = metadata.configuration.availableLogs(at: metadata.urlForTunnelLog)
|
||||
.max(by: { $0.key < $1.key }) {
|
||||
|
||||
tunnelLog = try? Data(contentsOf: latestTunnelEntry.value)
|
||||
|
@ -55,13 +67,15 @@ extension Issue {
|
|||
}
|
||||
|
||||
return Issue(
|
||||
appLine: "\(Strings.Unlocalized.appName) \(versionString)",
|
||||
purchasedProducts: purchasedProducts,
|
||||
appLine: "\(Strings.Unlocalized.appName) \(metadata.versionString)",
|
||||
purchasedProducts: metadata.purchasedProducts,
|
||||
appLog: appLog,
|
||||
tunnelLog: tunnelLog
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Issue {
|
||||
var to: String {
|
||||
Constants.shared.emails.issues
|
||||
}
|
|
@ -29,22 +29,18 @@ import SwiftUI
|
|||
import UtilsLibrary
|
||||
|
||||
extension DebugLogView {
|
||||
|
||||
// TODO: #656, make non-static
|
||||
static func withApp(parameters: Constants.Log) -> DebugLogView {
|
||||
DebugLogView {
|
||||
CommonLibrary.currentLog(parameters: parameters)
|
||||
PassepartoutConfiguration.shared.currentLog(parameters: parameters)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: #656, make non-static
|
||||
static func withTunnel(_ tunnel: Tunnel, parameters: Constants.Log) -> DebugLogView {
|
||||
DebugLogView {
|
||||
await tunnel.currentLog(parameters: parameters)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: #656, make non-static
|
||||
static func withURL(_ url: URL) -> DebugLogView {
|
||||
DebugLogView {
|
||||
do {
|
||||
|
|
|
@ -47,11 +47,11 @@ struct DiagnosticsView: View {
|
|||
@EnvironmentObject
|
||||
var iapManager: IAPManager
|
||||
|
||||
@AppStorage(AppPreference.logsPrivateData.key, store: .group)
|
||||
@AppStorage(AppPreference.logsPrivateData.key, store: .appGroup)
|
||||
private var logsPrivateData = false
|
||||
|
||||
var availableTunnelLogs: () -> [LogEntry] = {
|
||||
CommonLibrary.availableLogs(at: BundleConfiguration.urlForTunnelLog)
|
||||
PassepartoutConfiguration.shared.availableLogs(at: BundleConfiguration.urlForTunnelLog)
|
||||
.sorted {
|
||||
$0.key > $1.key
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#if os(iOS)
|
||||
|
||||
import CommonLibrary
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
@ -33,24 +34,7 @@ import UtilsLibrary
|
|||
extension ReportIssueButton: View {
|
||||
var body: some View {
|
||||
HStack {
|
||||
Button(title) {
|
||||
Task {
|
||||
isPending = true
|
||||
defer {
|
||||
isPending = false
|
||||
}
|
||||
let issue = await Issue.with(
|
||||
versionString: BundleConfiguration.mainVersionString,
|
||||
purchasedProducts: purchasedProducts,
|
||||
tunnel: tunnel
|
||||
)
|
||||
guard MailComposerView.canSendMail() else {
|
||||
openMailTo(with: issue)
|
||||
return
|
||||
}
|
||||
issueBeingReported = issue
|
||||
}
|
||||
}
|
||||
Button(title, action: sendEmail)
|
||||
if isPending {
|
||||
Spacer()
|
||||
ProgressView()
|
||||
|
@ -75,6 +59,41 @@ extension ReportIssueButton: View {
|
|||
}
|
||||
}
|
||||
|
||||
private extension ReportIssueButton {
|
||||
func sendEmail() {
|
||||
Task {
|
||||
isPending = true
|
||||
defer {
|
||||
isPending = false
|
||||
}
|
||||
let issue = await Issue.withMetadata(.init(
|
||||
configuration: .shared,
|
||||
versionString: BundleConfiguration.mainVersionString,
|
||||
purchasedProducts: purchasedProducts,
|
||||
tunnel: tunnel,
|
||||
urlForTunnelLog: BundleConfiguration.urlForTunnelLog,
|
||||
parameters: Constants.shared.log
|
||||
))
|
||||
guard MailComposerView.canSendMail() else {
|
||||
openMailTo(with: issue)
|
||||
return
|
||||
}
|
||||
issueBeingReported = issue
|
||||
}
|
||||
}
|
||||
|
||||
func openMailTo(with issue: Issue) {
|
||||
guard let url = URL.mailto(to: issue.to, subject: issue.subject, body: issue.body) else {
|
||||
return
|
||||
}
|
||||
guard UIApplication.shared.canOpenURL(url) else {
|
||||
isUnableToEmail = true
|
||||
return
|
||||
}
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
private extension Issue {
|
||||
var attachments: [MailComposerView.Attachment] {
|
||||
var list: [MailComposerView.Attachment] = []
|
||||
|
@ -89,17 +108,4 @@ private extension Issue {
|
|||
}
|
||||
}
|
||||
|
||||
private extension ReportIssueButton {
|
||||
func openMailTo(with issue: Issue) {
|
||||
guard let url = URL.mailto(to: issue.to, subject: issue.subject, body: issue.body) else {
|
||||
return
|
||||
}
|
||||
guard UIApplication.shared.canOpenURL(url) else {
|
||||
isUnableToEmail = true
|
||||
return
|
||||
}
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,26 +25,39 @@
|
|||
|
||||
#if os(macOS)
|
||||
|
||||
import CommonLibrary
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
extension ReportIssueButton: View {
|
||||
var body: some View {
|
||||
Button(title) {
|
||||
Button(title, action: sendEmail)
|
||||
.disabled(isPending)
|
||||
}
|
||||
}
|
||||
|
||||
private extension ReportIssueButton {
|
||||
func sendEmail() {
|
||||
Task {
|
||||
guard let service = NSSharingService(named: .composeEmail) else {
|
||||
isUnableToEmail = true
|
||||
return
|
||||
}
|
||||
Task {
|
||||
let issue = await Issue.with(
|
||||
versionString: BundleConfiguration.mainVersionString,
|
||||
purchasedProducts: purchasedProducts,
|
||||
tunnel: tunnel
|
||||
)
|
||||
service.recipients = [issue.to]
|
||||
service.subject = issue.subject
|
||||
service.perform(withItems: issue.items)
|
||||
isPending = true
|
||||
defer {
|
||||
isPending = false
|
||||
}
|
||||
let issue = await Issue.withMetadata(.init(
|
||||
configuration: .shared,
|
||||
versionString: BundleConfiguration.mainVersionString,
|
||||
purchasedProducts: purchasedProducts,
|
||||
tunnel: tunnel,
|
||||
urlForTunnelLog: BundleConfiguration.urlForTunnelLog,
|
||||
parameters: Constants.shared.log
|
||||
))
|
||||
service.recipients = [issue.to]
|
||||
service.subject = issue.subject
|
||||
service.perform(withItems: issue.items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//
|
||||
// CommonLibrary.swift
|
||||
// PassepartoutConfiguration+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 8/31/24.
|
||||
// Created by Davide De Rosa on 10/4/24.
|
||||
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
|
@ -26,24 +26,20 @@
|
|||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
// TODO: #656, make non-static
|
||||
public struct CommonLibrary {
|
||||
private init() {
|
||||
}
|
||||
|
||||
public static func configureLogging(to url: URL, parameters: Constants.Log) {
|
||||
extension PassepartoutConfiguration {
|
||||
public func configureLogging(to url: URL, parameters: Constants.Log, logsPrivateData: Bool) {
|
||||
pp_log(.common, .debug, "Log to: \(url)")
|
||||
|
||||
PassepartoutConfiguration.shared.setLocalLogger(options: .init(
|
||||
setLocalLogger(options: .init(
|
||||
url: url,
|
||||
maxNumberOfLines: parameters.maxNumberOfLines,
|
||||
maxLevel: parameters.maxLevel,
|
||||
mapper: parameters.formatter.formattedLine
|
||||
))
|
||||
|
||||
if UserDefaults.group.bool(forKey: AppPreference.logsPrivateData.key) {
|
||||
PassepartoutConfiguration.shared.logsAddresses = true
|
||||
PassepartoutConfiguration.shared.logsModules = true
|
||||
if logsPrivateData {
|
||||
logsAddresses = true
|
||||
logsModules = true
|
||||
}
|
||||
|
||||
if let maxAge = parameters.maxAge {
|
||||
|
@ -51,15 +47,15 @@ public struct CommonLibrary {
|
|||
}
|
||||
}
|
||||
|
||||
public static func currentLog(parameters: Constants.Log) -> [String] {
|
||||
PassepartoutConfiguration.shared.currentLogLines(
|
||||
public func currentLog(parameters: Constants.Log) -> [String] {
|
||||
currentLogLines(
|
||||
sinceLast: parameters.sinceLast,
|
||||
maxLevel: parameters.maxLevel
|
||||
)
|
||||
.map(parameters.formatter.formattedLine)
|
||||
}
|
||||
|
||||
public static func availableLogs(at url: URL) -> [Date: URL] {
|
||||
public func availableLogs(at url: URL) -> [Date: URL] {
|
||||
let parent = url.deletingLastPathComponent()
|
||||
let prefix = url.lastPathComponent
|
||||
do {
|
||||
|
@ -81,13 +77,13 @@ public struct CommonLibrary {
|
|||
}
|
||||
}
|
||||
|
||||
public static func flushLog() {
|
||||
try? PassepartoutConfiguration.shared.saveLog()
|
||||
public func flushLog() {
|
||||
try? saveLog()
|
||||
}
|
||||
}
|
||||
|
||||
private extension CommonLibrary {
|
||||
static func purgeLogs(at url: URL, beyond maxAge: TimeInterval) {
|
||||
private extension PassepartoutConfiguration {
|
||||
func purgeLogs(at url: URL, beyond maxAge: TimeInterval) {
|
||||
let logs = availableLogs(at: url)
|
||||
let minDate = Date().addingTimeInterval(-maxAge)
|
||||
logs.forEach { date, url in
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// BundleConfiguration+AppGroup.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 10/4/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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
// WARNING: beware of Constants.shared dependency
|
||||
|
||||
extension BundleConfiguration {
|
||||
public static var urlForAppLog: URL {
|
||||
cachesURL.appending(path: Constants.shared.log.appPath)
|
||||
}
|
||||
|
||||
public static var urlForTunnelLog: URL {
|
||||
cachesURL.appending(path: Constants.shared.log.tunnelPath)
|
||||
}
|
||||
}
|
||||
|
||||
private extension BundleConfiguration {
|
||||
static var cachesURL: URL {
|
||||
let groupId = mainString(for: .groupId)
|
||||
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupId) else {
|
||||
fatalError("Unable to access App Group container")
|
||||
}
|
||||
return url.appending(components: "Library", "Caches")
|
||||
}
|
||||
}
|
|
@ -26,7 +26,8 @@
|
|||
import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
// TODO: #656, make non-static
|
||||
// WARNING: beware of Constants.shared dependency
|
||||
|
||||
extension BundleConfiguration {
|
||||
public enum BundleKey: String {
|
||||
case appStoreId
|
||||
|
@ -92,21 +93,13 @@ extension BundleConfiguration {
|
|||
}
|
||||
return url
|
||||
}
|
||||
|
||||
public static var urlForAppLog: URL {
|
||||
cachesURL.appending(path: Constants.shared.log.appPath)
|
||||
}
|
||||
|
||||
public static var urlForTunnelLog: URL {
|
||||
cachesURL.appending(path: Constants.shared.log.tunnelPath)
|
||||
}
|
||||
}
|
||||
|
||||
private extension BundleConfiguration {
|
||||
|
||||
// WARNING: fails from package itself, e.g. in previews
|
||||
static var main: BundleConfiguration {
|
||||
guard let bundle = BundleConfiguration(.main, key: Constants.shared.bundle) else {
|
||||
guard let bundle = BundleConfiguration(.main, key: Constants.shared.bundleKey) else {
|
||||
fatalError("Missing main bundle")
|
||||
}
|
||||
return bundle
|
||||
|
@ -115,12 +108,4 @@ private extension BundleConfiguration {
|
|||
static var isPreview: Bool {
|
||||
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
|
||||
}
|
||||
|
||||
static var cachesURL: URL {
|
||||
let groupId = mainString(for: .groupId)
|
||||
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupId) else {
|
||||
fatalError("Unable to access App Group container")
|
||||
}
|
||||
return url.appending(components: "Library", "Caches")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ public struct Constants: Decodable, Sendable {
|
|||
public let maxAge: TimeInterval?
|
||||
}
|
||||
|
||||
public let bundle: String
|
||||
public let bundleKey: String
|
||||
|
||||
public let websites: Websites
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"bundle": "AppConfig",
|
||||
"bundleKey": "AppConfig",
|
||||
"websites": {
|
||||
"home": "https://passepartoutvpn.app",
|
||||
"subreddit": "https://www.reddit.com/r/passepartout/",
|
||||
|
|
|
@ -31,7 +31,7 @@ extension LoggerDestination {
|
|||
}
|
||||
|
||||
extension UserDefaults {
|
||||
public static let group: UserDefaults = {
|
||||
public static let appGroup: UserDefaults = {
|
||||
let appGroup = BundleConfiguration.mainString(for: .groupId)
|
||||
guard let defaults = UserDefaults(suiteName: appGroup) else {
|
||||
fatalError("No access to App Group: \(appGroup)")
|
||||
|
|
|
@ -219,7 +219,7 @@ private extension GenericCreditsView.LicenseView {
|
|||
do {
|
||||
let session = URLSession(configuration: .ephemeral)
|
||||
let response = try await session.data(from: url)
|
||||
let string = String(decoding: response.0, as: UTF8.self)
|
||||
let string = String(data: response.0, encoding: .utf8)
|
||||
withAnimation {
|
||||
content = string
|
||||
}
|
||||
|
|
|
@ -188,70 +188,8 @@ extension IAPManagerTests {
|
|||
|
||||
// MARK: Purchasable
|
||||
|
||||
// func test_givenNoPurchase_thenCanBuyFullAndPlatformVersion() {
|
||||
// let reader = MockReceiptReader()
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
//
|
||||
//#if targetEnvironment(macCatalyst)
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion, .fullVersion_macOS])
|
||||
//#else
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion, .fullVersion_iOS])
|
||||
//#endif
|
||||
// }
|
||||
//
|
||||
// func test_givenFullVersion_thenCannotPurchase() {
|
||||
// let reader = MockReceiptReader()
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
//
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
|
||||
// }
|
||||
//
|
||||
// func test_givenPlatformVersion_thenCannotPurchaseSamePlatform() {
|
||||
// let reader = MockReceiptReader()
|
||||
//
|
||||
//#if targetEnvironment(macCatalyst)
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_macOS])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
|
||||
//#else
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_iOS])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [])
|
||||
//#endif
|
||||
// }
|
||||
//
|
||||
// func test_givenOtherPlatformVersion_thenCanOnlyPurchaseMissingPlatform() {
|
||||
// let reader = MockReceiptReader()
|
||||
//
|
||||
//#if targetEnvironment(macCatalyst)
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_iOS])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_macOS])
|
||||
//#else
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [.fullVersion_macOS])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: nil), [.fullVersion_iOS])
|
||||
//#endif
|
||||
// }
|
||||
//
|
||||
// func test_givenAppleTV_whenDidNotPurchase_thenCanPurchase() {
|
||||
// let reader = MockReceiptReader()
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
//
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: .appleTV), [.appleTV])
|
||||
// }
|
||||
//
|
||||
// func test_givenAppleTV_whenDidPurchase_thenCannotPurchase() {
|
||||
// let reader = MockReceiptReader()
|
||||
// reader.setReceipt(withBuild: defaultBuildNumber, products: [.appleTV])
|
||||
// let sut = IAPManager(receiptReader: reader)
|
||||
//
|
||||
// XCTAssertEqual(sut.purchasableProducts(withFeature: .appleTV), [])
|
||||
// }
|
||||
//
|
||||
// TODO: #570, test app library
|
||||
|
||||
// MARK: App level
|
||||
|
||||
func test_givenBetaApp_thenIsRestricted() async {
|
||||
|
|
|
@ -31,9 +31,10 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
private var fwd: NEPTPForwarder?
|
||||
|
||||
override func startTunnel(options: [String: NSObject]? = nil) async throws {
|
||||
CommonLibrary.configureLogging(
|
||||
PassepartoutConfiguration.shared.configureLogging(
|
||||
to: BundleConfiguration.urlForTunnelLog,
|
||||
parameters: Constants.shared.log
|
||||
parameters: Constants.shared.log,
|
||||
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
|
||||
)
|
||||
fwd = try await NEPTPForwarder(
|
||||
provider: self,
|
||||
|
@ -44,7 +45,7 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
do {
|
||||
try await fwd?.startTunnel(options: options)
|
||||
} catch {
|
||||
CommonLibrary.flushLog()
|
||||
PassepartoutConfiguration.shared.flushLog()
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
@ -52,11 +53,11 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
|||
override func stopTunnel(with reason: NEProviderStopReason) async {
|
||||
await fwd?.stopTunnel(with: reason)
|
||||
fwd = nil
|
||||
CommonLibrary.flushLog()
|
||||
PassepartoutConfiguration.shared.flushLog()
|
||||
}
|
||||
|
||||
override func cancelTunnelWithError(_ error: (any Error)?) {
|
||||
CommonLibrary.flushLog()
|
||||
PassepartoutConfiguration.shared.flushLog()
|
||||
super.cancelTunnelWithError(error)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue