개발/Swift

Tuist활용하여 멀티 모듈 SwiftUI 프로젝트 생성하기

덤벨로퍼 2024. 10. 15. 22:26

 

Swift UI 용으로 init 해줌 , 반드시 빈 폴더여야 함

 tuist init --platform ios --template swiftui

tuist edit 하면 세팅할수있음

모듈형태면 Project가 아닌 Workspace사용하므로 Project 파일 지워버리고

Workspace 파일 생성함

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

이렇게 그냥 간단지게 만들어버림

 

그러고 Project 폴더에

App - 메인

Module - 생성할 모듈

폴더를 생성함, 그러고 Project.swift 파일을 새로 만듬

나는 Network 모듈, Entity 모듈을 생성함

 

 

App에서 Project 객체를 생성

import Foundation
import ProjectDescription

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

let project = Project(name: "MyWeather", //이름
                      organizationName: "MyName",
                      targets: [
                        Target(name: "MyWeather", platform: .iOS, product: .app, bundleId: "simon.kang.myweather",
                               infoPlist: .extendingDefault(with: infoPlist),
                               sources: ["Sources/**"],
                               resources: ["Resources/**"],
                               dependencies: [
                                .project(target: "Network", path: "../Network"), //에러나면 이거 지웠다가 나중에 추가
                                .project(target: "Entity", path: "../Entity"),
                               ]
                              )
                      ]
)

번들 아이디, 이름 지정 해야 하고 파일경로 리소스 경로 지정해줬으니

이에 맞게 Sources, Resources폴더 생성 해야함

 

그러고 모듈에서도 똑같이 Project 생성하되

name 바꿔주고

product를 framework로 변경

dependencies는 빈배열로

import Foundation
import ProjectDescription

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

let project = Project(name: "Entity",
                      organizationName: "MyName",
                      targets: [
                        Target(name: "Entity", platform: .iOS, product: .framework, bundleId: "simon.kang.myweather",
                               infoPlist: .extendingDefault(with: infoPlist),
                               sources: ["Sources/**"],
                               dependencies: [
                               ]
                              )
                      ])

infoPlist 변수명같다고 compile 에러났지만 무시하니 사라졌음

Sources 폴더도 만들어줘야함

이러고 tuist generate

 

이렇게 세개 모듈 생김

이제 Project내부에 Source 폴더 추가해주고 파일 하나씩 아무거나 추가해줘야함

SwiftUI main view도 없으니 하나 만들어줌

 

import 까지 성공

외부 라이브러리사용

tuist edit → Tuist 폴더내부에 Dependencies파일 생성

import ProjectDescription

let spm = SwiftPackageManagerDependencies( [
    .remote(url: "<https://github.com/Alamofire/Alamofire.git>", requirement: .upToNextMajor(from: "5.10.0"))
])

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

이렇게 Spm 디펜던시추가함

그리고 이 라이브러리가 필요한 Network모듈 project에 dependencies 에 추가

let project = Project(name: "Network",
                      organizationName: "MyName",
                      targets: [
                        Target(name: "Network", platform: .iOS, product: .framework, bundleId: "simon.kang.myweather",
                               infoPlist: .extendingDefault(with: infoPlist),
                               sources: ["Sources/**"],
                               dependencies: [
                                .external(name: "Alamofire")
                               ]
                              )
                      ])

이러고 tuist fetch

Resolving and fetching plugins.
Plugins resolved and fetched successfully.
Resolving and fetching dependencies.
Installing Swift Package Manager dependencies.
Fetching <https://github.com/Alamofire/Alamofire.git> from cache
Fetched <https://github.com/Alamofire/Alamofire.git> from cache (1.83s)
Computing version for <https://github.com/Alamofire/Alamofire.git>
Computed <https://github.com/Alamofire/Alamofire.git> at 5.10.0 (0.46s)
Creating working copy for <https://github.com/Alamofire/Alamofire.git>
Working copy of <https://github.com/Alamofire/Alamofire.git> resolved at 5.10.0
Swift Package Manager dependencies installed successfully.
Dependencies resolved and fetched successfully.

성공

Swift UI 용으로 init 해줌 , 반드시 빈 폴더여야 함

 tuist init --platform ios --template swiftui

tuist edit 하면 세팅할수있음

모듈형태면 Project가 아닌 Workspace사용하므로 Project 파일 지워버리고

Workspace 파일 생성함

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

이렇게 그냥 간단지게 만들어버림

그러고 Project 폴더에

App - 메인

Module - 생성할 모듈

폴더를 생성함, 그러고 Project.swift 파일을 새로 만듬

나는 Network 모듈, Entity 모듈을 생성함

App에서 Project 객체를 생성

import Foundation
import ProjectDescription

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

let project = Project(name: "MyWeather", //이름
                      organizationName: "MyName",
                      targets: [
                        Target(name: "MyWeather", platform: .iOS, product: .app, bundleId: "simon.kang.myweather",
                               infoPlist: .extendingDefault(with: infoPlist),
                               sources: ["Sources/**"],
                               resources: ["Resources/**"],
                               dependencies: [
                                .project(target: "Network", path: "../Network"), //에러나면 이거 지웠다가 나중에 추가
                                .project(target: "Entity", path: "../Entity"),
                               ]
                              )
                      ]
)

