- SwiftUI + async/await architecture - Barcode scanning with AVFoundation - Product display with score ring, NOVA badge, nutrition - Alternatives with sort/filter - Auth (login/register) - Favorites & history - Account management - Dark theme - Connected to food.payfrit.com API (Open Food Facts proxy) Co-Authored-By: Claude <noreply@anthropic.com>
85 lines
2.5 KiB
Swift
85 lines
2.5 KiB
Swift
import Foundation
|
|
|
|
struct Alternative: Identifiable {
|
|
let id: Int
|
|
let product: Product
|
|
let price: Double?
|
|
let distance: Double? // miles from user
|
|
let deliveryUrl: String?
|
|
let pickupUrl: String?
|
|
let buyUrl: String?
|
|
let isSponsored: Bool
|
|
|
|
init(json: [String: Any]) {
|
|
id = JSON.parseInt(json["id"] ?? json["alternativeId"])
|
|
|
|
if let productData = json["product"] as? [String: Any] {
|
|
product = Product(json: productData)
|
|
} else {
|
|
// Product data might be at the root level
|
|
product = Product(json: json)
|
|
}
|
|
|
|
if let p = json["price"] as? Double, p > 0 {
|
|
price = p
|
|
} else if let p = json["Price"] as? Double, p > 0 {
|
|
price = p
|
|
} else {
|
|
price = nil
|
|
}
|
|
|
|
if let d = json["distance"] as? Double, d > 0 {
|
|
distance = d
|
|
} else if let d = json["Distance"] as? Double, d > 0 {
|
|
distance = d
|
|
} else {
|
|
distance = nil
|
|
}
|
|
|
|
deliveryUrl = JSON.parseOptionalString(json["deliveryUrl"] ?? json["DeliveryUrl"])
|
|
pickupUrl = JSON.parseOptionalString(json["pickupUrl"] ?? json["PickupUrl"])
|
|
buyUrl = JSON.parseOptionalString(json["buyUrl"] ?? json["BuyUrl"])
|
|
isSponsored = JSON.parseBool(json["isSponsored"] ?? json["IsSponsored"] ?? json["sponsored"])
|
|
}
|
|
|
|
init(
|
|
id: Int,
|
|
product: Product,
|
|
price: Double? = nil,
|
|
distance: Double? = nil,
|
|
deliveryUrl: String? = nil,
|
|
pickupUrl: String? = nil,
|
|
buyUrl: String? = nil,
|
|
isSponsored: Bool = false
|
|
) {
|
|
self.id = id
|
|
self.product = product
|
|
self.price = price
|
|
self.distance = distance
|
|
self.deliveryUrl = deliveryUrl
|
|
self.pickupUrl = pickupUrl
|
|
self.buyUrl = buyUrl
|
|
self.isSponsored = isSponsored
|
|
}
|
|
|
|
// MARK: - Computed Properties
|
|
var hasDelivery: Bool { deliveryUrl != nil }
|
|
var hasPickup: Bool { pickupUrl != nil }
|
|
var hasBuyLink: Bool { buyUrl != nil }
|
|
|
|
var formattedPrice: String? {
|
|
guard let price = price else { return nil }
|
|
return String(format: "$%.2f", price)
|
|
}
|
|
|
|
var formattedDistance: String? {
|
|
guard let distance = distance else { return nil }
|
|
if distance < 0.1 {
|
|
return "Nearby"
|
|
} else if distance < 1 {
|
|
return String(format: "%.1f mi", distance)
|
|
} else {
|
|
return String(format: "%.0f mi", distance)
|
|
}
|
|
}
|
|
}
|