diff --git a/Outpost/Root/SessionStore.swift b/Outpost/Root/SessionStore.swift index e085a6f..a22bf08 100644 --- a/Outpost/Root/SessionStore.swift +++ b/Outpost/Root/SessionStore.swift @@ -39,11 +39,27 @@ final class SessionStore { init(tokenStore: TokenStoring = KeychainTokenStore(), defaults: UserDefaults = .standard) { self.tokenStore = tokenStore self.defaults = defaults - self.isSignedIn = (try? tokenStore.token()) != nil self.cacheStore = (try? OfflineCacheStore.makeContainer()).map(OfflineCacheStore.init(modelContainer:)) - if isSignedIn, let serverURL { - (apiClient, cachingClient) = Self.makeAPIClient(serverURL: serverURL, tokenStore: tokenStore, cache: cacheStore) + let hasToken = (try? tokenStore.token()) != nil + let storedServerURL = defaults.string(forKey: Self.serverURLDefaultsKey).flatMap(URL.init(string:)) + + if hasToken, let storedServerURL { + isSignedIn = true + (apiClient, cachingClient) = Self.makeAPIClient(serverURL: storedServerURL, tokenStore: tokenStore, cache: cacheStore) + } else { + // Keychain and the sandboxed UserDefaults container don't + // always survive together — a Keychain item written by an + // older-signed build can outlive a reinstall that wipes the + // container (or vice versa), leaving a token with no server or + // a server with no token. Clear whichever half survived rather + // than showing a broken "signed in" UI with no working + // apiClient — a fresh sign-in rewrites both consistently. + if hasToken { + try? tokenStore.clear() + } + defaults.removeObject(forKey: Self.serverURLDefaultsKey) + isSignedIn = false } }