개발아 담하자

[iOS] Tuist 를 활용한 프로젝트 모듈화 (1) 본문

📱 iOS

[iOS] Tuist 를 활용한 프로젝트 모듈화 (1)

choidam 2022. 8. 19. 18:08

안녕하세요! 굉장히 오랜만에 포스팅을 작성합니다.

오늘은 tuist 를 활용한 프로젝트 모듈화를 소개해보려고 합니다.

 

회사에서는 이미 tuist 를 사용하고 있었지만 기능 단위로 모듈화를 하고 있지는 않았고

저도 관련 내용에 대해서 사실 잘 모르고 있었는데 (ㅋㅋ)

올해 여름 넥스터즈 21기 활동을 하면서 tuist에 대해 많이 공부했고 그 내용을 블로그에 정리해보려고 합니다.

고고~~

Tuist 란? 

다들 알고있겠지만,, 그래도 다시 정리하자면

Tuist는 Xcode프로젝트를 생성, 관리하는 command line tool입니다.

협업 때 프로젝트 충돌을 방지하기 위해서 사용합니다.

 

외워두면 좋은 기본 명령어

tuist에서 주로 사용하는 기본 명령어 들입니다.

 

1. tuist 설치하기 (맨처음)

curl -Ls https://install.tuist.io | bash

 

2. 기본 프로젝트 생성하기

tuist init --platform ios

 

3. 프로젝트 파일 생성

tuist generate

 

4. tuist 파일 수정하고 싶은 경우

tuist edit

 

5. 외부 Dependency 가져오기

tuist fetch

Dependency.swift 를 통해서 외부 라이브러리를 로컬로 불러오는데

fetch 명령어를 하지 않으면 tuist generate 해도 라이브러리를 찾을 수 없는 상태가 됩니다.

 

tuist generate 명령어가 먹지 않는다면 tuist fetch 를 먼저 해보기 기억해 둡시다~

 

Tuist 기능 단위 모듈화

사실상 이것을 작성하기 위한 빌드업..ㅎㅎ

그래서 기능 단위 모듈화는 어떻게 하는지 예제를 통해 알아봅시당

 

tuist init --platform ios // tuist 초기화
tuist edit // tuist 파일 수정

 

원하는 디렉토리에 가서 처음 Init을 해줬을 때 상태입니다.

이제 여기에 파일을 하나씩 작성해줘야 합니다.

 

프로젝트 디렉토리 구조는 넥터에서 같이 개발해준 동규랑 수현이랑,,

https://ios-development.tistory.com/1006?category=899471 

이 분 블로그를 많이 참고했습니다. ㅎㅎ

 

먼저 Manifests 하위에

Project 폴더를 만들고, 기존 Project.swift 파일은 삭제합니다. 대신 Workspace.swift 파일을 추가합니다.

그럼 이런 구조가 됩니다.

Project 폴더에서는 Project폴더들을 만들어 관리하고, 

Tuist 폴더에서는 tuist 관련 템플릿이나 정보들을 관리할 것입니다.

 

약간 감이 잘 안 잡히지만 하나씩 작성을 시작해 봅시다.

Workspace.swift

import ProjectDescription

let appName = "MyApp"

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

workspace 는 다수의 Project 들을 관리하는 역할을 합니다.

파일 위치는 상관 없지만 공식 문서에서는 프로젝트 루트 안에 바로 추가하는 것을 추천한다고 하네용

Dependency.swift

Dependencies.swift

Tuist 폴더 바로 하위에 Dependencies 파일을 추가합니다.

tuist fetch 명령어를 통해 외부 라이브러리를 관리하는 역할을 합니다.

let dependencies = Dependencies(
    carthage: [],
    swiftPackageManager: [
        .remote(url: "https://github.com/Alamofire/Alamofire", requirement: .upToNextMajor(from: "5.0.0"))
    ],
    platforms: [.iOS]
)

저는 SPM을 사용해 라이브러리를 추가했습니다.

 

tuist 공식 문서를 참고하면 carthage, cocoapods 내용도 있으니 확인해주세요! 

(라이브러리들이 점점 코코아팟을 지원하지 않는 추세이다 보니 더 이상 Dependencies 파일에서 코코아팟 지원을 하지 않는다고 하니 참고해 주세용)

https://docs.tuist.io/manifests/workspace/

 

Workspace.swift | Tuist Documentation

This page documents how the Workspace.swift manifest file can be used to group projects together, add additional files, and define workspace schemes.

docs.tuist.io

 

Project+Templates.swift

파일 추가 없이 원래 기존에 있던 파일에 작성하면 됩니다.

말 그대로 project 템플릿 관련 코드를 작성하는 파일입니다.

extension Project {
    private static let organizationName = "team.io"
    
    public static func app(name: String,
                           platform: Platform,
                           iOSTargetVersion: String,
                           infoPlist: String,
                           dependencies: [TargetDependency] = []) -> Project {
        let targets = makeAppTargets(name: name,
                                     platform: platform,
                                     iOSTargetVersion: iOSTargetVersion,
                                     infoPlist: infoPlist,
                                     dependencies: dependencies)
        return Project(name: name,
                       organizationName: organizationName,
                       targets: targets)
    }

