DEV Community

Achim Loobes
Achim Loobes

Posted on

Why I Built VigiGym: A Solo Dev’s Journey From ’80s Coding to an iOS Fitness App

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)
Enter fullscreen mode Exit fullscreen mode

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))
    )
Enter fullscreen mode Exit fullscreen mode

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")
}

Enter fullscreen mode Exit fullscreen mode

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)