Back to blog

Building BreathFlow: A Native iOS Breathing App with SwiftUI

Why I built a guided breathing app for iOS, and what I learned shipping SwiftUI with HealthKit, CloudKit, and SwiftData.

6 min read
Gagan Deep Singh

Gagan Deep Singh

Founder | GLINR Studios


I've been a web developer for most of my career. React, Next.js, TypeScript -- that's my comfort zone. So when I decided to build a native iOS app from scratch, I knew I was signing up for a significant learning curve. What I didn't expect was how much I'd enjoy it.

The Problem With Breathing Apps

Breathing apps fall into two camps. The first camp is too simple -- a circle that expands and contracts, nothing else. You use it once, forget about it, and it collects dust on your home screen. The second camp is the opposite: packed with features, gamification, social feeds, and subscription paywalls for things that should be free. Neither camp actually helps you build a consistent practice.

I wanted something in the middle. Focused enough to feel calm, structured enough to create habits, and grounded in how breathing actually affects the nervous system.

Why SwiftUI and iOS 17

I chose SwiftUI targeting iOS 17+ for a few reasons. The framework has matured significantly, and the new Observable macro made state management feel genuinely clean rather than ceremonial. The Observation framework replaced the older @ObservedObject and @StateObject patterns in a way that clicked with how I already think about reactivity from web development.

iOS 17 also unlocked APIs I needed -- particularly improvements to HealthKit authorization flows and TipKit for surfacing contextual guidance without being annoying about it. Going iOS 17+ meant I could write modern Swift without worrying about backwards compatibility hacks.

Evidence-Based Patterns, Not Just Timers

The core of BreathFlow is a set of breathing techniques backed by research. Box breathing (4-4-4-4) is used by Navy SEALs for stress regulation and focus. The 4-7-8 pattern, popularized by Andrew Weil, is particularly effective for sleep onset. Physiological sighs -- a double inhale through the nose followed by a long exhale -- are the fastest way to reduce acute stress, based on work from the Huberman Lab and Stanford research.

Each pattern has a description of what it does and when to use it. I didn't want to just hand someone a timer and say "breathe." Context matters. If you're anxious before a meeting, that calls for something different than if you're winding down before sleep.

Programs, Not Sessions

The feature I'm most proud of is the program structure. Instead of dropping users into a pattern picker and hoping they figure it out, BreathFlow has guided programs -- multi-week sequences that build on each other.

A "Stress Reset" program might start with physiological sighs in week one, layer in box breathing in week two, and introduce longer 4-7-8 sessions in week three. The idea is progressive overload applied to breath training. It mirrors how you'd approach any physical practice: start accessible, build consistency, then increase depth.

Programs also have check-in moments. After each session, you rate how you feel on a simple scale. That data feeds into the transformation insights.

Mood Tracking and Insights

Every session ends with a quick mood check. No lengthy journaling prompts -- just a mood rating before and after. Over time, BreathFlow surfaces patterns. If your post-session mood consistently improves after box breathing but not after 4-7-8, it tells you that. If you tend to feel more stressed on Mondays, it notices that too.

This was the most complex piece to build on the data side. I had to think carefully about aggregation windows, how to handle sparse data, and how to present insights without making users feel surveilled. The goal is useful, not creepy.

HealthKit Integration

Integrating with Apple Health was one of the highlights of the project. BreathFlow writes mindful session minutes to HealthKit after each session completes, so your breathing practice shows up alongside your workouts and sleep data in the Health app.

The authorization flow is well-documented but requires careful handling. You request permission, the user sees the system sheet, and you can't assume they granted it -- even if they did. Every write operation has to handle the case where permission was revoked. Getting this right took a few iterations.

Reading heart rate data (when available from Apple Watch) is on the roadmap. The idea is to show real-time HRV response during sessions, which would make the feedback loop much tighter.

CloudKit and SwiftData

Local persistence is handled with SwiftData, which is Apple's newer persistence framework built on top of Core Data. The declarative model definition is pleasant -- you annotate your Swift types with @Model and the framework handles the rest.

CloudKit sync is enabled through SwiftData's ModelConfiguration with a CloudKit container identifier. In theory this is straightforward. In practice, getting sync to behave correctly across devices required understanding conflict resolution, how CloudKit handles schema migrations, and the importance of not mutating model properties in ways that create unnecessary sync events. There were a few days where I was deep in Console logs watching CloudKit traffic to understand what was happening.

It works reliably now. Your programs, sessions, and mood history sync across your iPhone and iPad automatically.

StoreKit 2

Subscriptions are handled with StoreKit 2, which is a significant improvement over the original StoreKit. The async/await based API fits naturally into modern Swift, and transaction verification is handled in a way that makes it harder to get wrong.

BreathFlow has a free tier with core patterns and limited history, and a Pro subscription that unlocks programs, full history, and insights. I kept the paywall simple and transparent -- you can see exactly what you're getting before you subscribe.

Going From Web to Native

The mental model shift was the hardest part. In web development, you think in terms of the network, the DOM, and JavaScript's single-threaded event loop. In SwiftUI, you're thinking about the view hierarchy, the main actor, and how state flows through a tree of value types.

The Swift concurrency model (async/await with actors) is elegant once it clicks, but getting there took time. I also had to unlearn habits around styling -- there's no CSS, and Auto Layout has been mostly replaced by SwiftUI's layout system, which is powerful but different.

What I didn't expect was how much I'd appreciate the compiler. Swift's type system catches a category of bugs at compile time that would slip through to runtime in TypeScript. That tradeoff -- more ceremony upfront, fewer surprises in production -- felt worth it.

Where It Stands

BreathFlow is currently in App Store review. I submitted it last week and I'm in the waiting period that every iOS developer knows well. The review process has historically taken anywhere from 24 hours to a week, and there's not much to do but wait and prepare for any feedback.

If it clears review without issues, it'll be available on the App Store shortly. If you're interested, I'll post an update here when it goes live.

Building something native after years of web work was genuinely fun. The constraints of the platform -- battery, privacy permissions, the review process -- push you to be more intentional. I'm already thinking about what comes next.


Contact