Hyunjun Kim
← All work

Case 03 / 09

Momento

Directing AI to build the same app natively on iOS and Android — a couple's auto-matching memory app

In testingIdea→ship~7–8 weeks

2026.05 – 2026.06 / Solo (AI as the team) / iOS · Android

Problem
Logging every shared moment by hand is tedious — let the app notice when a couple is together.
My role
Planning · setting the bar · directing AI to build (iOS, Android, backend)
Stack
SwiftUI · Kotlin · Jetpack Compose · Supabase · WidgetKit

Results

Dev start → TestFlight (iOS & Android native)
~7–8 weeks
Dev start → TestFlight (iOS & Android native)
Built natively on iOS and Android
2 platforms
Built natively on iOS and Android
iOS feature modules
24
iOS feature modules
iOS uploaded (Android in progress)
TestFlight
iOS uploaded (Android in progress)

⚡ Idea→ship

~7–8 weeks

  1. 2026-03-21

    First prompt

  2. +45 days

    2026-05-05

    First working build

  3. +45 days

    2026-06-19

    First release · Latest

Dev start (04-27) → TestFlight ~7–8 weeks (~23 active days). ~5-week gap between idea and start (other projects in parallel)

01

Idea / Problem

First instruction: "When a couple is in roughly the same place (within ~10m) and one of them takes a photo, auto-collect that photo with its date and location — can this be done?" Original (KO): "커플 둘이 비슷한 공간(오차 10m)에 같이 있을 때 한 명이 사진을 찍으면, 그 사진을 날짜·위치와 함께 자동으로 모아주기 — 할 수 있나?"

Logging every shared moment by hand is tedious. The idea came from everyday life: what if the app itself noticed when two people were in the same place at the same time, and bundled that into a memory?

02

