How to Build a Habit Tracker App with SwiftUI (2026)
Production-grade guide. SwiftData persistence, streak logic, calendar heatmap, push notification reminders, RevenueCat paywall.
To build a habit tracker iOS app with SwiftUI in 2026: (1) model habits + completions as SwiftData @Models, (2) compute streaks from completion dates rather than storing them, (3) schedule UNCalendarNotificationTrigger reminders per habit, (4) gate unlimited habits + themes via RevenueCat paywall, (5) ship in 4–7 days with The Swift Kit. The full architecture, schema, and Swift code is below.
Ship your habit tracker this week.
The Swift Kit ships everything except the habit logic — design system, paywall, push notifications, all pre-wired.
The 7 Steps to Ship
Each step takes 4–8 hours with the Swift Kit. From scratch, double-to-triple that.
- 1
Step 1 — Project setup
Clone Swift Kit, run setup.sh, pick "Productivity" preset.
git clone <swift-kit-repo> MyHabitApp cd MyHabitApp ./setup.sh - 2
Step 2 — Model habits + completions in SwiftData
Habit owns a list of HabitCompletion records. Completion has a date (normalized to midnight). Use a @Model for native SwiftUI integration.
import SwiftData @Model final class Habit { var name: String var emoji: String var colorHex: String var reminderTime: Date? @Relationship(deleteRule: .cascade) var completions: [HabitCompletion] = [] var createdAt: Date init(name: String, emoji: String, colorHex: String) { self.name = name self.emoji = emoji self.colorHex = colorHex self.createdAt = .now } } @Model final class HabitCompletion { var date: Date // midnight in user timezone var habit: Habit? } - 3
Step 3 — Compute streaks (don't store them)
Walk backward from today, counting consecutive completed days. Computing on read is cheap and avoids stale-data bugs.
extension Habit { var currentStreak: Int { let dates = Set(completions.map { Calendar.current.startOfDay(for: $0.date) }) var streak = 0 var day = Calendar.current.startOfDay(for: .now) while dates.contains(day) { streak += 1 day = Calendar.current.date(byAdding: .day, value: -1, to: day)! } return streak } } - 4
Step 4 — Build the habit list + completion toggle
List of habits with a tap-to-complete circle. Toggling adds or removes today's HabitCompletion. Haptic + animation on complete.
List { ForEach(habits) { habit in HStack { Text(habit.emoji).font(.title) VStack(alignment: .leading) { Text(habit.name).font(.headline) Text("\(habit.currentStreak) day streak").font(.caption).foregroundStyle(.secondary) } Spacer() Button { toggle(habit) } label: { Image(systemName: isCompletedToday(habit) ? "checkmark.circle.fill" : "circle") .font(.title) } } } } - 5
Step 5 — Calendar heatmap view
GitHub-style calendar heat map showing last 90 days. Use LazyVGrid with 7 columns. Color intensity scales with completion.
LazyVGrid(columns: Array(repeating: .init(.fixed(20)), count: 7), spacing: 4) { ForEach(last90Days, id: \.self) { day in RoundedRectangle(cornerRadius: 4) .fill(color(for: day, habit: habit)) .frame(height: 20) } } - 6
Step 6 — Schedule push notification reminders
For each habit with a reminder time, schedule a daily UNCalendarNotificationTrigger. Cap at 64 (Apple's limit on pending notifications). Cancel today's when user marks complete.
func scheduleReminder(for habit: Habit) async throws { guard let time = habit.reminderTime else { return } let center = UNUserNotificationCenter.current() let content = UNMutableNotificationContent() content.title = habit.emoji + " " + habit.name content.body = "Time to log your habit" content.sound = .default let trigger = UNCalendarNotificationTrigger( dateMatching: Calendar.current.dateComponents([.hour, .minute], from: time), repeats: true ) let request = UNNotificationRequest(identifier: habit.id.uuidString, content: content, trigger: trigger) try await center.add(request) } - 7
Step 7 — Paywall gating + ship
Free tier: 3 habits, basic theme. Premium: unlimited habits, all themes, widget customization, CSV export. RevenueCat handles the rest. Ship with included App Store assets.
if !subscription.isPremium && habits.count >= 3 { showPaywall = true; return }
Frequently Asked Questions
How long does it take to build a habit tracker app with SwiftUI?
Should I use SwiftData or Core Data for habit tracker storage?
How do I implement streaks in SwiftUI?
How do I schedule habit reminder push notifications?
What pricing model works for habit tracker apps?
Does The Swift Kit help with habit tracker apps?
Keep exploring
Ship your iOS app 10× faster
The Swift Kit gives you a production-ready SwiftUI boilerplate — design system, paywall, auth, AI, all pre-wired. $99 one-time.
Get The Swift Kit — $99One-time purchase · Lifetime updates · 14-day refund