From 58b947e515c33a69afb7a1e7fb659acd6804e755 Mon Sep 17 00:00:00 2001 From: superdooper86 Date: Mon, 11 May 2026 10:46:11 +0200 Subject: [PATCH] fix: add KVO on webView.url to catch SPA pushState navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit didFinish only fires for cross-document (full page) navigations. After loading https://claude.ai/login the SPA redirects authenticated users via history.pushState to /new — this changes the URL visually but never fires didFinish, so auth was never detected. KVO on webView.url fires for every URL change including SPA pushState, covering the case where the app routes client-side after the initial page load. Both KVO and didFinish now call the same checkCurrentURL helper so detection is not missed regardless of navigation type. --- ClaudeChecker/Info.plist | 4 ++-- ClaudeChecker/LoginView.swift | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/ClaudeChecker/Info.plist b/ClaudeChecker/Info.plist index 7bac4ed..aa3738d 100644 --- a/ClaudeChecker/Info.plist +++ b/ClaudeChecker/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.1-beta.10 + 1.2.1-beta.11 CFBundleVersion - 58 + 59 LSMinimumSystemVersion 13.0 LSUIElement diff --git a/ClaudeChecker/LoginView.swift b/ClaudeChecker/LoginView.swift index 49ede14..f8be219 100644 --- a/ClaudeChecker/LoginView.swift +++ b/ClaudeChecker/LoginView.swift @@ -12,6 +12,12 @@ struct LoginWebView: NSViewRepresentable { let webView = WKWebView(frame: .zero, configuration: config) webView.navigationDelegate = context.coordinator + + // KVO on url catches SPA pushState navigations that don't fire didFinish + context.coordinator.urlObservation = webView.observe(\.url, options: [.new]) { [weak coordinator = context.coordinator] wv, _ in + coordinator?.checkCurrentURL(wv.url?.absoluteString) + } + webView.load(URLRequest(url: URL(string: "https://claude.ai/login")!)) return webView } @@ -25,22 +31,25 @@ struct LoginWebView: NSViewRepresentable { class Coordinator: NSObject, WKNavigationDelegate { let onAuthenticated: () -> Void var didAuthenticate = false + var urlObservation: NSKeyValueObservation? init(onAuthenticated: @escaping () -> Void) { self.onAuthenticated = onAuthenticated } - func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - guard !didAuthenticate else { return } - guard let url = webView.url?.absoluteString else { return } - // Stay on login/auth pages — user hasn't completed sign-in yet + func checkCurrentURL(_ url: String?) { + guard !didAuthenticate, let url else { return } if url.contains("/login") || url.contains("/auth") { return } - // Navigated away from login — server redirected us, so sign-in completed didAuthenticate = true DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.onAuthenticated() } } + + // Covers full cross-document navigations + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + checkCurrentURL(webView.url?.absoluteString) + } } }