본문으로 바로가기

Xcode 프로젝트 마이그레이션

Tuist를 사용해 새로운 프로젝트 생성을 하지 않는다면 Tuist의 기본 구성 요소를 사용해 Xcode 프로젝트를 직접 정의해야 합니다. 이 과정이 얼마나 번거로운지는 프로젝트의 복잡도에 따라 달라집니다.

이미 알고 있듯이, Xcode 프로젝트는 시간이 지남에 따라 더 복잡하고 어려워질 수 있습니다: 디렉토리 구조와 일치하지 않는 그룹, 여러 타겟 간에 공유되는 파일, 존재하지 않는 파일 참조 등이 그 예입니다. 이러한 모든 복잡성은 프로젝트를 안정적으로 마이그레이션하는 명령어를 제공하기가 어렵습니다.

수동 마이그레이션은 프로젝트를 정리하고 단순화하는데 좋은 기회이기도 합니다. 프로젝트의 개발자 뿐만 아니라 프로젝트를 더 빠르게 처리하고 인덱싱할 수 있는 Xcode에도 도움이 됩니다. Tuist를 적용한 후에는 프로젝트가 일관되게 정의되고 단순한 상태를 유지하도록 보장합니다.

이 작업을 좀 더 쉽게 하기위해 사용자로부터 받은 피드백 기준으로 몇 가지 라이드라인을 제공합니다.

프로젝트 기본 구조 생성#

먼저 다음의 Tuist 파일로 프로젝트의 기본 구조를 생성합니다:

javascript
import ProjectDescription
let tuist = Tuist()
javascript
import ProjectDescription
let project = Project(
name: "MyApp-Tuist",
targets: [
/** Targets will go here **/
]
)
javascript
// swift-tools-version: 5.9
import PackageDescription
#if TUIST
import ProjectDescription
let packageSettings = PackageSettings(
// Customize the product types for specific package product
// Default is .staticFramework
// productTypes: ["Alamofire": .framework,]
productTypes: [:]
)
#endif
let package = Package(
name: "MyApp",
dependencies: [
// Add your own dependencies here:
// .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
// You can read more about dependencies here: https://docs.tuist.io/documentation/tuist/dependencies
]
)

Project.swift는 프로젝트를 정의하는 매니페스트 파일이며 Package.swift는 의존성을 정의하는 매니페스트 파일입니다. Tuist.swift는 프로젝트 범위에서 Tuist 설정을 정의할 수 있는 파일입니다.

프로젝트 이름에 접미사 Tuist

기존 Xcode 프로젝트에서 충돌을 방지하기 위해 프로젝트 이름 뒤에 -Tuist 접미사를 붙이길 권장합니다. 프로젝트를 Tuist로 완전히 마이그레이션한 후에 이를 제거할 수 있습니다.

CI에서 Tuist 프로젝트 빌드와 테스트#

각 변경 사항의 마이그레이션이 유효한지 확인하기 위해 매니페스트 파일로부터 Tuist가 생성한 프로젝트를 빌드하고 테스트하도록 CI 확장을 권장합니다:

bash
tuist install
tuist generate
xcodebuild build {xcodebuild flags} # or tuist test

프로젝트 빌드 설정을 .xcconfig 파일로 분리#

프로젝트의 빌드 설정을 .xcconfig 파일로 분리하면 프로젝트는 더 간결하고 마이그레이션하기 쉬워집니다. 다음 명령어를 사용해 프로젝트의 빌드 설정을 .xcconfig 파일로 분리할 수 있습니다:

bash
mkdir -p xcconfigs/
tuist migration settings-to-xcconfig -p MyApp.xcodeproj -x xcconfigs/MyApp-Project.xcconfig

그런 다음에 방금 생성된 .xcconfig 파일을 참조하도록 Project.swift 파일을 업데이트합니다:

swift
import ProjectDescription
let project = Project(
name: "MyApp",
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/MyApp-Project.xcconfig"), // [!code ++]
.release(name: "Release", xcconfig: "./xcconfigs/MyApp-Project.xcconfig"), // [!code ++]
]),
targets: [
/** Targets will go here **/
]
)

그런 다음에 빌드 설정 변경이 직접적으로 .xcconfig 파일에 적용되기 위해 다음 명령어를 수행하도록 CI 파이프라인을 확장합니다:

bash
tuist migration check-empty-settings -p Project.xcodeproj

패키지 의존성 분리#

프로젝트의 모든 의존성을 Tuist/Package.swift 파일로 분리합니다:

