Models/Baby.swift
@Model
final class Baby {
@Attribute(.unique) var id: UUID
var name: String
var birthday: Date
/// Start of day prevents time-of-day discrepancies
var daysSinceBirth: Int {
let calendar = Calendar.current
let startOfBirth = calendar.startOfDay(for: birthday)
let startOfToday = calendar.startOfDay(for: Date())
return calendar.dateComponents(
[.day], from: startOfBirth, to: startOfToday
).day ?? 0
}
/// Formats age to match pediatric guidance: "D+388, 55w3d"
var ageDisplayString: String {
let totalDays = daysSinceBirth
let weeks = totalDays / 7
let remainingDays = totalDays % 7
return "D+\(totalDays), \(weeks)w\(remainingDays)d"
}
@Relationship(deleteRule: .cascade, inverse: \FoodLog.baby)
var foodLogs: [FoodLog] = []
@Relationship(deleteRule: .cascade, inverse: \SleepLog.baby)
var sleepLogs: [SleepLog] = []
// ...diaper and activity logs follow the same pattern
}
Baby Model & Age Engine: Every tracked log hangs off a baby profile, and parents read age the way pediatric guidance writes it — in days and weeks, not months. A SwiftData @Model with cascade relationships to every log type, plus a computed age engine that anchors on start-of-day so the count never flickers with the time of day.
Models/FoodLog.swift
@Model
final class FoodLog {
var id: UUID
var type: FoodType // breastfeeding, formula, solids…
var amount: Double?
var feedingUnit: FeedingUnit?
var startTime: Date
var endTime: Date?
var notes: String
var baby: Baby?
/// Session length, when an end time was recorded
var duration: TimeInterval? {
guard let endTime else { return nil }
return endTime.timeIntervalSince(startTime)
}
}
enum FoodType: String, Codable, CaseIterable {
case breastfeeding = "Breastfeeding"
case formula = "Formula"
case solids = "Solids"
case pumpedMilk = "Pumped Milk"
}
Typed Feeding Logs: Parents think in typed events — a breastfeed, a bottle, solids — each with different fields. Untyped notes would lose exactly the structure healthcare visits ask for. Each log is its own SwiftData model with a typed enum, optional amount and unit, and a computed duration — so the UI can render each feeding kind correctly without parsing.
ParPairApp.swift
@main
struct ParPairApp: App {
let container: ModelContainer
init() {
do {
let schema = Schema([
Baby.self,
FoodLog.self,
DiaperLog.self,
SleepLog.self,
ActivityLog.self,
])
let config = ModelConfiguration(
schema: schema, isStoredInMemoryOnly: false
)
self.container = try ModelContainer(
for: schema, configurations: [config]
)
} catch {
fatalError("Could not initialize ModelContainer: \(error)")
}
}
var body: some Scene {
WindowGroup { ContentView() }
.modelContainer(container)
}
}
SwiftData Container Setup: All five models need one persistent store, initialized before any view renders — and a schema failure should be loud, not silent data loss. Build the ModelContainer in the App initializer from an explicit Schema, store on-device, and inject it into the environment once.