diff --git a/ios/WatchRunner Watch App/Services/NetworkService.swift b/ios/WatchRunner Watch App/Services/NetworkService.swift index efae0a8f..8c881134 100644 --- a/ios/WatchRunner Watch App/Services/NetworkService.swift +++ b/ios/WatchRunner Watch App/Services/NetworkService.swift @@ -455,17 +455,19 @@ class NetworkService { } func connectWebSocket(token: String, serverUrl: String) { - connectLock.lock() - defer { connectLock.unlock() } - webSocketQueue.async { [weak self] in guard let self = self else { return } + self.connectLock.lock() + defer { self.connectLock.unlock() } + // Prevent redundant connection attempts if self.currentConnectionState == .connecting || self.currentConnectionState == .connected { print("[WebSocket] Already connecting or connected, ignoring new connect request.") return } + + self.currentConnectionState = .connecting // Ensure any existing task is cancelled before starting a new one self.webSocketTask?.cancel(with: .goingAway, reason: nil) @@ -490,13 +492,12 @@ class NetworkService { return } - print("[WebSocket] Trying connecting to \(url)") - self.currentConnectionState = .connecting - var request = URLRequest(url: url) request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization") request.addValue("application/json", forHTTPHeaderField: "Content-Type") - + + print("[WebSocket] Trying connecting to \(url)") + self.webSocketTask = self.session.webSocketTask(with: request) self.webSocketTask?.resume() diff --git a/ios/WatchRunner Watch App/State/AppState.swift b/ios/WatchRunner Watch App/State/AppState.swift index 2c6392ec..df67e909 100644 --- a/ios/WatchRunner Watch App/State/AppState.swift +++ b/ios/WatchRunner Watch App/State/AppState.swift @@ -19,22 +19,34 @@ class AppState: ObservableObject { let networkService = NetworkService() private var wcService = WatchConnectivityService() private var cancellables = Set() + private var hasAttemptedConnection = false init() { - wcService.$token.combineLatest(wcService.$serverUrl) + wcService.$token.combineLatest(wcService.$serverUrl, wcService.$isFetched) .receive(on: DispatchQueue.main) - .sink { [weak self] token, serverUrl in - self?.token = token - self?.serverUrl = serverUrl - if let token = token, let serverUrl = serverUrl { - self?.isReady = true - // Auto-connect WebSocket here - self?.networkService.connectWebSocket(token: token, serverUrl: serverUrl) - } else { - self?.isReady = false - // Disconnect WebSocket if token or serverUrl become nil - self?.networkService.disconnectWebSocket() - } } + .sink { [weak self] (token: String?, serverUrl: String?, isFetched: Bool?) in + guard let self = self else { return } + + self.token = token + self.serverUrl = serverUrl + + if let token = token, let serverUrl = serverUrl, !token.isEmpty, !serverUrl.isEmpty { + self.isReady = true + // Only connect once when we have valid credentials and tried fetch from phone + if !self.hasAttemptedConnection && isFetched == true { + self.hasAttemptedConnection = true + print("[AppState] Connecting WebSocket to server: \(serverUrl)") + self.networkService.connectWebSocket(token: token, serverUrl: serverUrl) + } + } else { + self.isReady = false + if self.hasAttemptedConnection { + self.hasAttemptedConnection = false + // Disconnect WebSocket if token or serverUrl become invalid + self.networkService.disconnectWebSocket() + } + } + } .store(in: &cancellables) } diff --git a/ios/WatchRunner Watch App/State/WatchConnectivityService.swift b/ios/WatchRunner Watch App/State/WatchConnectivityService.swift index 720665c4..7db1f9a8 100644 --- a/ios/WatchRunner Watch App/State/WatchConnectivityService.swift +++ b/ios/WatchRunner Watch App/State/WatchConnectivityService.swift @@ -14,6 +14,7 @@ import Combine class WatchConnectivityService: NSObject, WCSessionDelegate, ObservableObject { @Published var token: String? @Published var serverUrl: String? + @Published var isFetched: Bool? private let session: WCSession private let userDefaults = UserDefaults.standard @@ -30,6 +31,7 @@ class WatchConnectivityService: NSObject, WCSessionDelegate, ObservableObject { // Load cached data self.token = userDefaults.string(forKey: tokenKey) self.serverUrl = userDefaults.string(forKey: serverUrlKey) + self.isFetched = false } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { @@ -58,7 +60,13 @@ class WatchConnectivityService: NSObject, WCSessionDelegate, ObservableObject { } func requestDataFromPhone() { + if self.isFetched == true { + print("[watchOS] Skipped fetch from phone due to tried.") + return + } + guard session.isReachable else { + self.isFetched = true print("[watchOS] Phone is not reachable") return } @@ -68,6 +76,7 @@ class WatchConnectivityService: NSObject, WCSessionDelegate, ObservableObject { guard let self = self else { return } print("[watchOS] Received reply: \(response)") DispatchQueue.main.async { + self.isFetched = true if let token = response["token"] as? String { self.token = token self.userDefaults.set(token, forKey: self.tokenKey) diff --git a/ios/WatchRunner Watch App/Views/ExploreView.swift b/ios/WatchRunner Watch App/Views/ExploreView.swift index c8c48829..692905c0 100644 --- a/ios/WatchRunner Watch App/Views/ExploreView.swift +++ b/ios/WatchRunner Watch App/Views/ExploreView.swift @@ -49,9 +49,6 @@ struct ExploreView: View { .environmentObject(appState) } else { ProgressView { Text("Connecting to phone...") } - .onAppear { - appState.requestData() - } } } .sheet(isPresented: $isComposing) {