From 525e5f57b0f788adff2e4b7a1b731f14838609ca Mon Sep 17 00:00:00 2001 From: Stephen B <158498287+StephenDev0@users.noreply.github.com> Date: Mon, 10 Nov 2025 00:30:45 -0500 Subject: [PATCH] Fix the black screen --- StikJIT/StikJITApp.swift | 84 +++++++++++++++++++++++++++----- StikJIT/Utilities/mountDDI.swift | 4 ++ StikJIT/Views/HomeView.swift | 10 +++- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index 4a33285..9861f2c 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -227,6 +227,9 @@ class TunnelManager: ObservableObject { self.tunnelStatus = .error } VPNLogger.shared.log("VPN status updated: \(self.tunnelStatus.rawValue)") + if connectionStatus == .connected && heartbeatStartPending { + startHeartbeatInBackground() + } } } @@ -295,8 +298,8 @@ class TunnelManager: ObservableObject { } private func startExistingVPN(manager: NETunnelProviderManager) { - guard tunnelStatus != .connected else { - VPNLogger.shared.log("Network tunnel is already connected") + guard tunnelStatus == .disconnected || tunnelStatus == .error else { + VPNLogger.shared.log("Ignoring VPN start; current status: \(tunnelStatus.rawValue)") return } tunnelStatus = .connecting @@ -483,12 +486,15 @@ class DNSChecker: ObservableObject { // Global state variable for the heartbeat response. var pubHeartBeat = false +private var heartbeatStartPending = false +private var heartbeatStartInProgress = false @main struct HeartbeatApp: App { @AppStorage("hasLaunchedBefore") var hasLaunchedBefore: Bool = false @AppStorage("customAccentColor") private var customAccentColorHex: String = "" @AppStorage("appTheme") private var appThemeRaw: String = AppTheme.system.rawValue + @AppStorage("autoStartVPN") private var autoStartVPN = true @State private var showWelcomeSheet: Bool = false @State private var show_alert = false @State private var alert_string = "" @@ -551,6 +557,14 @@ struct HeartbeatApp: App { } } + private func triggerAutoVPNStartIfNeeded() { + guard autoStartVPN else { return } + let manager = TunnelManager.shared + if manager.tunnelStatus == .disconnected || manager.tunnelStatus == .error { + manager.startVPN() + } + } + private var globalAccent: Color { themeExpansionManager.resolvedAccentColor(from: customAccentColorHex) } @@ -599,7 +613,7 @@ struct HeartbeatApp: App { if !hasLaunchedBefore { showWelcomeSheet = true } else { - TunnelManager.shared.startVPN() + triggerAutoVPNStartIfNeeded() } HeartbeatApp.updateUIKitTint(customHex: customAccentColorHex, hasAccess: themeExpansionManager.hasThemeExpansion) @@ -613,10 +627,10 @@ struct HeartbeatApp: App { } .sheet(isPresented: $showWelcomeSheet) { WelcomeSheetView { - // When the user taps "Continue", mark the app as launched and start the VPN. + // When the user taps "Continue", mark the app as launched and start the VPN if allowed. hasLaunchedBefore = true showWelcomeSheet = false - TunnelManager.shared.startVPN() + triggerAutoVPNStartIfNeeded() } } } @@ -653,8 +667,11 @@ class MountingProgress: ObservableObject { @Published var coolisMounted: Bool = false func checkforMounted() { - DispatchQueue.main.async { - self.coolisMounted = isMounted() + DispatchQueue.global(qos: .utility).async { + let mounted = isMounted() + DispatchQueue.main.async { + self.coolisMounted = mounted + } } } @@ -671,10 +688,21 @@ class MountingProgress: ObservableObject { } private func mount() { - self.coolisMounted = isMounted() + guard TunnelManager.shared.tunnelStatus == .connected else { + DispatchQueue.main.async { + self.coolisMounted = false + self.mountingThread = nil + } + return + } + + let currentlyMounted = isMounted() + DispatchQueue.main.async { + self.coolisMounted = currentlyMounted + } let pairingpath = URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path - if isPairing(), !isMounted() { + if isPairing(), !currentlyMounted { if let mountingThread = mountingThread { mountingThread.cancel() self.mountingThread = nil @@ -697,7 +725,8 @@ class MountingProgress: ObservableObject { } } } else { - self.coolisMounted = isMounted() + self.coolisMounted = true + self.checkforMounted() } self.mountingThread = nil } @@ -724,8 +753,37 @@ func isPairing() -> Bool { return true } -func startHeartbeatInBackground() { +func startHeartbeatInBackground(requireVPNConnection: Bool = true) { + assert(Thread.isMainThread, "startHeartbeatInBackground must be called on the main thread") + let pairingFileURL = URL.documentsDirectory.appendingPathComponent("pairingFile.plist") + + guard FileManager.default.fileExists(atPath: pairingFileURL.path) else { + heartbeatStartPending = false + return + } + + let vpnConnected = TunnelManager.shared.tunnelStatus == .connected + if requireVPNConnection && !vpnConnected { + if !heartbeatStartPending { + print("Heartbeat start deferred until VPN connects") + } + heartbeatStartPending = true + return + } + + guard !heartbeatStartInProgress else { + return + } + + heartbeatStartPending = false + heartbeatStartInProgress = true + let heartBeatThread = Thread { + defer { + DispatchQueue.main.async { + heartbeatStartInProgress = false + } + } let completionHandler: @convention(block) (Int32, String?) -> Void = { result, message in if result == 0 { print("Heartbeat started successfully: \(message ?? "")") @@ -762,7 +820,9 @@ func startHeartbeatInBackground() { showTryAgain: true ) { shouldTryAgain in if shouldTryAgain { - startHeartbeatInBackground() + DispatchQueue.main.async { + startHeartbeatInBackground() + } } } } diff --git a/StikJIT/Utilities/mountDDI.swift b/StikJIT/Utilities/mountDDI.swift index 7ecd95f..deb0d0d 100644 --- a/StikJIT/Utilities/mountDDI.swift +++ b/StikJIT/Utilities/mountDDI.swift @@ -47,6 +47,10 @@ func htons(_ value: UInt16) -> UInt16 { } func isMounted() -> Bool { + guard TunnelManager.shared.tunnelStatus == .connected else { + return false + } + var addr = sockaddr_in() memset(&addr, 0, MemoryLayout.size) addr.sin_family = sa_family_t(AF_INET) diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index 89c4696..a850d11 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -86,7 +86,7 @@ struct HomeView: View { themeExpansion?.resolvedAccentColor(from: customAccentColorHex) ?? .blue } - private var ddiMounted: Bool { isMounted() } + private var ddiMounted: Bool { mounting.coolisMounted } private var canConnectByApp: Bool { pairingFileExists && ddiMounted } private var pairingFileLikelyInvalid: Bool { (pairingFileExists || pairingFilePresentOnDisk) && @@ -119,6 +119,7 @@ struct HomeView: View { .padding(.horizontal, 20) .padding(.vertical, 30) } + .scrollIndicators(.hidden) if isImportingFile { Color.black.opacity(0.35).ignoresSafeArea() @@ -154,6 +155,9 @@ struct HomeView: View { refreshBackground() loadAppListIfNeeded() startWiFiMonitoring() + if tunnel.tunnelStatus == .connected { + MountingProgress.shared.checkforMounted() + } if autoStartVPN && tunnel.tunnelStatus == .disconnected { TunnelManager.shared.startVPN() } @@ -191,7 +195,9 @@ struct HomeView: View { } .onChange(of: tunnel.tunnelStatus) { _, newStatus in if newStatus == .connected { + loadAppListIfNeeded(force: cachedAppNames.isEmpty) runConnectionDiagnostics() + MountingProgress.shared.checkforMounted() } } .onChange(of: favoriteApps) { _, _ in @@ -1111,6 +1117,8 @@ struct HomeView: View { return } + guard tunnel.tunnelStatus == .connected else { return } + if !force && !cachedAppNames.isEmpty { return } DispatchQueue.global(qos: .userInitiated).async {