Skip to content
_________________________
V.1.0.0 // SECURE CONNECTION
Return_to_logs
[LOG: 2026-03-22]//TIME: 13 MIN READ

How I Built 3 iOS Apps as a Solo Developer

TheChosenVictor Logo
Victor SolanoSystem Architect
EngineeringiOSLifeSystem

Three apps. One developer. No team. No funding. No advisors. Just me, Xcode, and a list of problems I wanted to solve.

The apps: SwipeClean (gamified photo cleanup), SipStreak (water intake habit tracker), VaultSnap (encrypted photo vault). All three are on the App Store. All three were built from scratch in Swift 6 with SwiftUI. All three shipped within the same window of focused, relentless work.

This post is about what that actually looks like. Not the highlight reel. The real thing.


#Why Three Apps

The honest answer is that each one taught me something the previous one couldn't.

SwipeClean was the first. A gamification problem. How do you take something boring (deleting photos) and make it addictive? XP systems, streaks, boss battles, level progression. I had to learn engagement mechanics from scratch. Duolingo was the reference point, not file managers. The core question: can you make a game that happens to clean your phone?

SipStreak was the opposite. Minimalism. A water tracker has no room for complexity. Eight glasses, a simple UI, HealthKit integration, a widget. The challenge wasn't building features. It was resisting the urge to add them. Every screen had to justify its existence. The core question: how little can you build and still solve the problem?

VaultSnap was the hard one. Security and encryption. AES-256-GCM via CryptoKit, biometric authentication, a decoy vault that opens with a different passcode. Zero tolerance for bugs because the user is trusting you with their private photos. The core question: can you build something that handles sensitive data correctly without a security team reviewing your code?

Three different markets. Three different design philosophies. Three different bets. I didn't plan it this way. Each app emerged from a gap I saw that I thought I could fill.


#The Stack

Every app runs the same foundation:

LayerChoice
LanguageSwift 6, strict concurrency
UISwiftUI (no UIKit, no Storyboards)
ArchitectureMVVM + Services
PersistenceSwiftData
Project generationXcodeGen (SipStreak, VaultSnap)
DependenciesSPM only, minimal
TargetiOS 17.0+

The stack is deliberately narrow. No UIKit. No Combine. No third-party networking libraries. No reactive frameworks. No pods.

Why this matters when you're alone: every dependency is a liability. When something breaks at 11 PM and there's no one to call, the last thing you want is a bug in someone else's code that you can't read, can't debug, and can't fix. Apple-native APIs break too, but at least the documentation exists and thousands of other developers are hitting the same issues.

Swift 6 strict concurrency was painful upfront. The compiler will not let you get away with anything. Data races that would've been silent runtime bugs in Swift 5 become compile-time errors. I spent entire days fixing concurrency warnings that seemed pedantic. But the result is code that doesn't crash in production from thread safety issues. When you don't have a QA team, the compiler is your QA team.

The architecture is the same across all three apps. Views (SwiftUI only) talk to ViewModels (@Observable, @MainActor). ViewModels talk to Services (pure logic, no SwiftUI imports). Services talk to Models (@Model, SwiftData). Each layer depends only on the layer below it. No circular dependencies. No god objects. If I need to rewrite the UI of any screen, the service layer doesn't change. If I need to swap the persistence layer, the views don't change.

This isn't clever. It's survival. When you're the only person who will ever read this code, structure is the only thing standing between you and a codebase you can't maintain six months from now.


#What Actually Took the Time

Here's what nobody tells you about building apps solo: the code is maybe 20% of the work.

The other 80% is everything else.

App Store screenshots. Each app needs screenshots for multiple device sizes. That's not "take a screenshot." That's design a background, pick fonts, write copy, frame the device, make sure the text is readable at thumbnail size, render at the right resolution, and upload through App Store Connect's interface that was clearly designed by someone who has never uploaded screenshots. For three apps, that's dozens of screenshots. I spent more time on screenshots than on some entire features.

