This is Void, a simple meditation timer built with SwiftUI and the Composable Architecture (TCA). This repository demonstrates how to structure a small-to-medium app using the composable architecture.
This is by no means a perfect codebase, but I hope open-sourcing it might help others learn to use these wonderful tools. Feel free to open up an issue with any questions or comments you may have. Have fun and enjoy! 🙇♂️
Oh, and download the app! — And review it if you like it!
- SwiftUI + TCA: We organize the app into features, each with its own
@Reducer,State,Action, and SwiftUI views. - Dependency Injection: External services (e.g. HealthKit, Audio) are encapsulated as “clients” using TCA’s
DependencyKey. - Feature Folders: Each major app section (like Onboarding, Settings, Timer, etc.) lives in its own folder under
Features. - Reusable Components: Shared logic and helpers (e.g.
SoundManager,HealthKitClient) live inClientsandUtilities.
Below is a quick tour of the major directories and files.
Each feature folder contains:
- A Reducer (
FeatureNameFeature.swift) definingState,Action, and any side effects. - A View file (
FeatureNameView.swift) binding to the reducer’s state.
Highlights:
OnboardingFeature.swift
Handles the initial onboarding flow and requests HealthKit permissions.SettingsFeature.swift
Manages user preferences (timer length, intervals, ambient sounds).MeditationTimerFeature.swift
The main meditation timer logic (start/pause/play, timer completion).StatsFeature.swift
Fetches daily and weekly meditation stats via HealthKit.
The app’s root reducer is HomeFeature.swift. It ties all child features together (onboarding, settings, stats, meditation timer) and manages global app state.
Dependencies for external or system services are in Clients. Each client is implemented as a TCA DependencyKey:
HealthKitClient.swift
Reads and writes mindful sessions to HealthKit.SoundManagerandAmbientManager
Handle bell sounds, ambient loops, and audio session setup.
We store plain data models in the Models folder:
VoidSettings.swift: Holds user preferences (timer duration, ambient sound, etc.).MeditationState.swift: Tracks the current meditation session.Quote.swift: Simple struct for random quotes displayed on the home screen.
KeyboardView.swift: A custom numeric keyboard for user input.MovingLogo.swift&LogoView.swift: Animated shape-based logos.PulseView.swift: Overlay effect that shows pulsing visuals for transitions.Constants.swift: Global animations and style constants.PreventDeviceSleep.swift: Disables idle timer to keep the screen awake.
- App Launch
- The root
HomeFeaturechecks if HealthKit is authorized. If not, it presents the onboarding flow (OnboardingFeature).
- The root
- Meditation Flow
- The user sets their timer, intervals, and ambient sound in the Settings feature.
- Tapping Begin starts
MeditationTimerFeature, which counts elapsed time and plays interval/finish bells. - When the user stops, the session is saved to HealthKit (if over 10 seconds).
- Stats & Quotes
- StatsFeature queries HealthKit daily to display total minutes meditated and keeps track of your daily streak.
- QuotesView shows random mindfulness quotes on the home screen.
- Composable Architecture: Each feature has:
- A
@Reducerstruct withState,Action, andbody.
- A
- Dependency Clients:
- Wrapped in
@DependencyClient, withliveValue(real implementation) andtestValue(fake or mock). - Each client is registered in
DependencyValuesfor easy access in reducers.
- Wrapped in
- State Persistence:
- We use TCA’s
@Sharedstorage for persisting state across app launches (e.g.VoidSettings,MeditationState).
- We use TCA’s
I'm more than happy to accept contributions, but I also want to keep this app simple and functional. So perhaps open up an issue before coding up too much 😜