ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Tuist - Custom Flags 다루기
    Tuist 2023. 4. 18. 16:50

    안녕하세요. 그린입니다🍏

    이번 포스팅에서는 Tuist를 설정해주면서 삽질한것중 하나를 공유해볼까해요🙋🏻

     

    우선 주제는 Custom Flags입니다.
    다들, Debug / Relase라는 기본 swift compiler flag가 있다는것을 아신다는 가정하에 주절주절 해볼께요!

     

    Custom Flag 삽질하기

    Tuist를 통해 저는 하나의 프로젝트에 Prod, Dev, QA 타겟을 두고 이 타겟들을 각각의 스킴으로 만들어서 관리하는 도중 실제 사용 코드에서 전처리가 필요한 경우가 발생했습니다.

     

    예를들어, Dev, QA, Prod의 API host url이 다르거나 프리뷰를 띄워주거나 하는 것들에서 실제로 해당 플래그를 사용해 아래와 같이 전처리를 해주곤 하죠.

    #if DEBUG
      print("디버깅~")
    #elseif QA
      print("큐에이~")
    #else
      print("상용~")
    #endif

    자 여기까지는 다들 한번쯤 경험해봤을것 같아요.

     

    Tuist를 통해 실제 타겟들을 만들고 configuration을 구현해주는 구조는 아래와 같이 가져갈 수 있어요.

    import ProjectDescription
    import ProjectDescriptionHelpers
    
    let project = Project.make(
      name: "앱이름",
      targets: [
        .make(
          name: "Prod-App",
          product: .app,
          bundleId: "com.green.app",
          infoPlist: .file(path: .relativeToRoot("블라블라/Info.plist")),
          sources: ["Sources/**"],
          resources: ["Resources/**"],
          dependencies: [
          ],
          settings: .settings(
            base: [
              "ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon",
            ],
            configurations: [
              .release(name: .release, xcconfig: "./xcconfigs/greenApp.release.xcconfig"),
            ]
          ),
          scripts: [
          ]
        ),
        .make(
          name: "Dev-App",
          product: .app,
          bundleId: "com.green.app-dev",
          infoPlist: .file(path: .relativeToRoot("블라블라/Info.plist")),
          sources: ["Sources/**"],
          resources: ["Resources/**"],
          dependencies: [
          ],
          settings: .settings(
            base: [
              "ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon-Dev",
            ],
            configurations: [
              .debug(name: .debug, xcconfig: "./xcconfigs/greenApp.debug.xcconfig"),
            ]
          ),
          scripts: [
          ]
        ),
        .make(
          name: "QA-App",
          product: .app,
          bundleId: "com.green.app-qa",
          infoPlist: .file(path: .relativeToRoot("블라블라/Info.plist")),
          sources: ["Sources/**"],
          resources: ["Resources/**"],
          dependencies: [
          ],
          settings: .settings(
            base: [
              "ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon-QA",
            ],
            configurations: [
              .release(name: .release, xcconfig: "./xcconfigs/greenApp.qa.xcconfig"),
            ]
          ),
          scripts: [
          ]
        ),
      ],
      additionalFiles: [
        "./xcconfigs/greenApp.shared.xcconfig"
      ]
    )

    샘플로 이런식으로 각 Prod, Dev, QA 총 3개의 타겟을 가지고 프로젝트를 만들어줍니다.

     

    참고로, 해당 make 메서드는 타겟 생성을 조금 더 편리하게 하려고 별도로 아래와 같이 Tuist > ProjectDescriptionHelpers에 익스텐션을 만들었어요!

    import Foundation
    import ProjectDescription
    
    extension Target {
      public static func make(
        name: String,
        product: Product,
        bundleId: String,
        infoPlist: InfoPlist? = .default,
        sources: SourceFilesList,
        resources: ResourceFileElements? = nil,
        entitlements: Path? = .none,
        dependencies: [TargetDependency] = [],
        settings: Settings? = nil,
        scripts: [TargetScript] = []
      ) -> Target {
        return Target(
          name: name,
          platform: .iOS,
          product: product,
          bundleId: bundleId,
          deploymentTarget: .iOS(targetVersion: "15.0", devices: [.iphone]),
          infoPlist: infoPlist,
          sources: sources,
          resources: resources,
          entitlements: entitlements == .none ? .none : entitlements,
          scripts: scripts,
          dependencies: dependencies,
          settings: settings
        )
      }
    }

     

    자 그럼 본론으로 돌아와서🕺🏻

    여기서 각 settings 설정을 보시면 configurations에 해당 타겟의 설정이 들어가요.

    즉 xcconfig 파일로부터 release, debug 설정이나 자체적인 커스텀 설정을 해줄 수 있죠.

    그런데, Dev나 Prod는 기본적으로 configurations에서 정의된 debug, release를 사용하여 구분지어줄 수 있다고 칩니다.

     

    그럼, QA는 어떻게 구분 지어줄 수 있을까요?

     

    아래와 같이 해당 설정을 직접 넣어줄 수도 있습니다.

    settings: .settings(
      base: [
    	"ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon-QA",
      ],
      configurations: [
        .debug(name: "QA", xcconfig: "./xcconfigs/greenApp.qa.xcconfig"),
      ]
    )

    그런데 이것 자체도 debug 환경을 가지게 됩니다.

    실제로 tuist generate를 해보면, 해당 앱 타겟 build settings 부분에 Custom Flag를 보면 QA가 추가된걸 볼 수 있어요.

     

    그럼 우선 어 여기까지 되네? 라는 생각으로, Tuist로 쭉쭉 진행하면서 워크스페이스를 아래와 같이 구현해보면서 스킴을 나눴다고 쳐볼께요.

    import ProjectDescription
    
    let workspace = Workspace(
      name: "GreenApp",
      projects: [
      	// 경로 지정
      ],
      schemes: [
        Scheme(
          name: "Prod-GreenApp",
          buildAction: .buildAction(targets: [.project(path: "blabla", target: "Prod-GreenApp")]),
          runAction: .runAction(configuration: .release),
          archiveAction: .archiveAction(configuration: .release),
          profileAction: .profileAction(configuration: .release),
          analyzeAction: .analyzeAction(configuration: .release)
        ),
        Scheme(
          name: "Dev-GreenApp",
          buildAction: .buildAction(targets: [.project(path: "blabla", target: "Dev-GreenApp")]),
          runAction: .runAction(configuration: .debug),
          archiveAction: .archiveAction(configuration: .debug),
          profileAction: .profileAction(configuration: .debug),
          analyzeAction: .analyzeAction(configuration: .debug)
        ),
        Scheme(
          name: "QA-GreenApp",
          buildAction: .buildAction(targets: [.project(path: "blabla", target: "QA-GreenApp")]),
          runAction: .runAction(configuration: "QA"),
          archiveAction: .archiveAction(configuration: "QA"),
          profileAction: .profileAction(configuration: "QA"),
          analyzeAction: .analyzeAction(configuration: "QA")
        ),
      ],
      generationOptions: .options(autogeneratedWorkspaceSchemes: .disabled)
    )

     

    여기까지도 문제 없어 보이죠?
    tuist generate를 해도 잘되구요!

     

    그런데 실제 QA 스킴을 통해 QA 설정으로 돌리게 되면 해당 실행 파일의 위치를 찾을 수 없다는 에러가 나타납니다.

    아래와 같은 경로에서 실제 QA 앱 실행 파일을 찾는 과정을 거쳐요.

    User > library > Developer > Xcode > DerivedData > 해당 프로덕트 > Product > 하위 폴더

    바로 여기 경로에 이 파일들에서 찾는 과정을 거치게 됩니다.

     

    그런데 설정값이 QA이기에 QA-iphonesimulator를 찾게 되는데 없으니 실행이 되지 않는다고 오류를 무섭게 뱉습니다.

     

    그래서 방법은 두가지가 있을것 같아요.

     

    첫번째, QA-iphonesimulator 폴더를 각 타겟 settings 해주는 부분에서 생성해준다.

    혹은 스크립트를 돌아서 경로를 찾아 생성해주거나 지정해주면 될것 같은데 이 부분에서 삽질하다 도저히 잘 안되서 남겨두었습니다..⭐️

     

    그래서 저는 두번째 방법을 택했어요.

    약간 우회하는 느낌이 있지만 어차피 각 Dev, Prod, QA 스킴으로 실제 구현이나 배포를 진행할것이고 해당 타겟에서 커스텀하게 QA 설정을 만들지 않고 debug나 release일때 flag값을 QA로 변경하는 방법을 택했어요.

    settings: .settings(
      base: [
    	"ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon-QA",
      ],
      configurations: [
       .debug(
         name: .debug,
         settings: [
           "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "QA",
         ],
         xcconfig: "./xcconfigs/greenApp.qa.xcconfig"
       ),
      .release(
        name: .release,
        settings: [
          "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "QA",
        ],
        xcconfig: "./xcconfigs/greenApp.qa.xcconfig"
       ),
      ]
    )

    타겟에서는 결국 플래그값을 QA로 치환해주는것입니다.

     

    이러고 위 워크스페이스를 QA로 그대로 만드는것으로 configuration을 QA로 가져갈 수 있어요.

    그럼 아래와 같이 치환되어 플래그가 생성됩니다.

    자 그럼 실제로 스킴은 3개 생겼고 해당 스킴을 선택하면 이렇게 configuration은 QA로 들어가고

    QA 전처리도 정상적으로 사용할 수 있어요.

     

    혹시 다른 더 좋은 방법이나 잘못된 부분이 있으면 아직 확실한 삽질이 아니기에 알려주시면 감사하겠습니다🙇🏻

     

    마무리

    삽질을 계속했더니 손가락이 아프네요...🥹

Designed by Tistory.