Wishlist: Planning travel in a SwiftUI app
At a glance
| Item | Summary |
|---|---|
| Purpose | Build a travel planning app that organizes trips into collections and tracks activity completion. |
| App architecture | A Swift sample bundle with entry-bearing project variants WithSwiftData, WithoutPersistence, each leading to SwiftUI APIs. |
| Main patterns | Delegate or data-source callbacks, Binding-based state propagation |
| Project style | 43 scanned source file(s) across Swift, organized around ranked entry, type, and file boundaries. |
| Execution model | Source-visible boundaries: await suspension point; none alone proves a background thread. |
| State/event model | Source-visible mechanisms: SwiftUI state property wrapper, @Observable. |
| Key frameworks/packages | SwiftUI, SwiftData, Foundation, PhotosUI, UIKit; these are source dependencies, not architecture labels. |
Project structure
Source bundle/
├── WithSwiftData/
│ └── Wishlist/
│ ├── WishlistApp.swift
│ └── Views/
│ ├── Trips/
│ │ ├── AddTripView.swift
│ │ └── TripCollectionView.swift
│ ├── SearchView.swift
│ └── Goals/
│ └── GoalsView.swift
└── WithoutPersistence/
└── Wishlist/
├── WishlistApp.swift
├── Views/
│ ├── Trips/
│ │ ├── AddTripView.swift
│ │ └── TripCollectionView.swift
│ ├── SearchView.swift
│ └── Goals/
│ └── GoalsView.swift
└── Models/
├── DataSource.swift
└── TripEditModel.swift
Structure observations
- Architecturally prominent files are ranked from entry points and role-named declarations; resource-only paths are omitted.
- Primary languages: Swift.
- The verified tree contains 7 project/configuration file(s) and 84 source declaration(s).
Overall architecture
flowchart LR
Bundle["Sample bundle"]
V1["WithSwiftData"]
V2["WithoutPersistence"]
Boundary["SwiftUI APIs"]
Bundle --> V1
V1 --> Boundary
Bundle --> V2
V2 --> Boundary
Reference code
WithSwiftData/Wishlist/WishlistApp.swift:11 — architecture anchor
@main
struct WishlistApp: App {
let container: ModelContainer = {
do {
let modelContainer = try ModelContainer(for: Trip.self, Activity.self, TripImage.self, Goal.self, TripGoal.self, ActivityGoal.self)
try SampleData.seedIfNeeded(in: modelContainer.mainContext)
return modelContainer
} catch {
fatalError("Could not create model container: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
.preferredColorScheme(.dark)
}
.modelContainer(container)
}
}Interpretation
The branches represent separate entry-bearing project variants in the downloaded bundle, not runtime calls between those variants. Each branch is intentionally collapsed at the documented framework boundary; the detailed target-local flow remains in the cited files. Ownership is claimed only where the next section cites a stored property or assignment.
Ownership and state
classDiagram
SearchResultsListView *-- Array : trips
SearchResultsListView *-- Array : activities
SearchResultsListView *-- String : searchText
SearchResultsListView o-- ID : namespace
Ownership evidence
WithSwiftData/Wishlist/Views/SearchView.swift:52 — stored dependency or nearest verified ownership anchor
private struct SearchResultsListView: View {
// ...
@Query(sort: \Trip.name) private var trips: [Trip]
// ...
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
SearchResultsListView |
Array (trips) |
owns value state | Owning lexical scope |
SearchResultsListView |
Array (activities) |
owns value state | Owning lexical scope |
SearchResultsListView |
String (searchText) |
owns value state | App/module collaborators |
SearchResultsListView |
ID (namespace) |
stores or receives | App/module collaborators |
Composition arrows indicate a source-visible construction expression or locally owned value state; aggregation means the owner stores or receives a dependency without proving exclusive lifetime ownership.
Concurrency, scheduling, and thread safety
Evidence limit: actor isolation, async/await, or Task creation does not by itself prove background-thread execution; Sendable conformance alone does not prove thread-safe mutation.
| Concern | Source mechanism | Verified placement or handoff | Evidence |
|---|---|---|---|
| Suspension boundary | await suspension point |
The source declares or crosses an asynchronous boundary; it does not by itself establish background execution. | WithSwiftData/Wishlist/Views/Trips/AddTripView.swift:113 |
@MainActor/MainActor.run, DispatchQueue.main, and RunLoop.main are reported as distinct isolation, queue, and event-loop mechanisms. A plain Task is kept separate from Task.detached; neither is labeled as a background thread.
Reference code
WithSwiftData/Wishlist/Views/Trips/AddTripView.swift:113 — representative execution boundary
private struct TripImagePicker: View {
// ...
selectedPhotoData = try await newPhoto.loadTransferable(type: Data.self)
// ...
}State propagation, frameworks, and dependencies
Evidence limit: an import proves a source-level compilation dependency at the cited line; it does not prove runtime use, architectural adoption, or whether a Swift package is a direct application dependency.
| Category | Mechanism or module | Verified role | Evidence |
|---|---|---|---|
| State propagation | SwiftUI state property wrapper |
A SwiftUI property wrapper supplies or observes UI state. | WithSwiftData/Wishlist/Views/SearchView.swift:18 |
| State propagation | @Observable |
Observation macro publishes source-visible changes. | WithSwiftData/Wishlist/Views/Trips/AddTripView.swift:164 |
| Source import | SwiftUI |
The cited file imports this module; runtime use and architectural role are not inferred. | WithSwiftData/Wishlist/ContentView.swift:8 |
| Source import | SwiftData |
The cited file imports this module; runtime use and architectural role are not inferred. | WithSwiftData/Wishlist/ContentView.swift:9 |
| Source import | Foundation |
The cited file imports this module; runtime use and architectural role are not inferred. | WithSwiftData/Wishlist/Models/Activity.swift:8 |
| Source import | PhotosUI |
The cited file imports this module; runtime use and architectural role are not inferred. | WithSwiftData/Wishlist/Views/Trips/AddTripView.swift:9 |
| Source import | UIKit |
The cited file imports this module; runtime use and architectural role are not inferred. | WithSwiftData/Wishlist/Models/TripImage.swift:10 |
| Source import | UniformTypeIdentifiers |
The cited file imports this module; runtime use and architectural role are not inferred. | WithSwiftData/Wishlist/Views/Trips/TripImageView.swift:9 |
receive(on:) describes downstream delivery scheduling, while subscribe(on:) describes upstream subscription/request/cancel scheduling. An import Combine alone establishes neither behavior nor a Store, reducer, Redux, or other application architecture.
Class and protocol design
WithSwiftData/Wishlist/WishlistApp.swift:12 — representative type boundary
@main
struct WishlistApp: App {
// ...
let modelContainer = try ModelContainer(for: Trip.self, Activity.self, TripImage.self, Goal.self, TripGoal.self, ActivityGoal.self)
// ...
}| Type | Responsibility | Depends on or conforms to |
|---|---|---|
WishlistApp |
Application entry and top-level composition | App |
WishlistApp |
Application entry and top-level composition | App |
AddTripView |
User-interface presentation and input forwarding | View |
Model |
Feature data or observable state | Concrete collaborators/imported frameworks |
AddTripView |
User-interface presentation and input forwarding | View |
Model |
Feature data or observable state | Concrete collaborators/imported frameworks |
SearchView |
User-interface presentation and input forwarding | View |
SearchResultsListView |
User-interface presentation and input forwarding | View |
SearchItemView |
User-interface presentation and input forwarding | View |
TripSearchSectionView |
User-interface presentation and input forwarding | View |
No local protocol conformance is claimed as protocol-oriented design; external framework conformances are listed only as dependencies.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
ordinalModifier (WithSwiftData/Wishlist/Models/Goal.swift:56) |
fileprivate |
Use is restricted to this source file. | Inference: share with same-file helpers or extensions without exposing the symbol module-wide. |
allTrips (WithSwiftData/Wishlist/Models/SampleData.swift:58) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
allTripGoals (WithSwiftData/Wishlist/Models/SampleData.swift:182) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
allActivityGoals (WithSwiftData/Wishlist/Models/SampleData.swift:200) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
Reference code
WithSwiftData/Wishlist/Models/Goal.swift:56 — representative boundary
fileprivate var ordinalModifier: String {
target == 1 ? "first" : "\(target.ordinal)"
}Swift declarations without a modifier are internal; explicit private, fileprivate, private(set), public, or open entries above are interpreted by language semantics. Objective-C/C samples instead rely on header and implementation boundaries, which are not equivalent to Swift lexical privacy.
Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application entry and top-level composition | WishlistApp |
The source’s App suffix makes this role explicit. |
| Supplies data through a callback contract | DataSource |
The source’s DataSource suffix makes this role explicit. |
| Feature data or observable state | Model, TripEditModel |
The source’s Model suffix makes this role explicit. |
| User-interface presentation and input forwarding | ActivityItemView, ActivityProgressView, ActivitySearchSectionView, AddTripView |
The source’s View suffix makes this role explicit. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| Delegate or data-source callbacks | WithoutPersistence/Wishlist/Models/DataSource.swift:16 |
Callback protocols invert event delivery back into the sample’s owner. |
| Binding-based state propagation | WithSwiftData/Wishlist/Views/Trips/ActivitySection.swift:15 |
A binding exposes controlled read/write access while the upstream owner remains authoritative. |
Naming conventions
- Types: App: WishlistApp; DataSource: DataSource; Model: Model, TripEditModel; View: ActivityItemView, ActivityProgressView, ActivitySearchSectionView, AddTripView, ContentView.
- Protocols: no local protocol declaration in the scanned source.
- Methods:
saveImageToTemporaryDirectory,addTrip. - Files:
WithSwiftData/Wishlist/WishlistApp.swift,WithoutPersistence/Wishlist/WishlistApp.swift,WithSwiftData/Wishlist/Views/Trips/AddTripView.swift,WithoutPersistence/Wishlist/Views/Trips/AddTripView.swift,WithSwiftData/Wishlist/Views/SearchView.swift,WithoutPersistence/Wishlist/Views/SearchView.swift.
Architecture takeaways
WishlistAppis the main source-visible entry or composition anchor for this sample.- Framework work reaches SwiftUI, SwiftData, PhotosUI, UIKit through a deliberately small high-level chain; the detailed API graph remains inside the cited implementation files.
- Stored-property evidence identifies lifecycle collaboration; it does not by itself prove exclusive object ownership.
- Access-control conclusions separate verified language visibility from the likely design rationale.
- The source does not justify labeling the design protocol-oriented.
Source map
| Source file | Relevant symbols |
|---|---|
WithSwiftData/Wishlist/WishlistApp.swift |
Cited implementation, WishlistApp |
WithSwiftData/Wishlist/Views/SearchView.swift |
Cited implementation, SwiftUI state property wrapper, SearchView, SearchResultsListView, SearchItemView, TripSearchSectionView, ActivitySearchSectionView |
WithSwiftData/Wishlist/Models/Goal.swift |
Cited implementation, Goal, TripGoal, ActivityGoal |
WithSwiftData/Wishlist/Models/SampleData.swift |
Cited implementation, SampleData |
WithoutPersistence/Wishlist/Models/DataSource.swift |
DataSource |
WithSwiftData/Wishlist/Views/Trips/ActivitySection.swift |
Cited implementation, ActivitySection, ActivitiesHeader, ActivityList, ActivityItemView, ActivityTextField |
WithSwiftData/Wishlist/Views/Trips/AddTripView.swift |
await suspension point, @Observable, PhotosUI, AddTripView, TripImagePicker, TripDetailsForm, TripTitleField, TripGroupPicker, Model |
WithSwiftData/Wishlist/ContentView.swift |
SwiftUI, SwiftData, ContentView |
WithSwiftData/Wishlist/Models/Activity.swift |
Foundation, Activity |
WithSwiftData/Wishlist/Models/TripImage.swift |
UIKit, TripImage |
WithSwiftData/Wishlist/Views/Trips/TripImageView.swift |
UniformTypeIdentifiers, TripImageView |
WithoutPersistence/Wishlist/WishlistApp.swift |
WishlistApp |
WithoutPersistence/Wishlist/Views/Trips/AddTripView.swift |
AddTripView, TripImagePicker, TripDetailsForm, TripTitleField, TripGroupPicker, Model |
WithoutPersistence/Wishlist/Views/SearchView.swift |
SearchView, SearchResultsListView, SearchSection, SearchItemView, SearchSectionView |
WithSwiftData/Wishlist/Views/Goals/GoalsView.swift |
GoalsView, AchievedGoalTile, GoalTile |
WithSwiftData/Wishlist/Views/Trips/TripCollectionView.swift |
TripCollectionView, TripCard, Size |