
iphone-use-app
iphone-use-app은 아이폰에서 AI 모델을 실행해 같은 아이폰의 앱을 조작하는 오픈소스 앱이다. 맥이나 추가 하드웨어, 탈옥 없이 사용할 수 있다고 프로젝트는 설명한다.
데모 영상과 실행 단계 화면, 홈 화면을 공개했다. 데모에서 에이전트는 “에펠탑까지 가는 Lyft를 선택하되 아직 예약하지 마”라는 지시를 받고 앱을 벗어나 Lyft를 연 뒤 목적지를 입력하고 차량을 선택한다. 이후 차량 선택 화면에서 멈춘다. 완료한 실행은 각 단계의 스크린샷과 함께 표시되며, 새 대화도 시작할 수 있다.
앱은 채팅 화면, Siri, ‘Run Agent’ 단축어 또는 iphoneuse://run?prompt=… 링크에서 실행할 수 있다. 에이전트는 화면을 캡처해 모델에 보내고, 모델이 탭·쓸어넘기기·입력·앱 열기 등의 도구 호출을 반환하는 과정을 작업이 끝날 때까지 반복한다. Live Activity에서 진행 상황을 확인하고 중지할 수 있으며, 실행 내역은 각각 채팅으로 저장된다.
요구 사항과 제한
| 항목 | 조건 |
|---|---|
| iPhone | iOS 26 이상, 개발자 모드 활성화 |
| Apple 계정 | 유료 팀 계정에서는 앱에 내장된 VPN을 사용하며 Network Extension 권한이 필요하다. 무료 Apple ID에서는 BUILTIN_VPN=NO로 빌드하고 LocalDevVPN을 설치해야 한다. 7일마다 다시 서명해야 한다. |
| 페어링 파일 | 아래 절차에 따라 맥에서 USB로 한 번 만든다. |
| 비전 모델 | OpenRouter 키 또는 이미지와 도구 호출을 지원하는 OpenAI 호환 엔드포인트가 필요하다. |
| 맥과 Xcode, rustup | 앱을 빌드하고 페어링 파일을 만들 때만 필요하다. 실행 중에는 필요하지 않다. |
셀룰러 데이터를 사용할 때는 단축어가 필요하다. iOS가 셀룰러 연결에서 새 루프백 VPN 연결을 허용하지 않으므로, 앱은 연결 중 셀룰러 데이터를 끄고 연결 후 다시 켜는 ‘Cellular Toggle’ 단축어를 실행한다. 또는 셀룰러 데이터를 직접 끌 수 있다.
앱은 루프백 VPN을 사용하므로 다른 VPN과 동시에 쓸 수 없다. 내장 VPN 또는 LocalDevVPN이 필요하며, iOS는 한 번에 하나의 VPN만 허용한다. 따라서 VPN이 켜져 있는 동안 Tailscale 등은 작동을 멈춘다.
개발자 서비스를 사용할 때마다 iOS는 “Device Hub: This device was viewed remotely from Mac” 알림을 표시한다. 여기서 ‘Mac’은 앱 자체를 가리킨다. LLM에 보내는 호출을 제외하면 데이터는 아이폰 밖으로 나가지 않는다고 프로젝트는 설명한다.
앱은 개발자 모드, VPN 구성, 페어링 서비스를 찾기 위한 로컬 네트워크 접근 권한, 백그라운드에서 실행을 유지하기 위한 ‘항상’ 위치 접근 권한이 필요하다. 백그라운드 실행에는 무음 오디오도 함께 사용한다. 마이크 권한은 음성 프롬프트에만 필요하다. 페어링 파일은 만료되므로 앱에서 만료 또는 무효라고 알리면 새 파일을 만들어야 한다.
이 앱은 개발자 모드와 개발자 서비스가 필요해 사이드로드 방식으로만 설치할 수 있으며, App Store에 올릴 수 없다. 각 단계에는 몇 초가 걸리며, 대부분은 모델 처리 시간이라고 프로젝트는 설명한다.
설치와 사용
먼저 아이폰을 맥에 USB로 연결하고 잠금을 해제한다. 신뢰 여부를 묻는 메시지가 표시되면 ‘신뢰’를 누른다. 아이폰은 신뢰된 호스트에만 개발자 서비스를 제공한다. 페어링 파일은 해당 호스트의 키를 담으며, 앱이 자기 아이폰에 대해 호스트 역할을 할 수 있게 한다.
git clone https://github.com/jkcoxson/idevice && cd idevice
cargo build --release -p idevice-tools --bin idevice-tools
./target/release/idevice-tools rppairing pair my-mac pairingFile.plist
앱에서 페어링 파일이 무효이거나 만료됐다고 알리면 새 파일을 만든다.
앱을 빌드하고 설치하려면 다음을 실행한다.
scripts/build-idevice.sh # once: builds iPhoneUse/idevice/libidevice_ffi.a (needs the aarch64-apple-ios target)
open iPhoneUse.xcodeproj # set your team, run the iPhoneUse scheme on the phone
Xcode에서 팀을 설정한 뒤 아이폰에서 iPhoneUse 스킴을 실행한다. 앱에서 한 번 설정할 때는 오른쪽 위 톱니바퀴 모양의 설정에서 ‘Import Pairing File’을 선택한다. VPN 구성 추가 요청을 허용한다. 이 구성은 10.7.0.1로 향하는 연결만 아이폰으로 되돌린다. 대신 LocalDevVPN을 쓰려면 앱을 켠 뒤 설정의 ‘Phone Connection’에서 LocalDevVPN을 선택한다. 내장 VPN이 작동하지 않을 경우 앱이 이를 선택해 줄 수 있다.
설정의 ‘Model’에서 제공자를 선택하고 키를 입력한 뒤 모델을 고른다. 첫 실행에서는 개발자 디스크 이미지가 다운로드된다. 입력창 위에 표시되는 ‘not connected’ 문구가 사라질 때까지 기다린다.
앱 설치 후 페어링 파일을 다음 명령으로 복사할 수도 있다.
xcrun devicectl device copy to --device <udid> --domain-type appDataContainer --domain-identifier blog.eliot.iphoneuse --source pairingFile.plist --destination Documents/pairingFile.plist
이후 “설정을 열고 다크 모드를 켜”와 같은 목표를 입력해 전송한다. Dynamic Island나 채팅 화면에서 실행을 중지할 수 있다.
개발 관련 안내
LLM을 사용하지 않는 스크립트 실행은 다음 명령으로 할 수 있다.
DEV=<udid> scripts/runscript.sh "open com.apple.Preferences; wait 1.5; tap 0.5 0.9; shot a" a
이 명령은 -agentScript 인수로 앱을 실행하고, Documents/agent.log에 “script done”이 나타날 때까지 기다린 뒤 스크린샷을 가져온다. 실행 인수로 -agentPrompt "..."를 지정하면 실제 에이전트 실행을 시작한다.
시뮬레이터는 레이아웃 확인에만 사용할 수 있다. 실제 기기에 연결할 수는 없지만 UI를 표시한다. Debug 빌드에서만 쓸 수 있는 -demoChat YES는 자리 표시자 스크린샷과 답변할 질문을 보여주는 가짜 실행을 재생한다.
rustup target add aarch64-apple-ios-sim && scripts/build-idevice.sh sim
라이브러리는 단일 아키텍처로 빌드되므로 시뮬레이터용 라이브러리를 별도로 iPhoneUse/idevice/sim/에 만들어야 한다. 기본 스킴은 -demoChat을 제외하는 Release이므로, 해당 라이브러리를 사용해 Debug 빌드를 만든다.
xcodebuild -project iPhoneUse.xcodeproj -scheme iPhoneUse -configuration Debug -destination id=<sim udid> LIBRARY_SEARCH_PATHS='$(PROJECT_DIR)/iPhoneUse/idevice/sim' build
DerivedData에서 .app을 설치한 뒤 -demoChat YES 인수로 실행한다.
하트비트 연결 문제와 수정 사항
수정 전에는 백그라운드에서 약 1분이 지난 뒤 실행이 특정 단계에서 멈추고 Live Activity도 그 단계에 머물렀다. 앱이 종료된 것이 원인이었다. 아이폰의 충돌 로그에는 iPhoneUse.cpu_resource_fatal-*.ips 보고서가 있었으며, “80% cpu over 60 seconds 초과”, “Process killed”라고 기록됐다.
원인은 DeviceSession.swift의 Heartbeat.run() 하트비트 스레드가 실행 중 CoreDevice 서비스를 유지하는 방식에 있었다. 핸드셰이크 직후 연결이 닫히면 idevice의 사용자 공간 TCP 스택인 jktcp가 모든 읽기에 마이크로초 단위로 BrokenPipe를 반환했다. 반복문은 지연 없이 재시도해 초당 약 4만 5,000회 실행됐다. iOS는 60초 동안 CPU 사용률이 80%를 넘는 백그라운드 앱을 종료한다.
문제를 악화시킨 요소도 두 가지 있었다. Debug 수준으로 기록하던 idevice 로그는 모든 XPC 메시지와 스크린샷까지 10진수 바이트 목록으로 출력해 1분 동안 로그 178MB와 메모리 약 1GB를 만들었다. 또한 반복문은 HeartbeatTimeout과 HeartbeatSleepyTime이라는 문자열로 오류를 판별했지만, idevice 메시지에는 이 문자열이 없었다. 실제 메시지는 “heartbeat timeout”과 “device went to sleep”이었다.
수정 사항은 오류 코드(100, 하위 코드 2)로 시간 초과를 판별해 반복문이 다시 대기하도록 하고, 다른 오류가 발생하면 반복문을 끝내는 것이다. 파일 로그 수준도 Info로 낮췄다.
하트비트 연결이 닫히는 이유는 아직 해결되지 않았다. 하트비트 없이도 실행은 가능했다. 17단계로 2.5분 동안 진행한 실행은 정상적으로 끝났다. 장시간 실행에서 CoreDevice 서비스 연결이 끊기기 시작하면, 다음 단계는 백오프를 적용해 하트비트를 다시 연결하는 것이라고 프로젝트는 설명한다.
종료된 실행의 충돌 로그는 다음 명령으로 가져온다.
idevicecrashreport -u <udid> -k <dir>
정확히 같은 빌드의 dSYM을 사용해 atos로 심볼리케이션해야 한다. 보고서의 UUID가 dwarfdump --uuid 결과와 일치해야 한다. 기능 범위는 docs/FEATURES.md에 정리돼 있다.
책임 있는 사용과 라이선스
소유했거나 사용할 권한이 있는 기기와 계정만 자동화하고, 관련 앱의 이용 약관을 지켜야 한다. 에이전트는 사용자를 대신해 조작하므로 실행을 지켜보고 중지 기능을 가까이 두라고 프로젝트는 안내한다.
프로젝트는 AGPL-3.0 라이선스를 따른다. 자세한 내용은 LICENSE를 참조하면 된다.
크레딧
| 프로젝트 | 저장소 | 사용한 부분 |
|---|---|---|
| StikDebug, Stephen과 기여자들 | StikDebug/StikDebug | 터널, 페어링 파일, 개발자 디스크 이미지 코드의 기반 |
| idevice, jkcoxson | jkcoxson/idevice | 기기 호출에 쓰이는 Rust 라이브러리 |
| StosVPN, SideStore Team과 Stossy11 | SideStore/StosVPN | 루프백 VPN(TunnelProvider/)의 기반 |
| LocalDevVPN, jkcoxson | jkcoxson/LocalDevVPN | 무료 Apple ID용 VPN 앱. 터널은 StosVPN에서 이를 거쳐 가져왔다. |
| esp32-iphone-agent, Henry Yuan | onehumanbeing/esp32-iphone-agent | 모델 제공자·모델 선택기 설계와 README 레이아웃 |
| DeveloperDiskImage, doronz88 | doronz88/DeveloperDiskImage | 개발자 디스크 이미지 |
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요