    public static func frameworkWithDemoApp(name: String,
                                            platform: Platform,
                                            iOSTargetVersion: String,
                                            infoPlist: [String: InfoPlist.Value] = [:],
                                            dependencies: [TargetDependency] = []) -> Project {
        var targets = makeFrameworkTargets(name: name,
                                           platform: platform,
                                           iOSTargetVersion: iOSTargetVersion,
                                           dependencies: dependencies)
        targets.append(contentsOf: makeAppTargets(name: "\(name)DemoApp",
                                                  platform: platform,
                                                  iOSTargetVersion: iOSTargetVersion,
                                                  infoPlist: infoPlist,
                                                  dependencies: [.target(name: name)]))

        return Project(name: name,
                       organizationName: organizationName,
                       targets: targets)
    }

    public static func framework(name: String,
                                 platform: Platform, iOSTargetVersion: String,
                                 dependencies: [TargetDependency] = []) -> Project {
        let targets = makeFrameworkTargets(name: name,
                                           platform: platform,
                                           iOSTargetVersion: iOSTargetVersion,
                                           dependencies: dependencies)
        return Project(name: name,
                       organizationName: organizationName,
                       targets: targets)
    }
}

하나하나 톺아보자면 너무 길어질 것 같아서.. 간단하게 정리하자면

 

맨 처음 app은 전체 프로젝트를 관리하는 프로젝트를 만들고

frameworkWithDemoApp 은 우리가 원하는 모듈 프로젝트를 만듭니다. 

(뒤이어 프로젝트 파일에서 호출함)

 

Projects

이제 각 모듈별로 project 들을 만들어 봅시다.

저는 홈화면을 관리하는 Home 모듈을 추가하도록 하겠습니다.

Project 폴더 하위에 Home 폴더를 추가하고, 그 안에 Project.swift 파일을 추가했습니다.

 

//
//  Project.swift
//  ProjectDescriptionHelpers
//
//  Created by choidam on 2022/08/23.
//

import ProjectDescriptionHelpers
import ProjectDescription

private let projectName = "Home"
private let iOSTargetVersion = "14.0"

let infoPlist: [String: InfoPlist.Value] = [
    "UILaunchScreen": [:]
]

let project = Project.frameworkWithDemoApp(name: projectName,
                                           platform: .iOS,
                                           iOSTargetVersion: iOSTargetVersion,
                                           infoPlist: infoPlist,
                                           dependencies: [ ]
                                           )

홈 타겟은 프로젝트 템플릿에서 앞서 작성했던 .frameworkWithDemoApp 을 호출해 만듭니다.

 

모듈별 타겟 말고 전체를 실행시킬 수 있는 타겟을 만들어 봅시다.

Project 폴더 안에 App 폴더를 만들어 안에 Project.swift 파일을 만듭니다.

//
//  Project.swift
//  ProjectDescriptionHelpers
//
//  Created by choidam on 2022/08/23.
//

import ProjectDescription
import ProjectDescriptionHelpers

private let projectName = "App"
private let iOSTargetVersion = "14.0"

let infoPlistPath: String = "Resources/App.plist"

let project = Project.app(name: projectName,
                          platform: .iOS,
                          iOSTargetVersion: iOSTargetVersion,
                          infoPlist: infoPlistPath,
                          dependencies: [
                            .project(target: "Home", path: .relativeToCurrentFile("../Home"))
                          ])

전체 타겟은 프로젝트 템플릿에서 앞서 작성했던 .app 을 호출해 만듭니다.

dependencies 에서 앞서 작성했던 홈 타겟을 추가합니다. 

이 때 path 를 주의해서 작성해 주세요!

 

최종 프로젝트 구조입니다!

⚠️쓰면서 발견한 건데 Manifests 바로 하위 폴더이름은 Project 가 아니라 Projects 가 맞습니다!!!!⚠️

 

이제 tuist fetch 명령어 실행 후 tuist generate 를 하면 

Resources, Sources, Tests 파일들을 찾을 수 없다 어쩌구 저쩌구 에러가 나옵니다,,

 

이 때 당황하지 않고 

직접 파인더 안에 들어가 추가하라는 폴더들을 직접 추가해줍니다 .. 귀찮지만.. 

 

다시 tuist generate 을 하면!! 성공

원하는 대로 App(전체), Home 타겟이 두 개 나와있습니다.

감격..!

 

 

전체 코드 내용은 깃 주소를 첨부합니다.

https://github.com/choidam/tuist_sample

 

GitHub - choidam/tuist_sample: tuist 샘플 코드 레포지토리입니다.

tuist 샘플 코드 레포지토리입니다. Contribute to choidam/tuist_sample development by creating an account on GitHub.

github.com

 

 

그 다음 활용 방법은,,

너무 길어져서 다음에,,

 

두번째는 아래!

https://silver-g-0114.tistory.com/151

 

[iOS] Tuist 를 활용한 프로젝트 모듈화 (2)

Tuist 모듈화 1편 https://silver-g-0114.tistory.com/150?category=1094592 [iOS] Tuist 를 활용한 프로젝트 모듈화 (1) 안녕하세요! 굉장히 오랜만에 포스팅을 작성합니다. 오늘은 tuist 를 활용한 프로젝트 모..

silver-g-0114.tistory.com