천원의 개발

iOS Tuist 적용하여 여러 프로젝트 관리하기 본문

iOS&Swift🍎/iOS

iOS Tuist 적용하여 여러 프로젝트 관리하기

천 원 2023. 10. 24. 08:59

안녕하세요. 천원입니다.

이 글은 제가 Tuist를 학습하면서 여러 프로젝트를 하나의 Workspace로 관리하는 방법을 정리한 글이며 앞선 포스팅을 확인하시면 조금 더 이해하기 편하실 것 같습니다.

 

여러프로젝트 관리하기

먼저 Workspace를 생성할 폴더를 만들어 줍니다.

mkdir 폴더명
cd 폴더명

tuist init

tuist init --platform ios

editing

tuist edit

그러면 위와 같이 Project파일이 생성되는데 우리는 Root를 Project로 만들지 않을 테니 삭제해 줍시다. 그런 다음 Project를 대신해서 Workspace 파일을 추가해 줍니다.

import ProjectDescription

let workspace = Workspace(
    name: "TuistTour",
    projects: [
        "Projects/**"
    ]
)

Workspace 명과 프로젝트를 관리할 위치를 명시해 줍시다. 

 

그런 다음 프로젝트들을 관리할 폴더를 생성해 줍니다.

 

이제 생성한 Projects 폴더 내부에 추가할 프로젝트들의 폴더를 생성해 주고 프로젝트 생성을 위한 Project파일을 각각 추가해 줍시다. 저는 App과 네트워크를 담당할 NetworkKit 이렇게 두가지를 생성해 주겠습니다.

 

그리구 간단하게 각각의 Project를 만들어 줍니다. 저는 아래와 같이 만들어 주었습니다.

// APP Project
let infoPlist: [String: InfoPlist.Value] = [:]

let project = Project(
    name: "TuistTour",
    organizationName: "MyOrg",
    targets: [
        Target(
            name: "TuistTour",
            platform: .iOS,
            product: .app,
            bundleId: "io.tuist.MyApp",
            infoPlist: .extendingDefault(with: infoPlist),
            sources: ["Sources/**"],
            resources: ["Resources/**"],
            dependencies: [
                /* Target dependencies can be defined here */
                /* .framework(path: "framework") */
            ]
        )]
)
// NetWorkKit Project
let infoPlist: [String: InfoPlist.Value] = [:]

let project = Project(
    name: "NetworkKit",
    organizationName: "MyOrg",
    targets: [
        Target(
            name: "NetworkKit",
            platform: .iOS,
            product: .framework,
            bundleId: "io.tuist.NetworkKit",
            infoPlist: .extendingDefault(with: infoPlist),
            sources: ["Sources/**"],
            dependencies: [
                /* Target dependencies can be defined here */
                /* .framework(path: "framework") */
            ]
        )]
)

차이점을 조금 보자면 각각의 target의 Product가 .app과 .framework로 설정이 되어있고 NetwrokKit에는 Resoures 폴더가 필요없어서 추가하지 않은 모습입니다. 

이제 각각의 Project에 필요한 Sources와 Resoures 디렉토리를 추가해줍니다.

 

이제 control + c 로 manifests 종료해 주고 아래 명령어로 실행해 줍니다.

tuist fetch
tuist generate

 

그러면 정상적으로 두개의 프로젝트가 들어가 있는 모습을 확인할 수 있습니다.

 

이제 우리가 프로젝트를 실행할 수 있도록 내부에 파일이 존재하지 않아 Xcode에 추가되지 않은 Sources파일과 Resources 파일을 Add 해주고 내부에 앱을 실행할 수 있도록 Appdelegate와 Assets파일을 추가해 줍니다.

Sources, Resources 불러오기
AppDelegate, ViewController, Assets 생성

이렇게 AppDelegate와 ViewController를 생성해 주고 빌드를 해보면 아이콘이 필요하다고 하네요. 

아이콘을 추가해 주고 빌드를 해보면 정상적으로 빌드가 됩니다.