The Plan & Bar I Set

  • Planned the feature scope myself: pairing, timeline, diary, anniversaries, map, location check-in
  • Platform bar: iOS in SwiftUI, Android in Kotlin/Compose, each native. I made it a hard rule: "don't port iOS code to Android — follow each platform's conventions."
  • Design bar: "dakku" scrapbook tone (coral #E07A5F, polaroids / masking tape / handwriting)
  • A safety rule for AI work: explain side effects and get approval before any change
Momento — momento 02 1
Momento — momento 02 2
Momento — momento 02 3
Momento — momento 04 ios
iOS · SwiftUI
Momento — momento 04 android
Android · Jetpack Compose

03

Directing AI

Fig. momento-03

Directing AI

  1. 01 First instruction

    for iOS and Android each, build the same features the platform's own way.

  2. 02 What I checked

    I ran builds and screens and found where server dates broke and where the Android build stalled.

  3. 03 Correction

    I instructed by pointing at the cause — "build a decoder that flexibly accepts the server's various date formats," "the Android DI tool doesn't fit, switch to manual singletons."

Actual instructions (excerpts)

  • On Android, don't copy the iOS code — rebuild it the platform's own way.Original안드로이드는 iOS 코드를 복사하지 말고 그 플랫폼 관례대로 다시 만들어.
  • Don't tell me it's done from memory — show me by checking the actual code.Original됐다는 걸 기억 말고 실제 코드로 확인해서 보여줘.

04

Where It Got Stuck & How I Resolved It

  • Supabase date-format mismatch: Inconsistent server date formats broke iOS decoding. I directed AI to build a custom decoder accepting multiple formats.
  • Android DI tool incompatibility: Hilt clashed with the latest build setup, so I directed a switch to manual singletons to stabilize the build.
  • Location-match reliability: Comparing locations on-device varied by phone and drained battery. I changed the design to gather pings on the server and compute match candidates with a SQL function.

Fig. momento-07

Moving location matching from devices to the server

device ↔ device comparisoncomputed on the server
iOSA's location ping
AndroidB's location ping
DBPings stored on the server
SQLMatching functionSame place & time candidates
ResultMemory createdOn both timelines
Comparing locations app-to-app gave inconsistent results and drained battery, so pings now go to the server and a SQL function computes match candidates.

Case 01

Dates from the server failed to decode on iOS

  1. Symptom

    Dates from the server failed to decode on iOS, breaking screens.

  2. How I described it

    the server's date format is inconsistent, so parsing fails," as fact.Original서버 날짜 형식이 일정치 않아 파싱이 실패한다"고 사실로 전달.
  3. AI’s first try

    A fix assuming a single format failed on other formats.

  4. What I caught

    A flexible decoder that accepts multiple formats was needed.

  5. Fix

    Directed a custom decoder (accepting multiple formats).

  6. Prevention

    I treat externally-sourced values as "format may vary" and parse defensively.

Case 02

The Android build failed from a clash between…

  1. Symptom

    The Android build failed from a clash between the DI tool (Hilt) and the latest build setup.

  2. How I described it

    Shared the symptom with the build log.Original빌드 로그와 함께 증상 전달.
  3. AI’s first try

    Changing versions alone didn't resolve it.

  4. What I caught

    Working around it with manual singletons instead of Hilt was the right call.

  5. Fix

    Directed a switch to manual singletons, stabilizing the build.

  6. Prevention

    I don't force incompatible tools — I weigh alternatives first.

05

Results

  • Built the same app natively on both iOS (SwiftUI) and Android (Compose)
  • iOS: 136 Swift files, ~17,911 lines / 24 feature modules; Android: 84 Kotlin files, ~11,454 lines
  • iOS uploaded to TestFlight (including App Store payout bank account and tax forms); Android in progress
Momento — momento 06

06

Retrospective

Instructing the same features two ways taught me it's not copy-paste — each platform has its own natural design, and I have to separate that out in how I direct AI. Next time I'd lock down the backend and shared rules first, then attach both clients.

(01) Review

Honest Review

If a developer had built it

Strong3
  • Feature scope — 24 iOS feature modules, on TestFlight

    Pairing, location matching, timeline, map, diary, anniversaries, in-app purchase, ads, widget, account recovery; TestFlight build 12.

  • Speed & cost — ~29K lines across two native apps in ~7 weeks

    iOS (SwiftUI) and Android (Compose) built separately on one backend with 14 database migrations.

  • Localization — 4 languages, same ~220 keys on both platforms

    English, Korean, Japanese, Thai, kept identical across iOS and Android.

On par4
  • Architecture — feature-based, platform-standard patterns

    Feature folders, @Observable + 13 view models, most screens 300–500 lines; shared backend helpers.

  • Code hygiene — clean

    Essentially no force-unwraps or crash-prone casts; no file over 1,000 lines.

  • Performance — battery-aware location

    100m movement filter on iOS, 15-minute background work on Android, spatial indexes in the database.

  • Security basics — access rules on every table

    Row-level access rules on all 22 tables (47 policies); keys kept server-side.

Next step (in progress)5
  • Testing & automation — only the matching engine has tests

    8 backend tests for matching; none for the apps, no CI.

    Plan → Tests for pairing, matching, and purchase flows, plus CI for both apps.

  • Release & ops — live database drifted from the repository

    The backend repo stopped updating in early May while the live database kept changing.

    Plan → Dump the live schema into migration files, commit them, and stop dashboard-only changes.

  • Security — hardening before store launch

    An extra server-side check for purchases comes before the store release; errors will be surfaced instead of swallowed.

    Plan → Server-side purchase verification and proper error reporting.

  • Android completeness — Android trails iOS

    I finished iOS first; Android is in development.

    Plan → Bring Android to parity using the iOS feature list — not copied code.

  • Accessibility — labels on key screens only

    Screen-reader labels in 23 places on iOS, about 10 on Android.

    Plan → Labels and font scaling become release-checklist items.

Next project →

AttenNFC

A planner's design, built with AI — NFC + GPS attendance with automatic payroll for small Thai businesses

01 NFC dynamic token
02 Wi-Fi signal (secondary)
03 Device binding
04 Root / jailbreak detection
Clock-in record
View case
Resume PDF