SwiftUI Food Logging REST API Integration Guide
Published June 6, 2026
SwiftUI food logging needs a reliable data source behind every tap. This guide covers SwiftUI food logging REST API integration from URLSession calls to a polished search and log flow using Calorie API through your backend proxy.
Architecture
SwiftUI View → NutritionService → your-backend.com → Calorie API
↑
no API key in IPA
Read hide nutrition API key mobile backend proxy before TestFlight.
Step 1: Define Codable Models
struct FoodSearchResult: Codable, Identifiable {
let id: Int
let name: String
let brand: String?
let calories: Double?
}
struct FoodSearchResponse: Codable {
let results: [FoodSearchResult]
let total: Int
}
Step 2: NutritionService With async/await
enum NutritionError: Error {
case badURL, badResponse, decoding
}
final class NutritionService {
private let baseURL: URL
init(baseURL: URL) {
self.baseURL = baseURL
}
func searchFoods(query: String, limit: Int = 15) async throws -> [FoodSearchResult] {
guard query.count >= 2 else { return [] }
var components = URLComponents(url: baseURL.appendingPathComponent("api/foods/search"), resolvingAgainstBaseURL: false)!
components.queryItems = [
URLQueryItem(name: "q", value: query),
URLQueryItem(name: "limit", value: String(limit))
]
guard let url = components.url else { throw NutritionError.badURL }
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw NutritionError.badResponse }
let decoded = try JSONDecoder().decode(FoodSearchResponse.self, from: data)
return decoded.results
}
}
Inject baseURL from a config plist pointing at your Next.js or Firebase proxy.
Step 3: Search ViewModel
@MainActor
final class FoodSearchViewModel: ObservableObject {
@Published var query = ""
@Published var results: [FoodSearchResult] = []
@Published var isLoading = false
private let service: NutritionService
private var searchTask: Task<Void, Never>?
init(service: NutritionService) {
self.service = service
}
func onQueryChange() {
searchTask?.cancel()
searchTask = Task {
try? await Task.sleep(nanoseconds: 300_000_000)
guard !Task.isCancelled else { return }
isLoading = true
defer { isLoading = false }
do {
results = try await service.searchFoods(query: query)
} catch {
results = []
}
}
}
}
Step 4: SwiftUI Search View
struct FoodSearchView: View {
@StateObject var viewModel: FoodSearchViewModel
var body: some View {
List(viewModel.results) { food in
VStack(alignment: .leading) {
Text(food.name).font(.headline)
if let brand = food.brand { Text(brand).font(.caption) }
if let cal = food.calories {
Text("\(Int(cal)) kcal / 100g").foregroundStyle(.secondary)
}
}
}
.searchable(text: $viewModel.query)
.onChange(of: viewModel.query) { _, _ in viewModel.onQueryChange() }
.overlay { if viewModel.isLoading { ProgressView() } }
}
}
Step 5: Daily Log State
Store logged entries in SwiftData or Core Data:
struct FoodLogEntry: Identifiable {
let id: UUID
let foodId: Int
let name: String
let grams: Double
let calories: Double
let proteinG: Double
let carbsG: Double
let fatG: Double
let loggedAt: Date
}
Fetch detail on select, scale nutrients, append to today's section.
Step 6: Barcode With AVFoundation
Use DataScannerViewController (iOS 16+) or a third-party scanner. Send UPC to /api/foods/barcode/{upc} on your backend.
Tips for App Store Quality
| Tip | Why |
|---|---|
| Debounce 300 ms | Saves API quota |
| Cache recents | Faster repeat logs |
| Empty states | Reduces confusion |
| Haptic on log | Confirms action |
Related Guides
Frequently Asked Questions
How do I integrate a food API in SwiftUI?
Create Codable models, build a NutritionService with URLSession async/await calling your backend proxy, bind search results to a List with searchable, and scale per-100g nutrients when logging.
Should SwiftUI apps call Calorie API directly?
Only for local debugging. Production iOS apps should proxy through your server so the API key is not embedded in the IPA.
What is the best REST API for SwiftUI food logging?
Calorie API provides search, barcode, and per-100g macros in JSON ideal for SwiftUI Codable models and macro tracking apps.
How do I debounce food search in SwiftUI?
Cancel the previous Task on each query change, sleep 300 ms, then call searchFoods. This prevents one API call per keystroke.
Can I use SwiftData to store food logs?
Yes. Store scaled macros and food metadata locally after fetching detail from Calorie API via your backend proxy.
