Architecture
SwiftData
How I persisted favorites without making POI a SwiftData model.
How I saved favorites on device while keeping Domain as plain structs.
Favorites persist with SwiftData. Places from Geoapify do not; they are fetched on demand and cached briefly on disk inside POIRepository.
Why SwiftData here?
Favorites need:
- Storage across app launches
- Simple queries (is this id saved? list all)
- No backend to sync with (study app scope)
I picked SwiftData over Core Data for less boilerplate at this size. Apple-native, @Model, ModelContext.
What I did
FavoritePOI (@Model) in Data. Domain POI stays a struct. FavoritesRepository maps both ways.
Why (then)
I wanted to practice “persistence model ≠ domain model” the same way DTOs differ from entities for Geoapify.
What I’d reconsider
If favorites were the only representation of a place and never came from an API, I might use @Model as the single source of truth and skip the mapper. Here both tabs show POI, so the struct stayed central.
Domain vs persistence model
Domain: POI (struct, value type, no framework).
Data: FavoritePOI (@Model class).
@Model
final class FavoritePOI {
var id: String
var name: String
// ...
}
FavoritesRepository:
add(_ poi: POI)→ insertFavoritePOI(from: poi)fetchAll()→ map models back to[POI]
ViewModels call FavoritesUseCase, which calls FavoritesRepositoryProtocol. SwiftData never appears in Presentation.
ModelContext wiring
PersistenceDependencies creates a ModelContainer for FavoritePOI.self and passes ModelContext into FavoritesRepository and FavoritesUseCase. Once, in DI, not in Views.
Favorites tab flow
FavoritesViewModel→FavoritesUseCase.fetchAll()- UseCase → repository
- Repository fetches
FavoritePOIrows, returns[POI] - ViewModel updates UI state
- Swipe to delete → same chain
Detail uses the same UseCase for isFavorite and the heart button.
Could POI be @Model directly?
Yes. Simpler file count, one type for list and favorites. Downside: Domain would depend on SwiftData, and API-sourced fields (live Geoapify data) mix with persisted rows. I kept them separate to learn the mapper; I might merge on a production app if the domain model matched persistence 1:1.
Could I use UserDefaults or a JSON file?
Yes for a handful of favorites. SwiftData felt worth learning for queries and relationships if the app grew (folders, notes, sync later).