본문 바로가기

iOS

[iOS] Fastlane + Github Actions로 CI/CD 파이프라인 구축하기 (5편 - Tuist와 Fastlane 함께 사용하기, xcconfig과 schemes, match 설정)

[iOS] Fastlane + Github Actions로 CI/CD 파이프라인 구축하기 (1편 - CI/CD란 무엇일까?)

[iOS] Fatslane + Github Actions로 CI/CD 파이프라인 구축하기 (2편 - Github Actions 살펴보기)

[iOS] Fatslane + Github Actions로 CI/CD 파이프라인 구축하기 (3편 - fastlane으로 TestFlight에 앱 업로드하기)

[iOS] Fastlane + Github Actions로 CI/CD 파이프라인 구축하기 (4편 - fastlane match로 인증서와 profile을 git 저장소에 관리하기)


Tuist 프로젝트에 match 적용하기

tuist로 프로젝트를 생성하면 build setting을 설정해주지 않는 이상 code sign을 수동으로 해줘야 합니다. fastlane match를 이용하는 경우에는 match의 profile을 수동으로 부여해야 할 것입니다.

 

tuist 프로젝트에 match를 통해 signing을 하기 위해서는 아래 세가지를 신경써야 합니다.

 

1. match를 통해 얻을 수 있는 profile의 specifier를 build setting을 통해 명시하기

2. profile이 필요한 target이 여러개인 경우 각각의 provisioning profile specifier에 대해 분기처리하기

3. xcconfig을 사용하고 있다면 xcconfig에서 해당 값을 가져오기

 

코드를 통해 살펴보겠습니다.

 

tuistSettingsDictionary를 통해 build setting을 설정할 수 있는데, 아래와 같이 미리 extension을 작성해 두고 target마다 필요한 setting을 적절하게 merge시킬 수 있습니다. 제 경우에는 widget에 대한 provisioning을 분기처리해야 했기에, widget의 profile specifier를 따로 설정해줬습니다. 필요하다면 $()를 통해 xcconfig에 존재하는 값을 사용할 수도 있습니다.

아래 사진을 통해 xcconfig에서 App 및 Widget의 profile specifier를 부여하고, identitiy를 정의하는 것을 확인할 수 있습니다.

dev config의 경우 Apple Development로 정의하고, release config의 경우 Apple Distribution으로 설정하고 있습니다.

아래는 각 target에서 필요한 settings를 사용하고 있는 모습입니다.

let settings: SettingsDictionary = ["OTHER_LDFLAGS" : "$(inherited)"]
    .merging(
        SettingsDictionary()
            .setCodeSignManual()
    )

let target = Target(
    name: name,
    platform: .iOS,
    product: .app,
    bundleId: "com.earthIsRound.release",
    deploymentTarget: deploymentTarget,
    infoPlist: .extendingDefault(with: Project.iosAppInfoPlist),
    sources: ["Sources/**/*.swift"],
    resources:  [.glob(pattern: "Resources/**", excluding: [])],
    dependencies: [
        internalDependencies,
        externalDependencies,
        [
            Dep.project(target: "EarthIsRoundWatchApp", path: .watchApp),
            Dep.target(name: "WidgetExtension")
        ]
    ].flatMap { $0 },
    settings: .settings(base: settings.setProvisioning(), configurations: XCConfig.project)
)

// MARK: - Widget Extension

let widgetTarget = Target(
    name: "WidgetExtension",
    platform: .iOS,
    product: .appExtension,
    bundleId: "com.earthIsRound.release.widget",
    infoPlist: .extendingDefault(with: [
        "CFBundleDisplayName": "$(PRODUCT_NAME)",
        "NSExtension": [
            "NSExtensionPointIdentifier": "com.apple.widgetkit-extension",
        ],
    ]),
    sources: "../Extensions/WidgetExtension/Sources/**/*.swift",
    resources: "../Extensions/WidgetExtension/Resources/**",
    dependencies: [
        .Core.iOS
    ],
    settings: .settings(base: settings.setProvisioningWidget(), configurations: XCConfig.project)
)