뭐 내용 별거없으니 패스 번들아이디 알아서 이름 알아서 그러고 파일경로 리소스 경로 지정해줬으니

이에 맞게 Sources, Resources폴더 생성 해야함

그러고 모듈에서도 똑같이 Project 생성하되

name 바꿔주고

product를 framework로 변경

dependencies는 빈배열로

import Foundation
import ProjectDescription

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

let project = Project(name: "Entity",
                      organizationName: "MyName",
                      targets: [
                        Target(name: "Entity", platform: .iOS, product: .framework, bundleId: "simon.kang.myweather",
                               infoPlist: .extendingDefault(with: infoPlist),
                               sources: ["Sources/**"],
                               dependencies: [
                               ]
                              )
                      ])

infoPlist 변수명같다고 compile 에러났지만 무시하니 사라졌음

Sources 폴더도 만들어줘야함

이러고 tuist generate

이렇게 세개 모듈생김

이제 Project내부에 Source 폴더 추가해주고 파일 하나씩 아무거나 추가해줘야함

SwiftUI main view도 없으니 하나 만들어줌

import 까지 성공

외부 라이브러리사용

tuist edit → Tuist 폴더내부에 Dependencies파일 생성

import ProjectDescription

let spm = SwiftPackageManagerDependencies( [
    .remote(url: "<https://github.com/Alamofire/Alamofire.git>", requirement: .upToNextMajor(from: "5.10.0"))
])

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

이렇게 Spm 디펜던시추가함

그리고 이 라이브러리가 필요한 Network모듈 project에 dependencies 에 추가

let project = Project(name: "Network",
                      organizationName: "MyName",
                      targets: [
                        Target(name: "Network", platform: .iOS, product: .framework, bundleId: "simon.kang.myweather",
                               infoPlist: .extendingDefault(with: infoPlist),
                               sources: ["Sources/**"],
                               dependencies: [
                                .external(name: "Alamofire")
                               ]
                              )
                      ])

이러고 tuist fetch

Resolving and fetching plugins.
Plugins resolved and fetched successfully.
Resolving and fetching dependencies.
Installing Swift Package Manager dependencies.
Fetching <https://github.com/Alamofire/Alamofire.git> from cache
Fetched <https://github.com/Alamofire/Alamofire.git> from cache (1.83s)
Computing version for <https://github.com/Alamofire/Alamofire.git>
Computed <https://github.com/Alamofire/Alamofire.git> at 5.10.0 (0.46s)
Creating working copy for <https://github.com/Alamofire/Alamofire.git>
Working copy of <https://github.com/Alamofire/Alamofire.git> resolved at 5.10.0
Swift Package Manager dependencies installed successfully.
Dependencies resolved and fetched successfully.

성공

 

문제

1. SwfitUI의 view가 위 아래 잘린 형태로 노출이됨

코드로 별짓을 다해봐도 적용이 안됨, 커뮤니티 통해 얻은 소스 LaunchScreen 세팅이 없어서 그렇다는 이야기를 들음

자세히 알아보니 infoPlist에 원래 LaunchScreen 정보가 비어있더라고 들어있음

반면 내 infiPlist는 빈값을 넣어주어서 비어있는 상태였음

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

이렇게 넣어주었다 배경색만 널어준건데 빈 Dictionary 넣어주어도 무방해보임 → 해결 완료

 

2. 모듈에서 번들 관리하기

Network 모듈에서 Secret.plist 파일을 두고 Bundle 접근하려니 불러오지 못함

extension Bundle {
    var apiKey: String? {
        guard let file = self.path(forResource: "Secret", ofType: "plist"),
              let resource = NSDictionary(contentsOfFile: file),
              let key = resource["APIKEY"] as? String else {
            print("API KEY를 가져오는데 실패하였습니다.")
            return nil
        }
    
        return key
    }
    
}

//사용 예시
let apikey = Bundle.main.apiKey

일반적으로 Bundle.main 접근을 하면 모듈이 아닌 메인 앱에서 접근하는듯 하고

Secret.plist 파일은 모듈 내부에 있으므로 찾지를 못함

해결을 위해서 모듈에서는 접근 방식을 달리 해야함

 

우선 모듈에서 Resource 폴더 생성후 Secret.plist을 집어 넣어주고 (파일 타겟 설정 확인)

Target 에서 경로 지정을 해주고 generate

  resources: ["Resources/**"],

그리고 접근할떄 모듈 내부 Class (아무파일이나 상관없음) 를 사용한 번들 생성을 해야함

class BundleManager {
    static var apiKey: String? {
        let bundle = Bundle(for: BundleManager.self)
        guard let file = bundle.path(forResource: "Secret", ofType: "plist"),
              let resource = NSDictionary(contentsOfFile: file),
              let key = resource["APIKEY"] as? String else {
            print("API KEY를 가져오는데 실패하였습니다.")
            return nil
        }
        
        return key
    }
}

이렇게 접근 하면 성공