swift
// swift-tools-version: 5.9
import PackageDescription
#if TUIST
import ProjectDescription
let packageSettings = PackageSettings(
// Customize the product types for specific package product
// Default is .staticFramework
// productTypes: ["Alamofire": .framework,]
productTypes: [:]
)
#endif
let package = Package(
name: "MyApp",
dependencies: [
// Add your own dependencies here:
// .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
// You can read more about dependencies here: https://docs.tuist.io/documentation/tuist/dependencies
.package(url: "https://github.com/onevcat/Kingfisher", .upToNextMajor(from: "7.12.0")) // [!code ++]
]
)
Product Types

특정 패키지의 타입을 재정의하려면 PackageSettings 구조체의 productTypes 딕셔너리에 해당 패키지를 추가합니다. 기본적으로 Tuist는 모든 패키지가 정적 프레임워크라고 가정합니다.

마이그레이션 순서 결정#

의존성이 많은 타겟부터 가장 적은 순으로 마이그레이션하는 것을 추천합니다. 다음 명령어를 사용해 의존성 개수에 따라 정렬된 프로젝트의 타겟 목록을 확인할 수 있습니다:

bash
tuist migration list-targets -p Project.xcodeproj

목록의 가장 위에 있는 타겟이 의존성이 가장 많으므로 위에서 부터 마이그레이션을 시작합니다.

타겟 마이그레이션#

타겟을 하나씩 마이그레이션합니다. 각 타겟의 풀 리퀘스트를 생성하여 변경사항이 머지되기 전에 검토와 테스트가 이루어지는 것을 권장합니다.

타겟 빌드 설정을 .xcconfig 파일로 추출#

프로젝트 빌드 설정에서 했던 것처럼 타겟 빌드 설정을 .xcconfig 파일로 추출하여 타겟을 더 간결하고 마이그레이션하기 쉽게 만듭니다. 다음 명령어를 사용하여 타겟의 빌드 설정에서 .xcconfig 파일을 추출합니다:

bash
tuist migration settings-to-xcconfig -p MyApp.xcodeproj -t TargetX -x xcconfigs/TargetX.xcconfig

Project.swift 파일에 타겟 정의#

Project.targets에 타겟 정의:

swift
import ProjectDescription
let project = Project(
name: "MyApp",
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/Project.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/Project.xcconfig"),
]),
targets: [
.target( // [!code ++]
name: "TargetX", // [!code ++]
destinations: .iOS, // [!code ++]
product: .framework, // [!code ++] // or .staticFramework, .staticLibrary...
bundleId: "dev.tuist.targetX", // [!code ++]
sources: ["Sources/TargetX/**"], // [!code ++]
dependencies: [ // [!code ++]
/** Dependencies go here **/ // [!code ++]
/** .external(name: "Kingfisher") **/ // [!code ++]
/** .target(name: "OtherProjectTarget") **/ // [!code ++]
], // [!code ++]
settings: .settings(configurations: [ // [!code ++]
.debug(name: "Debug", xcconfig: "./xcconfigs/TargetX.xcconfig"), // [!code ++]
.debug(name: "Release", xcconfig: "./xcconfigs/TargetX.xcconfig"), // [!code ++]
]) // [!code ++]
), // [!code ++]
]
)
Test Targets

타겟의 테스트 타겟을 가지고 있다면 동일하게 Project.swift 파일에 정의합니다.

타겟 마이그레이션 검증#

tuist generate 를 실행한 다음 xcodebuild build 를 실행하여 프로젝트가 빌드되는지 확인하고 tuist test 를 실행하여 테스트가 통과되었는지 확인합니다. 또한 xcdiff을 사용하여 생성된 Xcode 프로젝트를 기존 프로젝트와 비교하여 변경 사항이 올바른지 확인할 수 있습니다.

반복#

모든 대상이 완전히 마이그레이션될 때까지 반복합니다. 완료되면 CI 및 CD 파이프라인을 업데이트하여 tuist 생성 다음 xcodebuild 빌드 및 tuist 테스트 를 사용하여 프로젝트를 빌드하고 테스트하는 것이 좋습니다.

문제 해결#

파일 누락으로 인한 컴파일 오류.#

Xcode 프로젝트 타겟에 연관된 파일이 타겟에 포함되지 않는 경우 컴파일되지 않는 프로젝트가 생성될 수 있습니다. Tuist로 프로젝트를 생성한 후에 파일 목록이 Xcode 프로젝트의 파일 목록과 일치하는지 확인하고 이 기회에 파일 구조를 타겟 구조와 일치하도록 정리하는 것을 추천합니다.