도입
QA 시나리오 캡처 자동화를 짜다가 막혔다.
서버 응답에 따라 분기되는 화면 3종을 캡처하고 싶어서, 디버그 빌드에 환경변수
FORCE_LABS_ENABLED 로 서버 응답을 덮어쓰는 분기를 넣어뒀다. 그리고 시뮬레이터를
이렇게 띄웠다.
| |
앱은 정상 실행됐다. 그런데 분기가 안 탔다.
ProcessInfo.processInfo.environment["FORCE_LABS_ENABLED"] 가 항상 nil 이었다.
분명히 Xcode Scheme 의 Arguments 탭에서 Environment Variables 에 같은 값을 적어줬을 때는 잘 동작했다. simctl 로 띄울 때만 안 먹었다.
원인 — argv였다
xcrun simctl launch 의 시그니처를 다시 보자.
simctl launch <device> <bundle id> [<launch arg>...]
bundle id 뒤에 오는 토큰은 전부 launch arguments, 즉 argv 다.
ProcessInfo.processInfo.arguments 에 들어가지, environment 에는 들어가지 않는다.
내가 적어준 FORCE_LABS_ENABLED=0 은 모양만 환경변수처럼 생긴 스트링 하나가
argv[1] 로 전달됐을 뿐이었다. 앱에서 환경변수로 읽을 길이 없다.
흐름 비교
부모 셸에서 시뮬레이터 안 앱 프로세스까지, 두 경우에 환경변수가 어떻게 흐르는지 한눈에 비교하면 이렇다.
flowchart LR
subgraph Bad["KEY=VAL을 그대로 넘긴 경우 (실패)"]
A1["부모 셸
simctl launch <bundle> FORCE_LABS_ENABLED=0"]
A2["simctl
토큰을 argv로 해석"]
A3["앱 프로세스
ProcessInfo.arguments = [..., 'FORCE_LABS_ENABLED=0']
ProcessInfo.environment['FORCE_LABS_ENABLED'] = nil"]
A1 --> A2 --> A3
end
subgraph Good["SIMCTL_CHILD_ prefix를 쓴 경우 (성공)"]
B1["부모 셸
export SIMCTL_CHILD_FORCE_LABS_ENABLED=0
simctl launch <bundle>"]
B2["simctl
SIMCTL_CHILD_ 접두 변수만
prefix 떼고 자식에게 상속"]
B3["앱 프로세스
ProcessInfo.environment['FORCE_LABS_ENABLED'] = '0'"]
B1 --> B2 --> B3
end
해결 — SIMCTL_CHILD_ prefix
xcrun simctl 은 부모 셸의 환경변수를 자식 프로세스(시뮬레이터 안의 앱)에
그대로 상속하지 않는다. 대신 약속된 prefix 가 있다.
부모 셸에서 SIMCTL_CHILD_<VARNAME>=value 로 export 해두면, simctl 이
prefix 를 떼고 자식 프로세스의 환경변수로 넘겨준다.
| |
위 예시처럼 변수 여러 개를 동시에 주입할 수도 있다. 앞에 prefix 만 똑같이 붙여 나열하면 끝.
이렇게 띄우면 앱 안에서:
| |
정상 수신된다.
왜 그렇게 설계됐는가
시뮬레이터 안의 앱 프로세스는 simctl 의 자식이지만, 셸 환경 전체를 그대로
넘겨주면 호스트 macOS 환경(PATH, HOME, TMPDIR 등)이 통째로 시뮬 안으로
들어가 동작이 꼬일 수 있다. 그래서 화이트리스트 방식으로, 명시적으로 prefix 를
단 변수만 넘기게 해둔 것이다.
같은 패턴이 xcrun simctl spawn 에도 있고, Xcode 의 Scheme Environment
Variables 도 내부적으로는 비슷한 방식으로 자식 프로세스에 주입한다.
교훈 / 점검 순서
xcrun simctl launch <bundle> KEY=VAL은 환경변수가 아니라 argv 다.- 환경변수를 주입하려면 부모 셸에서
SIMCTL_CHILD_<NAME>prefix 로 export. - CI 스크립트, QA 자동화, 시나리오 캡처 다 동일하게 적용된다.
안 먹을 때 점검 순서는 이렇게 잡아두면 편하다.
- Xcode Scheme 에서 같은 환경변수를 적어두고 직접 실행 → 코드 분기 자체는 정상인지 확인
- simctl 명령어 앞에
SIMCTL_CHILD_prefix 가 제대로 붙어있는지 확인 - 자식 프로세스의 실제 환경을 직접 확인:
| |
이 순서대로 한 번씩 짚어보면 십중팔구 2번에서 잡힌다. 한 번 알고 나면 다시는 안 막히는 종류의 함정이라, 메모 하나 남겨두고 다음에 똑같이 헤매지 않는 게 최고다.