[2026-07-resubmission] Add guest browsing flow with App Attest session for App Review resubmission

Adds a pre-login public store locator (guest session via DeviceCheck/App
Attest, keychain-backed token storage) so the app no longer forces sign-in
before showing any content, plus updated support URL metadata.
This commit is contained in:
Daniel Arantes Loverde
2026-07-30 11:35:25 -03:00
parent 3e93196b92
commit 017bd7168f
21 changed files with 1140 additions and 136 deletions

View File

@@ -2,7 +2,7 @@ import Foundation
import SwiftUI
struct ContentView: View {
@State var root: RootFlow = DefaultTokenStore().jwt == nil ? .auth : .main
@State var root: RootFlow = .main
@State var selectedTab: MainTab = .home
private let tokenStore: TokenStore = DefaultTokenStore()
@State var appState = AppState()
@@ -167,20 +167,35 @@ struct ContentView: View {
appState.cart = cachedCart
}
// Anonymous session: the account address cache above doesn't apply
// restore the "ENTREGAR EM" label from the state/city picked via the
// public locator (GuestLocationStore persists this in Keychain across
// launches on its own; this just resyncs the display label with it).
if appState.session.isAuthenticated == false,
let guestState = GuestLocationStore.shared.selectedState,
let guestCity = GuestLocationStore.shared.selectedCity {
appState.address.display = "\(guestCity), \(guestState)"
dismissAddressPickerIfAddressExists()
}
// Always refresh profile when authenticated.
// This keeps profile/address/cart scope consistent after relogin
// and avoids stale local state during checkout payload generation.
do {
let response = try await ApiService().profile()
if response.error == false, let customer = response.result {
hydrateAppState(with: customer)
// Anonymous browsing has no session to refresh, so skip the call
// entirely rather than let it 401 and force-logout a guest.
if appState.session.isAuthenticated {
do {
let response = try await ApiService().profile()
if response.error == false, let customer = response.result {
hydrateAppState(with: customer)
}
} catch let error as ApiServiceError {
if case .sessionExpired = error {
forceLogoutToStart()
}
} catch {
// Keep local state when backend refresh fails transiently
}
} catch let error as ApiServiceError {
if case .sessionExpired = error {
forceLogoutToStart()
}
} catch {
// Keep local state when backend refresh fails transiently
}
await refreshFeatureFlags(forceRefresh: false)
@@ -418,13 +433,17 @@ struct AddressPickerModalView: View {
var body: some View {
NavigationStack {
AddressesView(
message: appState.address.onboardingMessage,
appState: $appState,
selectionMode: true
)
.onAppear {
appState.address.onboardingMessage = nil
if appState.session.isAuthenticated {
AddressesView(
message: appState.address.onboardingMessage,
appState: $appState,
selectionMode: true
)
.onAppear {
appState.address.onboardingMessage = nil
}
} else {
PublicLocationPickerView(appState: $appState)
}
}
}