[Swift] Swift Package Manager로 Debugging 라이브러리 추가한 뒤 특정 Configruation 에서 제외하기

서론

기존 회사에서는 Swift Package Manager가 나오기 전에 CocoaPods를 사용하여 라이브러리들을 관리해왔습니다.

앱 개발을 하다보면 Xcode에서 디버깅하는 것 뿐만이 아닌 실제 디바이스에 올라간 앱에서도 디버깅이 필요할 때가 있어 괜찮은 오픈 소스 디버깅 라이브러리(CocoaDebug, FLEX 등등..)들을 도입하는 경우가 있습니다.

하지만 디버깅 라이브러리들은 앱에 포함된 채로 앱 스토어에 업로드 되면 안되는데요, 기존 CocoaPods의 경우 아래와 같은 구문으로 원하는 Configuration에만 설치할 수 있었지만 Swift Package Manager의 경우는 사용할 수 없습니다.

pod 'Library', :configurations => ['Debug']

하지만 최근 Swift Package Manager로 사용할 수 있는 것들은 최대한 사용하고 싶어, 시도해본 기록을 남겨보고자 합니다.

라이브러리 설치

일단 제가 설치할 라이브러리는 FLEX 라이브러리입니다. 디버깅에 도움이 되는 많은 기능들을 보유하고 있고, 최근 업데이트가 활발하다는 점, Stars 수도 상당히 많다는 점에서 선택하게 되었습니다.

일단 프로젝트를 생성하고 아래와 같이 SPM으로 설치해봅니다.

게시글을 작성한 시점 기준으로 repo url은 https://github.com/FLEXTool/FLEX.git 입니다.

앱 타겟에 추가해줍시다.

이제 앱에서 갖출 여러가지 환경을 구성해봅시다. 상황마다 다르겠지만 저는 ‘Debug’, ‘Staging’, ‘Release’로 나눠서 작업해보겠습니다.

일단 프로젝트를 선택하고 Info탭에서 Configurations 영역의 ‘+’ 버튼을 눌러 “Debug” 설정을 복제해주세요.

복제한 뒤 엔터 버튼을 눌러 Staging로 네이밍을 변경해주세요.

네이밍을 변경하였다면 그 다음은 이제 새 Scheme을 만들어 주어야 합니다.

해당 화면을 누른 뒤, ‘New Scheme’ 를 눌러 아래와 같이 Staging와 Release를 생성해주세요.

이제 해당 Staging Scheme의 수정 화면으로 들어와서 아래와 같이 Build Configuration을 올바르게 Staging으로 변경해주세요.

위와 같이 설정을 마쳤다면 아래 화면과 같이 여러가지 환경을 실행할 수 있습니다.

이제 각 Configuration 별로 상황에 맞는 코드를 분리하기 위해 추가적으로 해야하는 설정이 있습니다.

먼저 Target 앱을 선택하고 Build Settings 화면에서 아래와 같이 Active Compliation Conditions을 수정해야합니다.

위와 같이 설정이 되었다면, 제대로 작동하는지 확인해봅시다. 아래의 코드를 AppDelegate에 넣고 Staging 으로 실행해봅시다.

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    #if DEBUG
    print("Debug Mode")
    #elseif STAGING
    print("Stage Mode")
    #elseif RELEASE
    print("Release Mode")
    #else
    print("Other Mode")
    #endif

    return true
}

Xcode Console창에 Stage Mode가 출력된다면 성공입니다. Other Mode나 Debug Mode가 출력된다면 문제가 있는 것이니 다시 확인해보아야 합니다.

이제 SceneDelegate에서 아래의 코드를 추가하여 FLEX 라이브러리가 잘 작동하는지 확인해봅시다. 해당 코드는 Debug와 Staging 모드로 빌드된 앱에서만 실행할 수 있는 코드입니다. (Release 모드에서는 해당 코드가 아예 들어가지 않으므로 앱 스토어에 올라간 파일에선 확인할 수 없습니다)

#if DEBUG || STAGING
import FLEX
#endif

func scene(_ scene: UIScene,
           willConnectTo session: UISceneSession,
           options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
    guard let windowScene = (scene as? UIWindowScene) else { return }

    #if DEBUG || STAGING
    setUpFlexExplorer(windowScene: windowScene)
    #endif
}

// MARK: - DEV
#if DEBUG || STAGING
extension SceneDelegate {
    private func setUpFlexExplorer(windowScene: UIWindowScene) {
        FLEXManager.shared.showExplorer(from: windowScene)
    }
}
#endif

Debug와 Staging에서 실행됐을때 아래의 화면이 보여져야하고, Release 모드에서 실행했을 때는 보여지면 안됩니다.

이제 앱에서 Flex가 ‘Debug’, ‘Staging’ 에서만 실행될 수 있게되었습니다. 하지만 Release인 경우에 앱을 빌드하고 앱 스토어에 업로드했을 때 실행만 되지 않을 뿐. 실제 앱에는 Flex 라이브러리 코드가 포함되어 나가게 되는데요, 이 경우를 방지하기 위해 빌드 옵션을 살짝 수정해봅시다.

위와 같이 Excluded Source File Names 에서 Release 일 땐 FLEX 파일들이 전부 제외될 수 있게 설정해주면 됩니다. (FLEX* 로 해도 되지만 FLEX.o 로도 가능합니다)

위와 같이 적용했다면 이제 잘 제외되었는지 확인해봅시다. 아래의 명령어로 확인해볼 수 있습니다.

$ strings app실행파일 경로 | grep FLEX

위의 명령어를 입력하고 아래와 같은 화면이 나온다면 FLEX 모듈이 포함되었다는 의미입니다. (필자는 Staging에서 실행해서 아래와 같은 결과가 나왔습니다)

아래와 같이 아무것도 출력되지 않는다면 제대로 제외되었다는 의미입니다.

이제 특정 Configuration에서는 특정 라이브러리가 포함되지 않도록 구성할 수 있습니다.