Metadata. App name, subtitle, description, keywords, what's new, promotional text. All of it matters for search. All of it has character limits that force you to be precise. All of it needs to be written, revised, and tested against search results. For each app. In each locale you support.

Privacy policies and legal. Every app needs a privacy policy. If you use HealthKit (SipStreak does), you need a specific health data privacy disclosure. If you use the camera or photo library (all three do), you need usage descriptions that Apple will reject you for if they're too vague. I wrote privacy policies at 2 AM because the app review came back with a rejection I didn't expect.

Edge cases. What happens when the user denies photo access? What happens when HealthKit isn't available on their device? What happens when they delete the app and reinstall it? What happens when their iCloud storage is full? What happens when they upgrade from free to pro and the receipt hasn't synced yet? Every one of these is a half-day of work, minimum. None of them are features. They're just the tax you pay for shipping a real product.

Testing your own StoreKit integration. StoreKit 2 is better than StoreKit 1. That's a low bar. Sandbox testing still requires signing out of your Apple ID, signing into a sandbox account, making a purchase, verifying the transaction, testing restore, testing the case where the purchase is pending, and repeating this for every build. The sandbox environment is slow, flaky, and occasionally just stops working for no discernible reason.

I'd estimate that for every hour spent writing a feature, I spent four hours on everything around it. The ratio is brutal. And it never gets acknowledged because the "everything around it" part doesn't make for interesting blog posts or conference talks.


#The Hard Parts Nobody Warns You About

>Decision Fatigue

When you're on a team, you can say "what do you think?" and someone has an opinion. When you're alone, every decision is yours. And the decisions never stop.

Should this button be blue or green? Should the onboarding be 3 screens or 5? Should the free tier include streaks? Should I use a tab bar or a sidebar? Should the settings screen be a form or a list? Should I localize now or later?

None of these are hard decisions individually. But when you make 50 of them a day, six days a week, for months, the cumulative weight is exhausting. You start second-guessing choices you already made. You spend 45 minutes on a button color that no user will ever consciously notice.

The only defense I found: set a timer. If the decision doesn't have a clear winner in 10 minutes, pick one and move on. You can always change it later. You usually won't need to.

>Scope Creep

You're the product manager, the designer, the developer, and the tester. You're also the only person who can say "no, we're not building that." The problem is that you're saying no to yourself. And you're very persuasive.

Every app I built has features that shouldn't have made v1. SwipeClean's boss battle system could have waited. SipStreak's HealthKit integration could have been a v2 feature. VaultSnap's decoy vault was scope creep I justified as a "differentiator." Some of these turned out to be worth it. Some didn't. But all of them delayed the initial launch.

>The Gap Between "Works" and "Ships"

There's a specific moment in every project where the core functionality works. You can demo it. You could show someone and they'd understand what the app does. You're maybe 40% done.

The remaining 60% is: loading states, error handling, empty states, onboarding, settings, accessibility, haptic feedback, animations, icon design, launch screen, in-app purchase flow, restore purchases, rate prompts, analytics events, crash reporting, and the 200 small things that separate a prototype from a product.

This gap is where solo projects go to die. The exciting work is done. What's left is polish, infrastructure, and paperwork. There's no team to split it with. It's all you.

>Testing Your Own Work

You can't see your own blind spots. I tested every screen. I used every feature. I ran the apps daily. And users still found bugs in the first week that I'd never encountered.

Why? Because I always used the apps the way I designed them to be used. I never denied permissions because I always granted them. I never ran out of storage because my phone had plenty. I never tried to import 50,000 photos because my library was 8,000.

The solo developer's testing problem isn't laziness. It's that your mental model of the app is too complete. You know where every button leads. You know what every error means. You subconsciously avoid the paths that break.


#What Worked

