mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-01-31 04:52:05 +00:00
parent
28a2017da2
commit
a29495a69c
@ -4,8 +4,6 @@
|
||||
<dict>
|
||||
<key>AppConfig</key>
|
||||
<dict>
|
||||
<key>appId</key>
|
||||
<string>$(CFG_APP_ID)</string>
|
||||
<key>appStoreId</key>
|
||||
<string>$(CFG_APP_STORE_ID)</string>
|
||||
<key>groupId</key>
|
||||
@ -16,8 +14,6 @@
|
||||
<string>$(CFG_TEAM_ID).$(CFG_GROUP_ID)</string>
|
||||
<key>profilesContainerName</key>
|
||||
<string>$(CFG_PROFILES_CONTAINER_NAME)</string>
|
||||
<key>teamId</key>
|
||||
<string>$(CFG_TEAM_ID)</string>
|
||||
<key>tunnelId</key>
|
||||
<string>$(CFG_TUNNEL_ID)</string>
|
||||
</dict>
|
||||
|
@ -35,6 +35,7 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
import AppKit
|
||||
import AppUI
|
||||
import CommonLibrary
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
@ -59,7 +60,7 @@ private extension AppDelegate {
|
||||
func quitConfirmationAlert() -> NSApplication.TerminateReply {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .warning
|
||||
alert.messageText = Strings.Alerts.ConfirmQuit.title(Constants.shared.identifiers.displayName)
|
||||
alert.messageText = Strings.Alerts.ConfirmQuit.title(BundleConfiguration.mainDisplayName)
|
||||
alert.informativeText = Strings.Alerts.ConfirmQuit.message
|
||||
alert.addButton(withTitle: Strings.Global.ok)
|
||||
alert.addButton(withTitle: Strings.Global.cancel)
|
||||
|
@ -42,7 +42,7 @@ struct PassepartoutApp: App {
|
||||
private let context: AppContext = .shared
|
||||
// private let context: AppContext = .mock(withRegistry: .shared)
|
||||
|
||||
private let appName = Constants.shared.identifiers.displayName
|
||||
private let appName = BundleConfiguration.mainDisplayName
|
||||
|
||||
@StateObject
|
||||
private var theme = Theme()
|
||||
@ -71,7 +71,7 @@ private extension PassepartoutApp {
|
||||
)
|
||||
.onLoad {
|
||||
CommonLibrary.configureLogging(
|
||||
to: Constants.shared.urlForAppLog,
|
||||
to: BundleConfiguration.urlForAppLog,
|
||||
parameters: Constants.shared.log
|
||||
)
|
||||
AppUI.configure(with: context)
|
||||
|
@ -28,7 +28,7 @@ import Foundation
|
||||
import PassepartoutKit
|
||||
|
||||
extension Issue {
|
||||
static func fromBundle(_ bundle: BundleConfiguration, purchasedProducts: Set<AppProduct>, tunnel: Tunnel) async -> Self {
|
||||
static func with(versionString: String, purchasedProducts: Set<AppProduct>, tunnel: Tunnel) async -> Self {
|
||||
let appLog = CommonLibrary.currentLog(parameters: Constants.shared.log)
|
||||
.joined(separator: "\n")
|
||||
.data(using: .utf8)
|
||||
@ -42,7 +42,7 @@ extension Issue {
|
||||
.data(using: .utf8)
|
||||
}
|
||||
// latest persisted tunnel log
|
||||
else if let latestTunnelEntry = CommonLibrary.availableLogs(at: Constants.shared.urlForTunnelLog)
|
||||
else if let latestTunnelEntry = CommonLibrary.availableLogs(at: BundleConfiguration.urlForTunnelLog)
|
||||
.max(by: { $0.key < $1.key }) {
|
||||
|
||||
tunnelLog = try? Data(contentsOf: latestTunnelEntry.value)
|
||||
@ -53,7 +53,7 @@ extension Issue {
|
||||
}
|
||||
|
||||
return Issue(
|
||||
appLine: "\(Strings.Unlocalized.appName) \(bundle.versionString)",
|
||||
appLine: "\(Strings.Unlocalized.appName) \(versionString)",
|
||||
purchasedProducts: purchasedProducts,
|
||||
appLog: appLog,
|
||||
tunnelLog: tunnelLog
|
||||
|
@ -43,7 +43,7 @@ public struct AppProduct: RawRepresentable, Hashable, Sendable {
|
||||
extension AppProduct: InAppIdentifierProviding {
|
||||
public var inAppIdentifier: String {
|
||||
[
|
||||
BundleConfiguration.main.string(for: .iapBundlePrefix),
|
||||
BundleConfiguration.mainString(for: .iapBundlePrefix),
|
||||
rawValue
|
||||
].joined(separator: ".")
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
struct AboutView: View {
|
||||
let identifiers: Constants.Identifiers
|
||||
|
||||
@Binding
|
||||
var navigationRoute: AboutRouterView.NavigationRoute?
|
||||
@ -66,7 +65,6 @@ private extension AboutView {
|
||||
|
||||
#Preview {
|
||||
AboutView(
|
||||
identifiers: Constants.shared.identifiers,
|
||||
navigationRoute: .constant(nil)
|
||||
)
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
//
|
||||
|
||||
import CommonLibrary
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
struct LinksView: View {
|
||||
@ -42,10 +43,14 @@ private extension LinksView {
|
||||
.shared
|
||||
}
|
||||
|
||||
var appStoreId: String {
|
||||
BundleConfiguration.mainString(for: .appStoreId)
|
||||
}
|
||||
|
||||
var supportSection: some View {
|
||||
Section {
|
||||
Link(Strings.Views.About.Links.Rows.joinCommunity, destination: constants.websites.subreddit)
|
||||
Link(Strings.Views.About.Links.Rows.writeReview, destination: constants.urlForReview)
|
||||
Link(Strings.Views.About.Links.Rows.writeReview, destination: BundleConfiguration.urlForReview)
|
||||
} header: {
|
||||
Text(Strings.Views.About.Links.Sections.support)
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ extension AboutRouterView {
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
AboutView(
|
||||
identifiers: Constants.shared.identifiers,
|
||||
navigationRoute: $navigationRoute
|
||||
)
|
||||
.toolbar {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
extension AboutView {
|
||||
@ -41,7 +42,7 @@ extension AboutView {
|
||||
Section {
|
||||
diagnosticsLink
|
||||
Text(Strings.Global.version)
|
||||
.withTrailingText(identifiers.versionString)
|
||||
.withTrailingText(BundleConfiguration.mainVersionString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ extension AboutRouterView {
|
||||
var body: some View {
|
||||
NavigationSplitView {
|
||||
AboutView(
|
||||
identifiers: Constants.shared.identifiers,
|
||||
navigationRoute: $navigationRoute
|
||||
)
|
||||
} detail: {
|
||||
|
@ -23,10 +23,11 @@
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
#if os(macOS)
|
||||
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
extension AboutView {
|
||||
var listView: some View {
|
||||
List(selection: $navigationRoute) {
|
||||
@ -39,7 +40,7 @@ extension AboutView {
|
||||
}
|
||||
}
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
Text(identifiers.versionString)
|
||||
Text(BundleConfiguration.mainVersionString)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ struct DiagnosticsView: View {
|
||||
private var logsPrivateData = false
|
||||
|
||||
var availableTunnelLogs: () -> [LogEntry] = {
|
||||
CommonLibrary.availableLogs(at: Constants.shared.urlForTunnelLog)
|
||||
CommonLibrary.availableLogs(at: BundleConfiguration.urlForTunnelLog)
|
||||
.sorted {
|
||||
$0.key > $1.key
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import UtilsLibrary
|
||||
@ -38,7 +39,11 @@ extension ReportIssueButton: View {
|
||||
defer {
|
||||
isPending = false
|
||||
}
|
||||
let issue = await Issue.fromBundle(.main, purchasedProducts: purchasedProducts, tunnel: tunnel)
|
||||
let issue = await Issue.with(
|
||||
versionString: BundleConfiguration.mainVersionString,
|
||||
purchasedProducts: purchasedProducts,
|
||||
tunnel: tunnel
|
||||
)
|
||||
guard MailComposerView.canSendMail() else {
|
||||
openMailTo(with: issue)
|
||||
return
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#if os(macOS)
|
||||
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
extension ReportIssueButton: View {
|
||||
@ -35,7 +36,11 @@ extension ReportIssueButton: View {
|
||||
return
|
||||
}
|
||||
Task {
|
||||
let issue = await Issue.fromBundle(.main, purchasedProducts: purchasedProducts, tunnel: tunnel)
|
||||
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)
|
||||
|
@ -28,8 +28,6 @@ import PassepartoutKit
|
||||
|
||||
extension BundleConfiguration {
|
||||
public enum BundleKey: String {
|
||||
case appId
|
||||
|
||||
case appStoreId
|
||||
|
||||
case customUserLevel
|
||||
@ -42,31 +40,76 @@ extension BundleConfiguration {
|
||||
|
||||
case profilesContainerName
|
||||
|
||||
case teamId
|
||||
|
||||
case tunnelId
|
||||
}
|
||||
|
||||
// WARNING: nil from package itself, e.g. in previews
|
||||
static let failableMain: BundleConfiguration? = {
|
||||
BundleConfiguration(.main, key: "AppConfig")
|
||||
}()
|
||||
|
||||
public static let main: BundleConfiguration = {
|
||||
guard let failableMain else {
|
||||
fatalError("Unable to build BundleConfiguration")
|
||||
public static var mainDisplayName: String {
|
||||
if isPreview {
|
||||
return "preview-display-name"
|
||||
}
|
||||
return failableMain
|
||||
}()
|
||||
return main.displayName
|
||||
}
|
||||
|
||||
public func string(for key: BundleKey) -> String {
|
||||
guard let value: String = value(forKey: key.rawValue) else {
|
||||
fatalError("Key '\(key)' not found in bundle")
|
||||
public static var mainVersionString: String {
|
||||
if isPreview {
|
||||
return "preview-1.2.3"
|
||||
}
|
||||
return main.versionString
|
||||
}
|
||||
|
||||
public static func mainString(for key: BundleKey) -> String {
|
||||
if isPreview {
|
||||
return "preview-key(\(key.rawValue))"
|
||||
}
|
||||
guard let value: String = main.value(forKey: key.rawValue) else {
|
||||
fatalError("Missing main bundle key: \(key.rawValue)")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
public func integerIfPresent(for key: BundleKey) -> Int? {
|
||||
value(forKey: key.rawValue)
|
||||
public static func mainIntegerIfPresent(for key: BundleKey) -> Int? {
|
||||
if isPreview {
|
||||
return nil
|
||||
}
|
||||
return main.value(forKey: key.rawValue)
|
||||
}
|
||||
|
||||
public static var urlForReview: URL {
|
||||
let appStoreId = mainString(for: .appStoreId)
|
||||
guard let url = URL(string: "https://apps.apple.com/app/id\(appStoreId)?action=write-review") else {
|
||||
fatalError("Unable to build urlForReview")
|
||||
}
|
||||
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 {
|
||||
fatalError("Missing main bundle")
|
||||
}
|
||||
return bundle
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -31,36 +31,6 @@ extension Constants {
|
||||
}
|
||||
|
||||
public struct Constants: Decodable, Sendable {
|
||||
private static var bundleConfiguration: BundleConfiguration? {
|
||||
.failableMain
|
||||
}
|
||||
|
||||
public struct Identifiers: Decodable, Sendable {
|
||||
public var appId: String {
|
||||
bundleConfiguration?.string(for: .appId) ?? "1234567890"
|
||||
}
|
||||
|
||||
public var appStoreId: String {
|
||||
bundleConfiguration?.string(for: .appStoreId) ?? "11223344"
|
||||
}
|
||||
|
||||
public var groupId: String {
|
||||
bundleConfiguration?.string(for: .groupId) ?? "fake-group-id"
|
||||
}
|
||||
|
||||
public var iapBundlePrefix: String {
|
||||
bundleConfiguration?.string(for: .iapBundlePrefix) ?? "fake-iap-prefix"
|
||||
}
|
||||
|
||||
public var displayName: String {
|
||||
bundleConfiguration?.displayName ?? "DisplayName"
|
||||
}
|
||||
|
||||
public var versionString: String {
|
||||
bundleConfiguration?.versionString ?? "1.0.0 (1000)"
|
||||
}
|
||||
}
|
||||
|
||||
public struct Websites: Decodable, Sendable {
|
||||
public let home: URL
|
||||
|
||||
@ -158,7 +128,7 @@ public struct Constants: Decodable, Sendable {
|
||||
public let maxAge: TimeInterval?
|
||||
}
|
||||
|
||||
public let identifiers: Identifiers
|
||||
public let bundle: String
|
||||
|
||||
public let websites: Websites
|
||||
|
||||
@ -170,24 +140,3 @@ public struct Constants: Decodable, Sendable {
|
||||
|
||||
public let log: Log
|
||||
}
|
||||
|
||||
extension Constants {
|
||||
public var urlForReview: URL {
|
||||
URL(string: "https://apps.apple.com/app/id\(identifiers.appStoreId)?action=write-review")!
|
||||
}
|
||||
|
||||
public var urlForAppLog: URL {
|
||||
cachesURL.appending(path: log.appPath)
|
||||
}
|
||||
|
||||
public var urlForTunnelLog: URL {
|
||||
cachesURL.appending(path: log.tunnelPath)
|
||||
}
|
||||
|
||||
private var cachesURL: URL {
|
||||
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: identifiers.groupId) else {
|
||||
fatalError("Unable to access App Group container")
|
||||
}
|
||||
return url.appending(components: "Library", "Caches")
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"identifiers": {
|
||||
},
|
||||
"bundle": "AppConfig",
|
||||
"websites": {
|
||||
"home": "https://passepartoutvpn.app",
|
||||
"subreddit": "https://www.reddit.com/r/passepartout/",
|
||||
|
@ -32,7 +32,7 @@ extension LoggerDestination {
|
||||
|
||||
extension UserDefaults {
|
||||
public static let group: UserDefaults = {
|
||||
let appGroup = BundleConfiguration.main.string(for: .groupId)
|
||||
let appGroup = BundleConfiguration.mainString(for: .groupId)
|
||||
guard let defaults = UserDefaults(suiteName: appGroup) else {
|
||||
fatalError("No access to App Group: \(appGroup)")
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ extension ProfileManager {
|
||||
|
||||
let store = CoreDataPersistentStore(
|
||||
logger: .default,
|
||||
containerName: BundleConfiguration.main.string(for: .profilesContainerName),
|
||||
containerName: BundleConfiguration.mainString(for: .profilesContainerName),
|
||||
model: model,
|
||||
cloudKit: false,
|
||||
cloudKitIdentifier: nil,
|
||||
@ -70,7 +70,7 @@ extension Tunnel {
|
||||
extension Tunnel {
|
||||
static let shared = Tunnel(
|
||||
strategy: NETunnelStrategy(
|
||||
bundleIdentifier: BundleConfiguration.main.string(for: .tunnelId),
|
||||
bundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||
encoder: .shared
|
||||
)
|
||||
)
|
||||
|
@ -53,7 +53,7 @@ extension IAPManager {
|
||||
|
||||
return testAppType
|
||||
}
|
||||
if let infoValue = BundleConfiguration.main.integerIfPresent(for: .customUserLevel),
|
||||
if let infoValue = BundleConfiguration.mainIntegerIfPresent(for: .customUserLevel),
|
||||
let testAppType = AppUserLevel(rawValue: infoValue) {
|
||||
|
||||
return testAppType
|
||||
|
@ -64,7 +64,7 @@ extension Registry {
|
||||
extension TunnelEnvironment where Self == AppGroupEnvironment {
|
||||
static var shared: Self {
|
||||
AppGroupEnvironment(
|
||||
appGroup: BundleConfiguration.main.string(for: .groupId),
|
||||
appGroup: BundleConfiguration.mainString(for: .groupId),
|
||||
prefix: "PassepartoutKit."
|
||||
)
|
||||
}
|
||||
@ -84,9 +84,9 @@ extension NEProtocolDecoder where Self == KeychainNEProtocolCoder {
|
||||
|
||||
private var sharedProtocolCoder: KeychainNEProtocolCoder {
|
||||
KeychainNEProtocolCoder(
|
||||
tunnelBundleIdentifier: BundleConfiguration.main.string(for: .tunnelId),
|
||||
tunnelBundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||
registry: .shared,
|
||||
coder: CodableProfileCoder(),
|
||||
keychain: AppleKeychain(group: BundleConfiguration.main.string(for: .keychainGroupId))
|
||||
keychain: AppleKeychain(group: BundleConfiguration.mainString(for: .keychainGroupId))
|
||||
)
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
|
||||
|
||||
override func startTunnel(options: [String: NSObject]? = nil) async throws {
|
||||
CommonLibrary.configureLogging(
|
||||
to: Constants.shared.urlForTunnelLog,
|
||||
to: BundleConfiguration.urlForTunnelLog,
|
||||
parameters: Constants.shared.log
|
||||
)
|
||||
fwd = try await NEPTPForwarder(
|
||||
|
@ -4,14 +4,10 @@
|
||||
<dict>
|
||||
<key>AppConfig</key>
|
||||
<dict>
|
||||
<key>appId</key>
|
||||
<string>$(CFG_APP_ID)</string>
|
||||
<key>groupId</key>
|
||||
<string>$(CFG_GROUP_ID)</string>
|
||||
<key>keychainGroupId</key>
|
||||
<string>$(CFG_TEAM_ID).$(CFG_GROUP_ID)</string>
|
||||
<key>teamId</key>
|
||||
<string>$(CFG_TEAM_ID)</string>
|
||||
<key>tunnelId</key>
|
||||
<string>$(CFG_TUNNEL_ID)</string>
|
||||
</dict>
|
||||
|
Loading…
Reference in New Issue
Block a user