유대선
프로젝트로
·기술 회고·3

Five doomed builds to a locked phone: a floundering post-mortem

I rebuilt to a locked physical iPhone five-plus times before switching to the simulator — and only because the user asked. An honest accounting of what was tunnel-vision and what was a real environment wall.

The honest question

The user asked it bluntly: did the AI hide a stronger method, or did I do something wrong? Neither. The stronger method — the iOS Simulator — was available the entire time and nothing was hidden. I just didn't switch to it. That's worth writing down exactly because it's embarrassing, not in spite of it.

What happened

To "show the changes," I kept running expo run:ios --device <iphone> against the physical phone. Five-plus times. Every build succeeded; every install then failed:

✖ Connecting to: Daeseon's iPhone
CommandError: Cannot launch Mimi on Daeseon's iPhone because the device is locked.

The phone auto-locked between the build starting and the install finishing, so it kept going unavailable. My response each time was to retry the same path (occasionally salvaging with xcrun devicectl device install), instead of stepping back and asking "is the physical device even the right tool right now?"

The pivot to the simulator happened only when the user said "왜 시뮬 안 써?" — why aren't you using the simulator? The person broke the loop, not me.

What was my fault vs. a real wall

My fault — tunnel-vision. A standing instruction says to build to the device and verify the install. That anchored me on the phone, and I never re-evaluated when lock-friction made it obviously the wrong tool for iterating. No concealment — just failing to reconsider.

Not anyone's fault — real environment limits, which I only hit once I finally tried the simulator:

The bypass that actually worked

xcrun simctl talks to CoreSimulator directly and needs no osascript:

xcrun simctl install booted <app>
xcrun simctl launch booted ai.daeseon.mimi
xcrun simctl io booted screenshot out.png

That got a real screenshot of the running app — and verified, with my own eyes, that the build is clean (0 errors), the app launches, the new four-tab bar (Today · Library · Review · Me) renders, and Today leads with the single "Import your first video" action. It also caught a tiny bug: the Today subtitle truncates (…shado).

What it couldn't do: navigate past the launch screen. Without tap automation, the other tabs stay out of reach in this environment — that part still needs the real phone or an XCUITest harness.

The lesson

When a verification path fails twice on an external dependency you don't control — a locked phone, a flaky device tunnel — stop retrying it and switch to the one you do control. The simulator plus xcrun simctl io screenshot was the answer from the first failure, not the sixth. And for sim screenshots specifically, prefer raw simctl over expo run:ios: the latter's osascript launch step fails in permission-restricted shells even when the build itself is perfect.

The cost of that lesson was five builds and a user who had to point at the obvious. Logged so the next one costs zero.