How to Build a Hydration Reminder App in SwiftUI
A Hydration Reminder app tracks daily water intake and fires timely push notifications so users never forget to drink โ perfect for health-conscious iOS users who want a lightweight, glanceable wellness companion.
Prerequisites
- Xcode 16 or later installed on your Mac
- Basic familiarity with SwiftUI views and
@State/@Environment - An Apple Developer account (free tier works for Simulator; paid required for device + App Store)
1. Define the Data Model
Use SwiftData's @Model macro to persist each drink log entry with a timestamp and millilitre amount.
import SwiftData
import Foundation
@Model
final class DrinkLog {
var timestamp: Date
var millilitres: Int
init(millilitres: Int, timestamp: Date = .now) {
self.millilitres = millilitres
self.timestamp = timestamp
}
}
// In your App entry point:
// .modelContainer(for: DrinkLog.self)
2. Build the Core UI View
Display today's total with a Swift Charts ring gauge and a quick-add button row so logging a drink takes one tap.
import SwiftUI
import Charts
struct HydrationDashboard: View {
@Query(sort: \DrinkLog.timestamp, order: .reverse)
var logs: [DrinkLog]
let goal = 2500 // ml
var todayTotal: Int {
logs.filter { Calendar.current.isDateInToday($0.timestamp) }
.reduce(0) { $0 + $1.millilitres }
}
var body: some View {
VStack(spacing: 24) {
Chart {
SectorMark(
angle: .value("Drunk", min(todayTotal, goal)),
innerRadius: .ratio(0.65)
).foregroundStyle(.blue)
SectorMark(
angle: .value("Remaining", max(goal - todayTotal, 0)),
innerRadius: .ratio(0.65)
).foregroundStyle(.blue.opacity(0.15))
}
.frame(width: 180, height: 180)
Text("\(todayTotal) / \(goal) ml")
.font(.title2.bold())
}
.padding()
}
}
3. Implement Drink Water Nudges
Request notification permission on first launch and schedule hourly UNCalendarNotificationTrigger reminders between 8 AM and 8 PM.
import UserNotifications
func scheduleHydrationReminders() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
guard granted else { return }
center.removeAllPendingNotificationRequests()
for hour in stride(from: 8, through: 20, by: 2) {
var components = DateComponents()
components.hour = hour
components.minute = 0
let trigger = UNCalendarNotificationTrigger(
dateMatching: components, repeats: true)
let content = UNMutableNotificationContent()
content.title = "Time to hydrate ๐ง"
content.body = "Have a glass of water โ your body will thank you!"
content.sound = .default
let request = UNNotificationRequest(
identifier: "hydration-\(hour)",
content: content, trigger: trigger)
center.add(request)
}
}
}
Common Pitfalls
- Notification permission timing: always request permission in response to an explicit user action (e.g. tapping "Enable Reminders"), not on cold launch โ reviewers and users both expect the prompt to be contextual.
- SwiftData model migration: if you rename or add properties to
DrinkLogafter shipping, define aMigrationPlanor your users' containers will silently fail to load. - Charts on older OS: the Swift Charts framework requires iOS 16+; your deployment target is iOS 17+ here, but double-check the minimum in Xcode's project settings before archiving.
Monetization: Ad-Supported
An ad-supported model is the fastest path to revenue for a free utility like this โ integrate a banner ad SDK (Google AdMob or Apple Search Ads attribution) and show a tasteful banner on the history screen. For a premium upgrade path, pair ads with a StoreKit 2 Product.purchase() flow that unlocks an ad-free experience: present a single non-consumable IAP ("Remove Ads โ $1.99") and gate the ad views behind a persisted AppStorage flag that your StoreKit transaction listener updates on successful purchase. This keeps the free tier monetised while giving motivated users a clear upgrade path.
Ship Faster with Soarias
Soarias scaffolds the full SwiftData container, notification permission flow, Charts dashboard, and StoreKit IAP boilerplate in seconds โ skipping the hours of wiring up modelContainer, entitlements, and UNUserNotificationCenter delegates by hand. For a beginner project like this, that typically means going from zero to a working Simulator build in under 30 minutes rather than a full afternoon, leaving you free to focus on the design details and App Store listing that actually move downloads.
Related Tutorials
FAQ
Do I need an Apple Developer account?
You can build and run this app on the iOS Simulator for free with no account at all. To run on a physical iPhone you need a free Apple ID enrolled in Xcode. To distribute on TestFlight or the App Store you need a paid Apple Developer Program membership ($99/year).
How do I submit to the App Store?
Archive your app in Xcode (Product โ Archive), then use the Distribute App wizard to upload to App Store Connect. Fill in your app's metadata, screenshots (required for iPhone 6.9" and 6.5" sizes), privacy nutrition labels, and set your build to "Ready to Submit." Apple's review typically takes 24โ48 hours for a new app.
Last reviewed: 2026-05-12 by the Soarias team.