DEV Community

孫昊
孫昊

Posted on

Building 2 SwiftUI apps in parallel: TipJar Now + HabitHash dual launch

dev.to article #28 (paste-ready) — Building 2 SwiftUI apps in parallel: TipJar Now + HabitHash dual launch (8-week plan)

Calendar: 2026-05-29 09:00 PT (1 week after dev.to #27)
Tags: #ios #swift #swiftui #indie #productivity
Word count: 2000 (technical + reuse pattern + lessons learned)
Series: "Day 60 indie iOS dev" (part 4)


TL;DR

I built two iOS apps in parallel: TipJar Now (one-tap QR code tip jar for service workers) and HabitHash (pseudonymous habit tracker with git-like commit hash). Same SwiftUI / StoreKit 2 / @observable pattern, copy-paste-modify ~80% of code from existing apps. Each MVP is ~8 Swift files, ~600 lines total.

Here's the engineering approach + reuse pattern + 8-week plan.


Why 2 apps in parallel (instead of 1)

Conventional indie wisdom: ship one thing, focus, iterate. But:

  1. Marginal cost is near zero when you reuse 80% of code (IAPManager, SettingsView, paywall, OnboardingView all identical)
  2. 2 apps = 2 ASO slots + 2 keyword landings. Apple Search shows me on twice as many queries
  3. Cross-promo loop: footer in TipJar Now points to HabitHash and vice versa
  4. Data-rich: with 2 launches at once, I can compare conversion / retention / refund across niches faster than sequential

Risk: 2x review cycle, 2x rejection probability (per app, independent). Mitigated by the pre-flight checklist (see my 14 Rejection Reasons 1-pager).

The reuse pattern

After 4 iOS apps, I have a stable shared pattern:

<App>/
├── App/<App>App.swift          # @main + state injection (~15 LOC)
├── IAP/IAPManager.swift        # StoreKit 2 wrapper (~90 LOC, identical)
├── Models/<Domain>.swift       # Domain-specific (varies)
├── Services/<DomainStore>.swift # @Observable + persist (varies)
└── Views/
    ├── ContentView.swift       # Main UI (varies)
    ├── <Edit>View.swift        # Add/edit form
    ├── PaywallView.swift       # IAP unlock UI (~120 LOC, mostly identical)
    ├── SettingsView.swift      # Premium / About (~50 LOC, identical)
    └── OnboardingView.swift    # 3-screen tutorial (~80 LOC)
Enter fullscreen mode Exit fullscreen mode

~80% reusable:

  • IAPManager.swift: copy-paste, change premiumProductID
  • PaywallView.swift: copy-paste, change feature list
  • SettingsView About section: copy-paste, change Privacy Policy URL
  • OnboardingView: copy-paste, change copy

~20% domain-specific:

  • Models (Habit vs TipMethod vs Choice vs Event)
  • Store (HabitStore vs TipJarStore)
  • ContentView (grid vs QR display vs wheel)
  • Domain validation logic

TipJar Now: domain models

Service workers (waiters / bartenders / drivers) need to display QR code on demand for one-tap tip transfers. Multi-method support (PayPal / Venmo / WeChat / PayPay).

enum TipMethodKind: String, Codable, CaseIterable {
    case paypal, venmo, wechat, alipay, paypay, linePay
    case cashApp, zelle, revolut, wise

    var displayName: String { /* localized */ }
    var symbol: String { /* SF Symbol icon */ }
}

struct TipMethod: Identifiable, Codable {
    let id: UUID
    var kind: TipMethodKind
    var addressOrLink: String  // PayPal email / Venmo handle / QR URL
    var displayName: String?
    var qrImageData: Data?     // For WeChat / PayPay (custom QR)

    var paymentURL: URL? {
        // Build payment URL from kind + addressOrLink
    }
}
Enter fullscreen mode Exit fullscreen mode

QR generation uses CoreImage:

enum QRGenerator {
    static func image(from string: String) -> UIImage? {
        let context = CIContext()
        let filter = CIFilter.qrCodeGenerator()
        filter.message = Data(string.utf8)
        filter.correctionLevel = "M"
        guard let output = filter.outputImage else { return nil }
        let scaled = output.transformed(by: CGAffineTransform(scaleX: 10, y: 10))
        guard let cg = context.createCGImage(scaled, from: scaled.extent) else { return nil }
        return UIImage(cgImage: cg)
    }
}
Enter fullscreen mode Exit fullscreen mode

ContentView shows a single big QR card with method switcher in toolbar.

Free vs Pro:

  • Free: 1 method
  • Pro $1.99: unlimited methods + Apple Watch + custom themes + custom QR upload

HabitHash: domain models

Pseudonymous habit tracker. No account, no cloud (free). 5 habits max in free, with a "git-like" hash signature for visualizing your commit pattern.

struct Habit: Identifiable, Codable {
    let id: UUID
    var name: String
    var emoji: String
    var colorID: String
    var commits: Set<Date>  // Days committed

    var hashSignature: String {
        // Format: 4 hex digits (total commits) + 2 hex (last 30 days) + 2 hex (current streak)
        let count = commits.count
        let recent = commitCount(lastDays: 30)
        let streak = currentStreak()
        return String(format: "%04x%02x%02x", count, recent, streak)
    }

    func currentStreak() -> Int {
        var count = 0
        let cal = Calendar.current
        var day = cal.startOfDay(for: Date())
        while didCommit(on: day) {
            count += 1
            guard let prev = cal.date(byAdding: .day, value: -1, to: day) else { break }
            day = prev
        }
        return count
    }
}
Enter fullscreen mode Exit fullscreen mode

The hash signature is a fun differentiator — feels like git activity for habits. Programmer-targeted ICP loves this.

ContentView shows:

  • List of habits with name + emoji + streak
  • 7-day commit grid (last 7 days, green if committed)
  • Single tap to commit today

Free vs Pro:

  • Free: 5 habits, 30-day history, 7-day grid
  • Pro $2.99: unlimited habits + unlimited history + iCloud sync + widgets + Apple Watch

ICE scoring (why these 2 over others)

I had 10 iOS app candidates. Why these 2?

App Impact Conf Ease ICE
TipJar Now 8 9 9 6.48
HabitHash 9 8 7 5.04
PromptVault iOS 9 8 7 5.04
FocusBlock 8 8 7 4.48
WaterNow 7 8 8 4.48

TipJar Now wins ICE because:

  • High Impact: real pain point for service workers (no current iOS solution)
  • High Confidence: user testing shows clear value ("I'd pay $1.99 once for this")
  • High Ease: 1 main screen + 1 form + 1 paywall + Apple Watch = ~80 hr build

HabitHash wins ICE because:

  • High Impact: programmer-niche has high LTV (and high paywall conversion)
  • High Confidence: GitHub graph aesthetic is universally loved
  • Medium Ease: heatmap visualization + iCloud sync add complexity

8-week parallel plan

W1: TipJar Now skeleton (60 hr) | HabitHash skeleton (50 hr)
W2: TipJar Apple Watch          | HabitHash heatmap visualization
W3: TipJar widget               | HabitHash widget
W4: TipJar polish + onboarding  | HabitHash onboarding + iCloud sync
W5: Localization (en/ja/zh)     | Localization (en/ja/zh)
W6: Beta + bug fix              | Beta + bug fix
W7: ASC submit (TipJar)         | ASC submit (HabitHash)
W8: Apple review (both)         | Both go live + content launch
Enter fullscreen mode Exit fullscreen mode

Total: 4 weeks of mostly parallel work + 4 weeks of staggered launches. Both ship by Week 8.

What's hard

  1. App Store Review at scale: each app's review may take 24-72 hr. Submitting 2 simultaneously means 2x reject probability. Mitigation: use my 14 Rejection Reasons checklist before submitting.

  2. ASO competition: "tip jar" has low competition (good), "habit tracker" has high (bad). HabitHash needs niche differentiation: "pseudonymous habit tracker no account" (very specific keyword).

  3. Cross-promo balance: too aggressive (footer banner on every screen) feels spammy. Recommended: footer banner only in Settings + Onboarding screen 3.

  4. Code reuse cost: when you find a bug in IAPManager, you fix it in 4-5 places. Mitigation: extract to shared package (SPM local) eventually. For 4-app MVP, copy-paste is fine.

What's easy (because of 4 prior apps)

  1. ASC navigation: I now know exactly where Bundle ID / IAP / Pricing / Privacy / Submit pages are
  2. StoreKit 2 wiring: 90-line IAPManager copy-paste
  3. Paywall design: 120-line PaywallView with feature list update
  4. Localization: Same 3 languages (en/ja/zh-Hans), reuse phrases like "Unlock everything forever"

Anti-pattern: don't overcomplicate

Things I DIDN'T add to MVP (deferred to v1.1+):

  • Multi-currency support (TipJar) — defer
  • Heatmap year view (HabitHash) — defer to W2
  • AI suggestions for habits (HabitHash) — feature creep
  • Backend / cloud sync (both) — defer to Pro tier W4
  • Apple Watch independent app (both) — defer to v1.1
  • Custom QR image upload (TipJar) — defer to v1.1
  • Custom themes (both) — defer

MVP = "minimum viable", not "minimum interesting". Ship the core feature, validate, iterate.

What I'm tracking (Day 30 each app)

TipJar Now:
  - Downloads (target 200-500 organic Day 30)
  - Conversion to Pro (target 5-8%)
  - Top traffic source (Reddit r/bartending? TikTok? service-worker forums?)

HabitHash:
  - Downloads (target 300-800 — programmer ICP, broader reach)
  - Conversion to Pro (target 7-10%)
  - Streak distribution (D7 = how many users have 7-day streak?)
Enter fullscreen mode Exit fullscreen mode

I'll publish Day 30 numbers Substack #23 (likely 2026-06-30 or later).

If both apps clear $200/month sustained, I'll continue Wave 2 (Color Sweep game, ICE 3.92, deferred for higher-cert wins first).

If either fails (< $50/month), I'll kill it and double down on the survivor.


Code samples available

Both MVPs are open scaffolds:

Reuse pattern + IAPManager template: github.com/jiejuefuyou/autoapp-ios-template (placeholder)

See also


Tags

#ios #swift #swiftui #indie #productivity #app #development #testflight

A/B subject candidates

  1. "Building 2 SwiftUI apps in parallel: TipJar Now + HabitHash dual launch (8-week plan)"
  2. "How I built 2 indie iOS apps in 8 weeks (80% code reuse pattern)"
  3. "Day 30 indie iOS dev: shipping 2 new apps (the parallel reuse pattern)"

Cross-platform plan

Platform Date Variant
dev.to 2026-05-29 09:00 PT This piece
Substack 2026-05-27 (already covered in #18) n/a
X 2026-05-29 09:30 PT 8-tweet thread (TipJar + HabitHash demo gifs)
Reddit r/iOSProgramming 2026-05-29 22:00 PT Excerpt + link
HN Show HN 2026-05-30 06:00 PT "Show HN: 2 iOS apps in 8 weeks (80% code reuse)"
知乎专栏 2026-06-05 09:00 JST Chinese version
公众号 2026-06-07 09:00 JST Mobile-friendly version

Top comments (0)