Building a Localized Food-Ordering App
At a glance
| Item | Summary |
|---|---|
| Purpose | Demonstrate localized strings, inflection, attributed text, formatted currency, dates, and a watch complication surface. |
| Architecture | Two app targets: a SwiftUI food-order flow and a small watch app; both favor view-local state and Foundation value types. |
| Main state owner | The iOS ContentView owns the current Order and presentation flow; detail views return new value objects by closure. |
Project structure
Caffe/
├── Caffe/
│ ├── CaffeApp.swift
│ ├── Models/{Food,Order}.swift
│ └── Views/*.swift
└── CaffeCompanion WatchKit Extension/
├── CaffeWatchApp.swift
├── ContentView.swift
└── ComplicationController.swift
The phone and watch folders are separate executable targets. The watch sample is a localized date view, not a synchronized order client.
Overall architecture
flowchart LR
App["CaffeApp"] --> Content["ContentView"]
Content --> Catalog["FoodItem.allFoodItems"]
Content --> Detail["FoodDetailsView"]
Detail --> Item["OrderItem callback"]
Item --> Order["ContentView.Order state"]
Content --> Receipt["ReceiptView"]
Watch["CaffeWatchApp"] --> WatchView["localized date ContentView"]
Watch --> Complication["ComplicationController"]
Reference code
Caffe/Caffe/Views/ContentView.swift:10 — the root view owns order and navigation state.
struct ContentView: View {
// ...
}Ownership and state
classDiagram
ContentView *-- Order : State
Order *-- OrderItem : values
OrderItem --> FoodItem : immutable reference value
FoodDetailsView *-- Int : quantity State
FoodDetailsView *-- FoodSize : selection State
ReceiptView *-- Double : tip State
CaffeWatchApp *-- ContentView : separate target
Ownership evidence
Caffe/Caffe/Views/FoodDetailsView.swift:10 — the detail receives parent selection and returns one completed value through a closure.
struct FoodDetailsView: View {
// ...
}ContentView is the order mutation authority. FoodDetailsView owns only draft selection state. ReceiptView receives an order value and owns only the editable tip. Static FoodItem instances form the in-memory catalog.
Class and protocol design
| Type | Responsibility | Design |
|---|---|---|
FoodItem, FoodSize, Ingredient |
Catalog, localization, and pricing | Immutable or value-semantic model types |
Order, OrderItem |
Cart items and derived totals | Value types with computed properties |
ContentView |
Feature composition and state transitions | SwiftUI View |
| Detail/receipt/card views | Focused presentation and local interaction | SwiftUI composition |
ComplicationController |
watchOS complication data-source hooks | CLKComplicationDataSource |
There is no app-defined service or repository protocol. Protocol orientation is limited to Swift value conformances and framework UI contracts.
Access control
| Boundary | Effect and rationale |
|---|---|
private @State in feature views |
Only the owning view can mutate order, selection, sheet, quantity, or tip state. |
private layout constants and action helpers |
Keeps presentation mechanics out of the view’s construction API. |
Model and view types are implicit internal |
Both targets compile only the code they need; no public library surface exists. |
Immutable let model fields |
Prevent partially mutated catalog entries and order items after construction. |
Caffe/Caffe/Models/Food.swift:26 shows immutable catalog fields; Caffe/Caffe/Views/ReceiptView.swift:12 shows private receipt state.
Logic ownership and placement
| Logic | Owner |
|---|---|
| Catalog prices and localized names | FoodItem / FoodSize |
| Quantity aggregation and totals | Order / OrderItem |
| Order mutation and sheet flow | iOS ContentView |
| Draft item construction | FoodDetailsView |
| Tip and localized receipt description | ReceiptView |
| watch date formatting | Watch ContentView |
Design patterns
| Pattern | Evidence | Purpose |
|---|---|---|
| Value model | Caffe/Caffe/Models/Order.swift:10 |
Makes order updates simple and local to SwiftUI state. |
| Unidirectional callback | Caffe/Caffe/Views/FoodDetailsView.swift:12 |
Sends a completed item upward without sharing mutable draft state. |
| Composition | Caffe/Caffe/Views/ContentView.swift:27 |
Builds the feature from small specialized views. |
| Framework data source | Caffe/CaffeCompanion WatchKit Extension/ComplicationController.swift:10 |
Supplies watchOS complication hooks without app infrastructure. |
Naming conventions
- Model names use business nouns (
FoodItem,OrderItem,Ingredient) and computed intent (totalPrice,localizedPrice). - Views use screen/component suffixes:
FoodDetailsView,ReceiptView, andFoodHeaderView. - Event handlers state transitions:
onSelectionComplete,onOrderComplete, andorderButtonTapped.
Architecture takeaways
- Keep draft interaction state in the child and commit a complete value to the parent.
- Put price and total calculations on value models, not inside layout code.
- Treat phone and watch targets as separate composition roots unless synchronization is explicitly implemented.
- Use access control to make SwiftUI state ownership obvious.
Source map
| Source | Role |
|---|---|
Caffe/Caffe/CaffeApp.swift:10 |
iOS composition root |
Caffe/Caffe/Models/Food.swift:10 |
Food catalog model |
Caffe/Caffe/Models/Order.swift:10 |
Order model |
Caffe/Caffe/Views/ContentView.swift:10 |
Feature state owner |
Caffe/CaffeCompanion WatchKit Extension/ContentView.swift:10 |
Separate watch presentation |