본문 바로가기

iOS+

#Preview를 위한 환경 구분 방법

개요

TodoMate라는 프로젝트에선 FirebaseSDK의 Firestore를 사용하는데, 프로젝트 타겟에 해당 라이브러리를 추가하면 import를 안 해도 프리뷰 오류가 발생한다

지금까지는 그냥 코드 작성하고 빌드하고 직접 실행해가면서 확인했는데, 이젠 너무 불편해서 이를 나름 해결하는 과정을 글로 작성해 본다

추가로 런타임 시점에 설정된 값을 바탕으로 코드 분기를 수행시키는 방법에 대해서도 기억해 두자.

 

문제 원인

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	       0x19eeb6b4c __abort_with_payload + 8
1   libsystem_kernel.dylib        	       0x19eedce84 abort_with_payload_wrapper_internal + 104
2   libsystem_kernel.dylib        	       0x19eedce1c abort_with_reason + 32
3   libobjc.A.dylib               	       0x19eb50040 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 128
4   libobjc.A.dylib               	       0x19eb4ffc0 _objc_fatal(char const*, ...) + 44
5   libobjc.A.dylib               	       0x19eb1a1ac lookUpImpOrForward + 836
6   libobjc.A.dylib               	       0x19eb19b84 _objc_msgSend_uncached + 68
7   ???                           	       0x380034770 ???
8   ???                           	       0x380191b0c ???
9   ???                           	       0x380191a2c ???
10  libswiftCore.dylib            	       0x1afef6d04 swift::MetadataCacheEntryBase<(anonymous namespace)::SingletonMetadataCacheEntry, int>::doInitialization(swift::MetadataWaitQueue::Worker&, swift::MetadataRequest) + 848
11  libswiftCore.dylib            	       0x1afedcd80 swift_getSingletonMetadata + 1892
12  ???                           	       0x38019031c ???
13  ???                           	       0x380191378 ???
14  ???                           	       0x38019143c ???
15  TodoMate                      	       0x100a9a230 __debug_blank_executor_run_user_entry_point + 144
16  PreviewsInjection             	       0x25d89b7b0 0x25d864000 + 227248
17  PreviewsInjection             	       0x25d89c4c8 0x25d864000 + 230600
18  PreviewsInjection             	       0x25d89c38c __previews_injection_run_user_entrypoint + 16
19  XOJITExecutor                 	       0x271d42adc __xojit_executor_run_program_wrapper + 1832
20  XOJITExecutor                 	       0x271d3e7cc 0x271d3b000 + 14284
21  PreviewsInjection             	       0x25d89c2c0 0x25d864000 + 230080
22  TodoMate                      	       0x100a99970 __debug_blank_executor_main + 1056
23  dyld                          	       0x19eb68274 start + 2840

 

사실 위 글을 작성하게 되었을 때 부터 해당 문제가 발생했는데, 여기서 깨달은 점은 다음과 같다

 

FirebaseCore와 달리 FirebaseFirestore는 타겟에서 라이브러리 추가하기만 하면 바로 프리뷰 오류가 발생한다

프리뷰 동적 링킹과정이 올바르게 이뤄지지 않아서 발생하는 문제인데, 이는 프리뷰에서만 문제가 되는 것이지 실제 빌드 후 실행에선 문제되지 않았다

 

해결 방안

가장 간단한 해결방법으로 프로젝트를 하나 더 만들어서 똑같이 코드를 작성하고 라이브러리를 추가하지 않고 프리뷰를 이용하면 된다

그렇지만 이렇게하면, 매번 새로 코드를 업데이트하면 복붙해야 하는 게 너무 귀찮아보여서 이 방법은 포기했다

 

여러 방법들을 찾아보다가, 프리뷰를 사용할 때는 문제가 되는 라이브러리를 제거하고 실제 빌드 시 해당 라이브러리를 추가하는 방법을 생각하게 되었다

 

컴파일 플래그(Compile flag)를 통해 해당 라이브러리에 의존하는 부분들을 관리하고, 프리뷰 용 가짜 객체와 실제 코드를 위한 프로토콜을 정의하여 코드들을 작성하면 된다

 

또한, 이를 프리뷰 환경과 구분하기 위해 스킴(Scheme)에서 프리뷰를 위한 컴파일 플래그를 이용하는 빌드 구성(Build Configuration)을 설정하여 처리한다

Scheme

Xcode에서 Scheme란, 특정 빌드 구성(Build Configuration)과 실행 환경을 하나의 ‘세트’로 묶어놓은 것이다(빌드+실행+테스트 전 과정을 한 번에 정의해놓은 설정 세트)

Scheme의 역할

빌드 대상(Target) 선택

앱, 테스트(Target), 확장(Extension) 등 프로젝트 내 여러 Target 중 어떤 것을 빌드 및 실행

 

