From Lifting to Coding
I’ve been lifting weights since the ’80s — around the same time I was coding on beige-box computers with mechanical keyboards. Fitness and tech have always been the two pillars of my life.
When the iPhone came out, I expected someone would finally build a fitness app that felt as elegant as the device itself. But every app I tried was either bloated with features I didn’t need, or so stripped down it was useless.
One day in the gym, my son looked at me fumbling with another clunky app and said:
“Dad, why don’t you just build your own?”
That one line started everything.
Why VigiGym Had to Be Different
I didn’t want another “everything app.”
• I don’t need AI coaching.
• I don’t need social feeds.
• I don’t need gamification.
What I do need:
• Log sets in seconds
• See progress clearly
• Stay focused on lifting
That became the DNA of VigiGym: a tool that gets out of your way and lets you train.
Building as a Solo Developer
I started almost from scratch with SwiftUI. No prior UIKit baggage, no team, no funding. Just me, late nights, and stubbornness.
I threw away three full versions. Fought Core Data sync issues at 2 a.m. And questioned if I could finish this.
What saved me was my design background: years of UX/UI work meant I always knew what felt right, even if the code didn’t.
Tech Stack & Snippets
• SwiftUI for the UI
• Core Data / SwiftData for local storage (no cloud)
• Charts for progress visualization
• WatchOS integration for logging workouts without a phone
Example: My 1RM Calculator (SwiftUI)
A key feature is the One Rep Max calculator. Instead of forcing users to do math mid-workout, the app calculates and adapts weights automatically — especially useful for pyramid and drop sets.
enum TrainingGoal: String, CaseIterable {
case hypertrophy = "Hypertrophy"
case strength = "Strength"
case endurance = "Endurance"
}
Picker("Training Goal", selection: $selectedTrainingGoal) {
ForEach(TrainingGoal.allCases, id: \.self) { goal in
Text(goal.rawValue).tag(goal)
}
}
.pickerStyle(.segmented)
Simple UI, big impact: it makes advanced set programming effortless.
Example: Color-Coded Muscle Mapping
Each workout card shows which muscles are primary vs. secondary. That meant creating a custom color schema instead of generic stock images.
Image("Dumbbell-Chest-Press")
.resizable()
.overlay(
Rectangle()
.fill(primaryMuscleColor.opacity(0.5))
)
It’s more work (I manually edited 242 exercise images at launch), but the clarity for lifters is unmatched.
Example: Apple Watch Integration
The Watch app was crucial: lifters don’t want to grab their phones between sets.
NavigationStack {
List(workouts) { workout in
Button(workout.name) {
startWorkout(workout)
}
}
.navigationTitle("VigiGym")
}
Minimal interface, designed to track quickly without distractions.
Lessons Learned
• Throw away code early – sometimes starting over is faster than patching.
• UX is king – lifters don’t care about clever architecture, they care about frictionless flow.
• Solo ≠ alone – docs, forums, and dev communities like dev.to were critical.
What’s Next
• Smarter auto-detection of set types (supersets, cluster sets, pyramids)
• Deeper analytics that show training patterns
• Expanded Apple Watch features
*Final Thoughts
*
Building VigiGym was harder than learning to code in the ’80s, and harder than sticking to a training routine. But it’s also been the most rewarding thing I’ve built.
This isn’t a corporate product — it’s a solo project I needed to exist.
VigiGym on the App Store
Learn more at vigigym.com
Written by a solo dev who still lifts heavy and still codes late into the night.
Top comments (0)