여기까지 처리한 상태에서 tuist generate를 하면, match에서 얻어진 profile이 자동으로 target에 부여되어 있는 것을 확인할 수 있습니다.

fastlane을 통해 Versioning하기

fastlane에는 이미 구현되어 있는 'avilable actions'들이 존재합니다. 많은 action들이 있지만, 쉽게 사용할 수 있는 것 중에는 'increment_version_number' 'increment_build_number'가 있습니다. 각각의 lane은 MARKETING_VERSION과 CURRENT_PROJECT_VERSION을 다음 값으로 증가시키거나, 원하는 값으로 변경하기 위해 사용할 수 있습니다.

 

tuist 프로젝트에서도 물론 이러한 fastlane의 기능을 사용할 수 있습니다. 해당 lane에 존재하는 parameter를 통해 목표가 되는 xcodeproj 파일의 경로를 명시하기만 하면 됩니다.

 

아래의 set_version lane은 지정된 경로에 있는 xcodeproj 파일의 version 넘버를 원하는 값으로 세팅하고, build number를 현재 시간에 맞게 자동으로 생성해주는 기능을 합니다.

desc 'Set Marketing and Build version'
lane :set_version do |version|
increment_version_number(
  version_number: version[:version],
  xcodeproj: "./Projects/iOSApp/Application/EarthIsRound.xcodeproj"
)

increment_build_number(
  build_number: Time.new.strftime("%Y.%m%d.%H%M"), # 2023.0228.1138
  xcodeproj: "./Projects/iOSApp/Application/EarthIsRound.xcodeproj"
)
end

 

주의해야 할 점이 한 가지 있습니다. increment_build_number의 경우에는 괜찮지만, increment_version_number를 통해 MARKETING_VERSION을 증가시키고자 할 때에는 Xcode의 Build Setting을 미리 세팅해 두어야 합니다. agvtool(apple generic versioning tool)을 사용하기 위해서라고 하는데, 링크에 자세한 설명이 있습니다.

 

1. VERSIONING_SYSTEM = apple-generic

2. CURRENT_PROJECT_VERSION = 1(정수 또는 유리수 형태)

 

저는 xcconfigs를 사용하여 build setting을 관리하기에 xcconfig에 VERSIONING_SYSTEM, CURRENT_PROJECT_VERSION을 추가해 주었습니다. shared config은 project 타겟의 모든 configs들이 상속하는 config이기 때문에 Scheme에 관계 없이 모두 agvtool을 이용한 increment version 기능을 사용할 수 있게 됩니다.

 

지금까지 확인하셨다시피, tuist와 fastlane 같은 툴을 사용할 때에 빌드 세팅을 건드려야 할 일이 많습니다. 이 페이지에서 빌드 세팅에 대한 정보를 상세하게 제공하고 있습니다.

fastlane으로 xcconfig의 값을 변경하기

fastlane에는 다른 사용자가 구현한 기능들을 손쉽게 사용할 수 있도록 plugin을 제공합니다. 그 중에서는 xcconfig의 값을 확인하고 변경할 수 있게 해주는 plugin이 있습니다. 다양한 custom lane 및 action들이 존재하기 때문에, 원하는 기능을 구현할 때 plugin의 도움을 받을 수 있습니다.

 

https://github.com/sovcharenko/fastlane-plugin-xcconfig

 

GitHub - sovcharenko/fastlane-plugin-xcconfig

Contribute to sovcharenko/fastlane-plugin-xcconfig development by creating an account on GitHub.

github.com

다음 스텝

이번 편에서는 tuist와 fastlane을 사용하여 signing 및 build setting을 다루는 방법에 대해 알아보았습니다. 다음 편에서는 fastlane과 github action을 이용하여 가상 머신에서 tuist project를 배포하는 방법에 대해 다루어 보겠습니다.