[cart-checkout] Match selected address by coordinates before label, not after

CustomerAddress.id is genuinely optional (Services/ApiModels.swift:46) —
some address book entries have no id. AddressesView.selectAddress sets
appState.address.selectedId = address.id directly with no fallback, so
picking one of those addresses leaves selectedId nil. The matching
cascade in both CartView and CheckoutView+Logic then skipped straight to
a label match, which silently collides whenever two addresses share an
empty or duplicate label (common for unnamed entries), and finally fell
back to addresses.first — always redisplaying whatever's first in the
list regardless of what was tapped, with no error surfaced anywhere.

Coordinates are set immediately and reliably at selection time and are
far less likely to collide than a label. Checkout already had a lat/lng
fallback but ordered after the weak label match; promoted it ahead of
label matching in both files, and added the same fallback to Cart, which
didn't have one at all.
This commit is contained in:
Daniel Arantes Loverde
2026-07-10 10:49:18 -03:00
parent 4fc6a666b2
commit f706492823
2 changed files with 31 additions and 9 deletions

View File

@@ -296,6 +296,21 @@ struct CartView: View {
selectedCustomerAddress = nil selectedCustomerAddress = nil
} }
// id can be nil for some address book entries lat/lng is set
// immediately and reliably at selection time (AddressesView.
// selectAddress), so it's a stronger signal than the label match
// below, which silently collides whenever two addresses share an
// empty/duplicate label. Without this, an id-less address falls
// through to addresses.first and never actually "changes".
if selectedCustomerAddress == nil,
let lat = appState.address.latitude, let lng = appState.address.longitude {
selectedCustomerAddress = addresses.first { address in
guard let addrLat = address.latLong?.first,
let addrLng = address.latLong?.dropFirst().first else { return false }
return abs(addrLat - lat) < 0.00001 && abs(addrLng - lng) < 0.00001
}
}
if selectedCustomerAddress == nil { if selectedCustomerAddress == nil {
let display = appState.address.display let display = appState.address.display
.trimmingCharacters(in: .whitespacesAndNewlines) .trimmingCharacters(in: .whitespacesAndNewlines)

View File

@@ -92,6 +92,22 @@ extension CheckoutView {
selectedCustomerAddress = nil selectedCustomerAddress = nil
} }
// id can be nil for some address book entries lat/lng is set
// immediately and reliably at selection time (AddressesView.
// selectAddress), so it's a stronger signal than the label match
// below, which silently collides whenever two addresses share an
// empty/duplicate label. Without this ordered first, an id-less
// address falls through to addresses.first and never actually
// "changes" even though delivery to it is allowed.
if selectedCustomerAddress == nil,
let lat = appState.address.latitude, let lng = appState.address.longitude {
selectedCustomerAddress = addresses.first { address in
guard let addrLat = address.latLong?.first,
let addrLng = address.latLong?.dropFirst().first else { return false }
return abs(addrLat - lat) < 0.00001 && abs(addrLng - lng) < 0.00001
}
}
if selectedCustomerAddress == nil { if selectedCustomerAddress == nil {
let display = appState.address.display let display = appState.address.display
.trimmingCharacters(in: .whitespacesAndNewlines) .trimmingCharacters(in: .whitespacesAndNewlines)
@@ -103,15 +119,6 @@ extension CheckoutView {
} }
} }
if selectedCustomerAddress == nil,
let lat = appState.address.latitude, let lng = appState.address.longitude {
selectedCustomerAddress = addresses.first { address in
guard let addrLat = address.latLong?.first,
let addrLng = address.latLong?.dropFirst().first else { return false }
return abs(addrLat - lat) < 0.00001 && abs(addrLng - lng) < 0.00001
}
}
if selectedCustomerAddress == nil { if selectedCustomerAddress == nil {
selectedCustomerAddress = addresses.first selectedCustomerAddress = addresses.first
} }