XcodeGen for project generation. The .xcodeproj file is the single worst file format in software development. It's not human-readable. It causes merge conflicts with itself. It breaks if you look at it wrong. XcodeGen lets you define the project in a YAML file and generates the .xcodeproj from it. For SipStreak and VaultSnap, this was a significant quality-of-life improvement. When I add a new file group or change a build setting, I edit the YAML and regenerate. Clean, predictable, repeatable.

Strict architecture from day 1. MVVM+Services isn't optional for me anymore. It's the default. Every new file goes in the right layer before the first line of code is written. ViewModels never import SwiftData directly. Services never import SwiftUI. Models never contain business logic. This discipline feels slow on day 1. On day 90, when you need to refactor the persistence layer, it pays for itself ten times over.

Building in public on X. I post updates about what I'm working on. Not polished launches. Just "here's what I built today" with a screenshot. The audience is small. That's fine. The value isn't the followers. The value is the accountability. When you tell 30 people you're shipping this week, you ship this week. The public commitment turns an internal goal into an external obligation. Obligation is more reliable than motivation.

Swift 6 strict concurrency from the start. I mentioned this above, but it deserves its own bullet. Retrofitting strict concurrency onto an existing codebase is miserable. Starting with it is merely painful. The difference is that "painful at the start" produces clean, safe code from the first commit. "Miserable later" produces a week of compiler errors and regression bugs. If you're starting a new Swift project in 2026, turn strict concurrency on immediately. Don't tell yourself you'll adopt it later. You won't.


#What I'd Do Differently

Ship the first version sooner. Every app I built spent too long in the "almost ready" phase. The icon isn't perfect. The onboarding could be smoother. The animation timing feels slightly off. None of that matters for v1. Ship it. Get real users. Then polish. Polish before launch is a trap. It feels productive. It's not. It's the same avoidance behavior as "research" before starting. You're not making the product better. You're making yourself feel less scared about putting it out there.

Build the marketing page before the app. Not after. Writing the App Store description, designing the screenshots, and drafting the landing page before you write code forces you to articulate the value proposition. If you can't explain why someone should download this app in two sentences, you don't have a product yet. You have a project. Those are different things.

Pick one app and go deep instead of three in parallel. This is the advice I'd give anyone else but probably wouldn't follow myself. Three apps means three sets of metadata, three review cycles, three update schedules, three user bases to support. The breadth taught me a lot. But if my goal was revenue, I should have picked the most promising one after the first month and gone all-in. Spread thin is the default mode for solo developers. It feels productive because you're always busy. But busy and effective are different words for a reason.


#The Numbers

Since we're being specific:

  • Total lines of Swift code across all three apps: roughly 45,000
  • Time from first commit to App Store for SwipeClean: 6 weeks
  • Time from first commit to App Store for SipStreak: 4 weeks
  • Time from first commit to App Store for VaultSnap: 5 weeks
  • App Store rejections (total across all three): 4
  • Hours spent on App Store screenshots (total): probably 30+
  • Third-party dependencies (excluding AdMob): 0

Those timelines are compressed because I was working on these full-time, not side-project hours. If you're building nights and weekends, multiply by 3 or 4.


#The Job

Building apps alone is hard. That's not a complaint. That's the job description.

You don't get to specialize. You don't get to say "that's not my department." You don't get to hand off the boring parts. You're the designer, the developer, the PM, the QA engineer, the marketer, the support team, and the person who writes the privacy policy at 2 AM.

The upside is that every decision is yours. The downside is that every decision is yours.

If you're thinking about building something solo, the only advice that matters is this: start. Not tomorrow. Not after you've finished that Swift tutorial. Not after you've planned the perfect architecture. Start now. Ship something ugly. Improve it after real people use it.

The apps aren't perfect. They never will be. But they exist. They're on the App Store. People are using them.

That's the whole point.


Published: March 22, 2026

Share
Follow @TheChosenVictor on X

“End of transmission.”

[CLOSE_LOG]