Configuration 선택

Scheme의 Build Configuration으로 Debug, Release 그리고 따로 생성한 Preview가 존재한다

Scheme을 누르면 다음과 같이 Configuration을 설정할 수 있다

 

실행 동작 제어

Run, Test, Profile, Analyze, Archive 등 Xcode 툴바에서 버튼을 누르면 수행되는 동작들이 어떤 Build Configuration을 기준으로 동작할지 설정

 

환경 변수, 인수 설정

Scheme 레벨에서 런타임에 필요한 환경 변수(Environment Variables)나 실행 인수(Arguments)를 설정

Scheme의 구성 요소

Build

어떤 Target을 빌드하고, 필요 시 빌드 순서는 어떻게 할지 지정

 

Run

실행 시 어떤 Configuration을 사용할지, 런타임 Arguments/Environment Variables를 어떻게 설정할지 지정

 

Test

어떤 Test Target을 포함할지, 실행 순서, 병렬 실행 옵션, Code Coverage 생성 여부 등을 지정

 

Profile

Instruments로 프로파일링할 때 어떤 Configuration을 사용할지 지정

 

Analyze

정적 분석(Analyze) 시 어떤 Configuration을 사용할지 지정

 

Archive

Archive 시 어떤 Configuration을 사용할지, 빌드 번호 등에 대한 자동 증분 설정 등을 지정

 

Compile flag 적용 예시

Other Swift Flags를 통해 플래그 값을 설정하면, 앱 내부에서 #를 이용하여 빌드 시점에 코드 분기를 수행시킬 수 있다

 

우선 프로젝트의 Info에서 Configurations에 Preview를 추가한다

 

  1. 프로젝트 설정 화면에서, 프로젝트 혹은 타겟을 선택
  2. Build Settings 탭에서 Swift Compiler - Custom Flags 섹션을 검색
  3. Other Swift Flags 항목을 찾아서 알맞게 Configuration별로 값을 설정
  4. 설정하려는 Flag를 -D를 붙여서 추가

-DPREVIEW를 추가하고, 실제 코드에선 #if PREVIEW 매크로를 통해 코드를 작성하면 된다

코드 분기 - 빌드 시점과 런타임 시점

컴파일 플래그를 이용하면 실제 코드를 빌드 시점에 분기시킬 수 있는데, 또한 Scheme의 환경 변수를 통해서도 런타임 시점에 코드 분기를 수행시킬 수도 있다

 

빌드 시점 분기

빌드 시점 분기는 #써서 매크로 코드를 작성하면 된다

아래의 코드는 PREVIEW라는 컴파일 플래그를 통해 빌드 시점에 코드를 분기하는 예시 코드다

 

#if PREVIEW
	print("문제가 되는 라이브러리를 추가 안함")
#else
	import FirebaseFirestore
	print("문제 라이브러리 추가된 로직")
#endif

 

런타임 시점 분기

런타임 시점 분기는 ProcessInfo.processInfo.environment["ENV_FLAG"]를 통해 if 문으로 코드를 작성하면 된다

 

이는 Scheme 레벨에서 Arguments Passed On Launch 또는 Environment Variables을 통해 설정한다

아래의 코드는 QA라는 환경 변수(Environment Variables)를 설정했을 때, 이에 따라 런타임에 코드를 분기시키는 예시 코드다

 

if ProcessInfo.processInfo.environment["ENV_FLAG"] == "QA" {
    // QA 전용 로직
} else {
    // 기본 로직
}

 

마무리

지금은 해당 방법을 생각하고 기존코드들을 다시 작성하고 있다

이 또한 상당히 귀찮은 작업이지만, 그전에 프리뷰를 사용하지 못했던 것을 생각하면 할만하다

문제가 되던 라이브러리를 프리뷰를 통한 개발 중엔 제거하고 실제 빌드 시에만 추가할 생각을 진작 못했던 게 아쉽다..

 

 

추가

Scheme이 PREVIEW로 적용되어 있지만, 에디터는 이를 감지 못하는 오류가 발생했다

 

실제로 작업하다 보니, XCode 에디터에서 올바르게 플래그가 적용 안되는 문제가 있었는데 다음과 같이 Build SettingActive Compilation Conditions도 설정해주면 된다

 

 

위와 같이 Debug에 PREVIEW를 추가하고 나면, 다음과 같이 문제가 해결된다

 

'iOS+' 카테고리의 다른 글

On-Device AI with CoreML(feat. DeepSeek-R1)  (0) 2025.02.09
인증서와 프로파일  (0) 2025.02.02
Xcode Cloud와 Sparkle framework  (2) 2024.09.29
Objective-C  (1) 2024.09.22
WidgetKit  (0) 2024.09.15