음 그런데 뭔가 화면에 꽉안차고 이상하게 보이죠..? 이걸 해결하기 위해 App Project의 내부로 가서 InfoPlist를 수정해 줍시다. tuist edit 명령어를 통해서 수정할 수 있지만 귀찮으니까 그냥 Project파일만 켜서 수정해 줍니다.

// APP의 Project 파일 info 수정
let infoPlist: [String: Plist.Value] = [
    "UILaunchStoryboardName":"LaunchScreen", // 화면 정상적으로 띄우기
    "CFBundleVersion": "1" // 번들 버전 오류 방지
]

혹시 CFBundleVersion 오류가 발생하신다면 info에 같이 추가해 주시면 됩니다. tuist generate 해주면 정상적으로 Build가 가능한 모습입니다.

 

NetworkKit import 하기

우리 App에서 사용하기 위해 App에 NetworkKit을 의존하게 하고 import를 통해서 불러와 사용할 수 있게 해봅시다.

tuist edit -> app Project -> dependencies 에 NetworkKit 추가 후 -> tuist generate

import ProjectDescription

let infoPlist: [String: Plist.Value] = [
    "UILaunchStoryboardName":"LaunchScreen",
    "CFBundleVersion": "1"
]

let project = Project(
    name: "TuistTour",
    organizationName: "MyOrg",
    targets: [
        Target(
            name: "TuistTour",
            platform: .iOS,
            product: .app,
            bundleId: "io.tuist.MyApp",
            infoPlist: .extendingDefault(with: infoPlist),
            sources: ["Sources/**"],
            resources: ["Resources/**"],
            dependencies: [
               .project(target: "NetworkKit", path: "../NetworkKit") // 추가
            ]
        )]
)

이제 ViewController에서 import를 해주면 NetworkKit을 찾지 못합니다.

NetworkKit에 Sources파일이 없어서 import가 안된거니 원하는 파일을 추가해 줍시다. 저는 디버그 모드에 로그를 출력해주는 코드를 작성해 주었습니다.

이제 NetworkKit을 import 해주고 printLog를 호출하면 정상적으로 동작하는 모습입니다.

 

라이브러리 설치하기

마지막으로 알아볼 내용은 오픈소스 설치하기 입니다. 기본적으로 Tuist는 spm과 carthage을 지원을 합니다.

먼저 Tuist edit 해준후 Tuist 폴더 내부에 Dependencies 파일을 생성해 줍니다. 그런 다음 저는 Alamofire를 설치해 주는 코드를 작성해 주겠습니다.

import ProjectDescription

let spm = SwiftPackageManagerDependencies([
  .remote(
    url: "https://github.com/Alamofire/Alamofire.git", requirement: .exact("5.7.0")
  )
])

let dependencies = Dependencies(
    swiftPackageManager: spm,
    platforms: [.iOS]
)

spm 설치

그 후에 사용하고자 하는 프로젝트의 Project파일로 가서 dependencies를 추가해 줍니다. 저는 NetworkKit에 추가해 주겠습니다.

.external(name: )을 사용해서 추가

이제 fetch generate 해주면 Tuist 폴더가 추적이 되고 Alamofire를 정상적으로 NetworkKit에서 import 할 수 있습니다. Tuist는 특이하게 패키지를 사용하는 것이 아닌 sub Project를 만들어 framework형태로 라이브러리를 의존하게 만든다고 합니다.

import Alamofire

 

마지막으로 tuist의 편리한 기능을 하나 소개시켜 드리면 

tuist graph

명령어를 통해서 현재 어떤 의존관계를 가지고 있는지 한눈에 볼 수 있어서 복잡한 의존관계를 가진 앱에서 불필요한 의존성을 파악하기 좋아 보여서 소개 시켜 드립니다.

 

조금 더 복잡한 의존성을 가진 프로젝트인데  SDK나 오픈소스 내부에 어떤 의존관계를 가지는지 확인하기도 좋은 것 같네요.

 

여기까지 Tuist 정리였습니다. 감사합니다.

 

 

https://techblog.yogiyo.co.kr/ios-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-tuist-%EC%A0%81%EC%9A%A9%EA%B8%B0-1d8f7d489252

https://docs.tuist.io/tutorial/get-started