[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

@@ -167,36 +167,59 @@ extension StoreDetailView {
}
do {
let apiService = ApiService()
let infoResponse = try await apiService.storeInfo(storeId: storeId)
let catalogResponse = try await apiService.storeCatalog(storeId: storeId)
if appState.session.isAuthenticated {
let apiService = ApiService()
let infoResponse = try await apiService.storeInfo(storeId: storeId)
let catalogResponse = try await apiService.storeCatalog(storeId: storeId)
if infoResponse.error {
isLoading = false
reportStoreLoadFailure(infoResponse.message ?? "Não foi possível carregar a loja.", hadExistingContent: hadExistingContent)
return
}
if catalogResponse.error {
isLoading = false
reportStoreLoadFailure(catalogResponse.message ?? "Não foi possível carregar o catálogo.", hadExistingContent: hadExistingContent)
return
}
if infoResponse.error {
isLoading = false
reportStoreLoadFailure(infoResponse.message ?? "Não foi possível carregar a loja.", hadExistingContent: hadExistingContent)
return
}
if catalogResponse.error {
isLoading = false
reportStoreLoadFailure(catalogResponse.message ?? "Não foi possível carregar o catálogo.", hadExistingContent: hadExistingContent)
return
}
let normalizedCatalog = StoreCatalogNormalizer.sanitize(
categories: catalogResponse.result ?? [],
storeId: storeId
)
let normalizedCatalog = StoreCatalogNormalizer.sanitize(
categories: catalogResponse.result ?? [],
storeId: storeId
)
info = infoResponse.result
categories = normalizedCatalog
selectedCategoryId = StoreCatalogNormalizer.preferredCategoryId(
from: normalizedCatalog,
preferredId: selectedCategoryId
)
if let info = infoResponse.result {
AppContentCache.shared.set(info, for: infoCacheKey, ttl: AppCacheTTL.twoHours)
info = infoResponse.result
categories = normalizedCatalog
selectedCategoryId = StoreCatalogNormalizer.preferredCategoryId(
from: normalizedCatalog,
preferredId: selectedCategoryId
)
if let info = infoResponse.result {
AppContentCache.shared.set(info, for: infoCacheKey, ttl: AppCacheTTL.twoHours)
}
AppContentCache.shared.set(normalizedCatalog, for: catalogCacheKey, ttl: AppCacheTTL.twoHours)
} else {
// Anonymous browsing public/no-login store detail + catalog
// via pedifoods.com.br, mapped onto the same StoreInfoResult /
// StoreCatalogCategory models the authenticated path uses
// above, so the rest of this view doesn't need to know which
// source the data came from.
async let publicDetail = PublicLocationService.shared.fetchStoreDetail(identifier: storeId)
async let publicProducts = PublicLocationService.shared.fetchStoreProducts(storeId: storeId)
let (detail, products) = try await (publicDetail, publicProducts)
let normalizedCatalog = StoreCatalogNormalizer.sanitize(categories: products, storeId: storeId)
let publicInfo = StoreInfoResult(publicDetail: detail)
info = publicInfo
categories = normalizedCatalog
selectedCategoryId = StoreCatalogNormalizer.preferredCategoryId(
from: normalizedCatalog,
preferredId: selectedCategoryId
)
AppContentCache.shared.set(publicInfo, for: infoCacheKey, ttl: AppCacheTTL.twoHours)
AppContentCache.shared.set(normalizedCatalog, for: catalogCacheKey, ttl: AppCacheTTL.twoHours)
}
AppContentCache.shared.set(normalizedCatalog, for: catalogCacheKey, ttl: AppCacheTTL.twoHours)
errorMessage = nil
isLoading = false
} catch {