diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index f4d8fb5..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index 0fa898e..107a2c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +CodeSigning.xcconfig *.ipa /build diff --git a/Build.xcconfig b/Build.xcconfig new file mode 100644 index 0000000..98c21cf --- /dev/null +++ b/Build.xcconfig @@ -0,0 +1,23 @@ +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +MARKETING_VERSION = 1.1.1 +CURRENT_PROJECT_VERSION = 1 + +// Vars to be overwritten by `CodeSigning.xcconfig` if exists +DEVELOPMENT_TEAM = S32Z3HMYVQ +ORG_IDENTIFIER = com.stossy11 + +// Codesigning settings defined optionally, see `CodeSigning.xcconfig.example` +#include? "CodeSigning.xcconfig" + +ORG_PREFIX = $(ORG_IDENTIFIER) + +PRODUCT_BUNDLE_IDENTIFIER = $(ORG_PREFIX).StosVPN + +TUNNEL_NAME = TunnelProv + +TUNNEL_BUNDLE_IDENTIFIER = $(PRODUCT_BUNDLE_IDENTIFIER).$(TUNNEL_NAME) + +// Mute warnings about duplicate classes in AuthKit and AuthUIKit +DEBUG_DUPLICATE_CLASSES = NO diff --git a/CodeSigning.xcconfig.sample b/CodeSigning.xcconfig.sample new file mode 100644 index 0000000..45afdc6 --- /dev/null +++ b/CodeSigning.xcconfig.sample @@ -0,0 +1,34 @@ +// Your Team ID. +// If you have a paid Apple Developer account, you can find your Team ID at +// https://developer.apple.com/account/#/membership +DEVELOPMENT_TEAM = 95J8WZ4TN8 + +// Prefix of unique bundle IDs registered to you in Apple Developer Portal. +// You need to register: +// - com.myuniquename.StosVPN +ORG_IDENTIFIER = com.stossy11 + +// Name of the iOS development signing certificate, you probably do not need +// to change this. +CODE_SIGN_IDENTITY_IOS = Apple Development + +// Name of the iOS development signing certificate, you probably do not need +// to change this. +CODE_SIGN_IDENTITY_TVOS = Apple Development + +// The values below are specific to macOS development. If you do not define +// these keys, the build will default to ad-hoc signing. You will need to +// follow `Documentation/MacDevelopment.md` to disable library verification and +// remove unsupported entitlements. + +// Name of the macOS development signing certificate. Comment out this line to +// use ad-hoc signing. +CODE_SIGN_IDENTITY_MAC = Apple Development + +// Create a Mac provisioning profile for com.myuniquename.UTM with the +// Hypervisor entitlements and get its UUID. If you do not have access to these +// entitlements, comment out the line and delete the following entitlements +// - com.apple.vm.device-access +// from the following file +// - Provenance/macOS.entitlements +PROVISIONING_PROFILE_SPECIFIER_MAC = 00000000-1111-2222-3333-444444444444 diff --git a/StosVPN.xcodeproj/project.pbxproj b/StosVPN.xcodeproj/project.pbxproj index 77b2cbb..187b0ec 100644 --- a/StosVPN.xcodeproj/project.pbxproj +++ b/StosVPN.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0E6351BE2E18EABA002AF750 /* NavigationBackport in Frameworks */ = {isa = PBXBuildFile; productRef = 0E6351BD2E18EABA002AF750 /* NavigationBackport */; }; 4EB3C7712D96715400C1B22C /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EB3C7702D96715400C1B22C /* NetworkExtension.framework */; }; 4EB3C7792D96715400C1B22C /* TunnelProv.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4EB3C76E2D96715400C1B22C /* TunnelProv.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ @@ -36,6 +37,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0E6351B62E18DF53002AF750 /* Build.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Build.xcconfig; sourceTree = ""; }; + 0E6351B72E18DF53002AF750 /* CodeSigning.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CodeSigning.xcconfig; sourceTree = ""; }; + 0E6351B82E18DF53002AF750 /* CodeSigning.xcconfig.sample */ = {isa = PBXFileReference; lastKnownFileType = text; path = CodeSigning.xcconfig.sample; sourceTree = ""; }; 4EB3C7582D96631A00C1B22C /* StosVPN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StosVPN.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4EB3C76E2D96715400C1B22C /* TunnelProv.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TunnelProv.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 4EB3C7702D96715400C1B22C /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; @@ -68,6 +72,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0E6351BE2E18EABA002AF750 /* NavigationBackport in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -85,6 +90,9 @@ 4EB3C74F2D96631A00C1B22C = { isa = PBXGroup; children = ( + 0E6351B62E18DF53002AF750 /* Build.xcconfig */, + 0E6351B72E18DF53002AF750 /* CodeSigning.xcconfig */, + 0E6351B82E18DF53002AF750 /* CodeSigning.xcconfig.sample */, 4EB3C75A2D96631A00C1B22C /* StosVPN */, 4EB3C7722D96715400C1B22C /* TunnelProv */, 4EB3C76F2D96715400C1B22C /* Frameworks */, @@ -131,6 +139,7 @@ ); name = StosVPN; packageProductDependencies = ( + 0E6351BD2E18EABA002AF750 /* NavigationBackport */, ); productName = StosVPN; productReference = 4EB3C7582D96631A00C1B22C /* StosVPN.app */; @@ -185,9 +194,14 @@ knownRegions = ( en, Base, + es, + it, ); mainGroup = 4EB3C74F2D96631A00C1B22C; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 0E6351BC2E18EABA002AF750 /* XCRemoteSwiftPackageReference "NavigationBackport" */, + ); productRefGroup = 4EB3C7592D96631A00C1B22C /* Products */; projectDirPath = ""; projectRoot = ""; @@ -243,6 +257,7 @@ /* Begin XCBuildConfiguration section */ 4EB3C7642D96631B00C1B22C /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 0E6351B62E18DF53002AF750 /* Build.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -274,6 +289,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(TARGET_NAME)/$(TARGET_NAME).entitlements"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -293,19 +309,31 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "$(TARGET_NAME)"; + INFOPLIST_KEY_NSHumanReadableCopyright = Stossy11; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 15.0; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; 4EB3C7652D96631B00C1B22C /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 0E6351B62E18DF53002AF750 /* Build.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -337,6 +365,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(TARGET_NAME)/$(TARGET_NAME).entitlements"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -350,13 +379,24 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "$(TARGET_NAME)"; + INFOPLIST_KEY_NSHumanReadableCopyright = Stossy11; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 15.0; VALIDATE_PRODUCT = YES; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; @@ -366,32 +406,20 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = StosVPN/StosVPN.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"StosVPN/Preview Content\""; - DEVELOPMENT_TEAM = 95J8WZ4TN8; ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = StosVPN/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -401,87 +429,49 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = StosVPN/StosVPN.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"StosVPN/Preview Content\""; - DEVELOPMENT_TEAM = 95J8WZ4TN8; ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = StosVPN/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 4EB3C77C2D96715400C1B22C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_ENTITLEMENTS = TunnelProv/TunnelProv.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = 95J8WZ4TN8; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = TunnelProv/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = TunnelProv; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN.TunnelProv; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = "$(TUNNEL_BUNDLE_IDENTIFIER)"; SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 4EB3C77D2D96715400C1B22C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_ENTITLEMENTS = TunnelProv/TunnelProv.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = 95J8WZ4TN8; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = TunnelProv/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = TunnelProv; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN.TunnelProv; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = "$(TUNNEL_BUNDLE_IDENTIFIER)"; SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; @@ -516,6 +506,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 0E6351BC2E18EABA002AF750 /* XCRemoteSwiftPackageReference "NavigationBackport" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/johnpatrickmorgan/NavigationBackport"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.11.5; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 0E6351BD2E18EABA002AF750 /* NavigationBackport */ = { + isa = XCSwiftPackageProductDependency; + package = 0E6351BC2E18EABA002AF750 /* XCRemoteSwiftPackageReference "NavigationBackport" */; + productName = NavigationBackport; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 4EB3C7502D96631A00C1B22C /* Project object */; } diff --git a/StosVPN/ContentView.swift b/StosVPN/ContentView.swift index a9b6d05..5992b9e 100644 --- a/StosVPN/ContentView.swift +++ b/StosVPN/ContentView.swift @@ -9,6 +9,12 @@ import SwiftUI import Foundation import NetworkExtension +import NavigationBackport + +extension Bundle { + var shortVersion: String { object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.0" } + var tunnelBundleID: String { bundleIdentifier!.appending(".TunnelProv") } +} // MARK: - Logging Utility class VPNLogger: ObservableObject { @@ -50,12 +56,6 @@ class TunnelManager: ObservableObject { UserDefaults.standard.string(forKey: "TunnelSubnetMask") ?? "255.255.255.0" } - private var tunnelBundleId: String { - Bundle.main.bundleIdentifier!.appending(".TunnelProv") - } - - import SwiftUI - enum TunnelStatus { case disconnected case connecting @@ -123,7 +123,7 @@ class TunnelManager: ObservableObject { // Look specifically for StosVPN manager for manager in managers { if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol, - proto.providerBundleIdentifier == self.tunnelBundleId { + proto.providerBundleIdentifier == Bundle.main.tunnelBundleID { self.vpnManager = manager self.updateTunnelStatus(from: manager.connection.status) VPNLogger.shared.log("Loaded existing StosVPN tunnel configuration") @@ -173,7 +173,7 @@ class TunnelManager: ObservableObject { self.tunnelStatus = .error } - VPNLogger.shared.log("StosVPN status updated: \(self.tunnelStatus.rawValue)") + VPNLogger.shared.log("StosVPN status updated: \(self.tunnelStatus)") } } @@ -182,7 +182,7 @@ class TunnelManager: ObservableObject { manager.localizedDescription = "StosVPN" let proto = NETunnelProviderProtocol() - proto.providerBundleIdentifier = self.tunnelBundleId + proto.providerBundleIdentifier = Bundle.main.tunnelBundleID proto.serverAddress = NSLocalizedString("server_address_name", comment: "") manager.protocolConfiguration = proto @@ -248,7 +248,7 @@ class TunnelManager: ObservableObject { guard let self = self else { return } if let activeManager = activeManager, - (activeManager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier != self.tunnelBundleId { + (activeManager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier != Bundle.main.tunnelBundleID { VPNLogger.shared.log("Disconnecting existing VPN connection before starting StosVPN") // Set a flag to start StosVPN after disconnection @@ -359,7 +359,7 @@ struct ContentView: View { @AppStorage("hasNotCompletedSetup") private var hasNotCompletedSetup = true var body: some View { - NavigationStack { + NBNavigationStack { VStack(spacing: 30) { Spacer() @@ -430,7 +430,7 @@ struct StatusIndicatorView: View { .font(.system(size: 50)) .foregroundColor(tunnelManager.tunnelStatus.color) - Text(tunnelManager.tunnelStatus.rawValue) + Text(tunnelManager.tunnelStatus.localizedTitle) .font(.headline) .foregroundColor(.primary) } @@ -614,7 +614,7 @@ struct StatItemView: View { // MARK: - Updated SettingsView struct SettingsView: View { - @Environment(\.dismiss) private var dismiss + @Environment(\.presentationMode) var presentationMode @AppStorage("selectedLanguage") private var selectedLanguage = Locale.current.languageCode ?? "en" @AppStorage("TunnelDeviceIP") private var deviceIP = "10.7.0.0" @AppStorage("TunnelFakeIP") private var fakeIP = "10.7.0.1" @@ -622,7 +622,7 @@ struct SettingsView: View { @AppStorage("autoConnect") private var autoConnect = false var body: some View { - NavigationStack { + NBNavigationStack { List { Section(header: Text("connection_settings")) { Toggle("auto_connect_on_launch", isOn: $autoConnect) @@ -670,7 +670,7 @@ struct SettingsView: View { HStack { Text("app_version") Spacer() - Text("1.1.0") + Text(Bundle.main.shortVersion) .foregroundColor(.secondary) } NavigationLink(destination: HelpView()) { @@ -680,13 +680,13 @@ struct SettingsView: View { Section(header: Text("language")) { Picker("language", selection: $selectedLanguage) { - Text("English").tag(0) - Text("Spanish").tag(1) - Text("Italian").tag(2) + Text("English").tag("en") + Text("Spanish").tag("es") + Text("Italian").tag("it") } .onChange(of: selectedLanguage) { newValue in - let languageCode = ["en", "es", "it"][newValue] - LanguageManager().updateLanguage(to: languageCode) + let languageCode = newValue + LanguageManager.shared.updateLanguage(to: languageCode) } } } @@ -701,6 +701,10 @@ struct SettingsView: View { } } } + + private func dismiss() { + presentationMode.wrappedValue.dismiss() + } } @@ -779,7 +783,7 @@ struct HelpView: View { VStack(alignment: .leading, spacing: 15) { Text("faq_q2_a1") .padding(.bottom, 10) - .fontWeight(.medium) + .font(.headline) Text("faq_q2_point1") Text("faq_q2_point2") Text("faq_q2_point3") @@ -794,7 +798,7 @@ struct HelpView: View { Text("faq_q3_a1") .padding(.bottom, 10) Text("faq_troubleshoot_header") - .fontWeight(.medium) + .font(.headline) Text("faq_troubleshoot1") Text("faq_troubleshoot2") Text("faq_troubleshoot3") @@ -805,7 +809,7 @@ struct HelpView: View { NavigationLink("faq_q4") { VStack(alignment: .leading, spacing: 15) { Text("faq_q4_intro") - .fontWeight(.medium) + .font(.headline) .padding(.bottom, 10) Text("faq_q4_case1") Text("faq_q4_case2") @@ -823,7 +827,7 @@ struct HelpView: View { Text("biz_q1_a1") .padding(.bottom, 10) Text("biz_key_points_header") - .fontWeight(.medium) + .font(.headline) Text("biz_point1") Text("biz_point2") Text("biz_point3") @@ -850,7 +854,7 @@ struct HelpView: View { } struct SetupView: View { - @Environment(\.dismiss) private var dismiss + @Environment(\.presentationMode) var presentationMode @AppStorage("hasNotCompletedSetup") private var hasNotCompletedSetup = true @State private var currentPage = 0 let pages = [ @@ -880,7 +884,7 @@ struct SetupView: View { ) ] var body: some View { - NavigationStack { + NBNavigationStack { VStack { TabView(selection: $currentPage) { ForEach(0..