-
[Tuist] Tuist를 활용하여 SwiftUI 클린 아키텍쳐를 적용한 모듈로 나눈 후 Github에 업로드 하기 2탄!iOS Dev 2023. 5. 7. 01:04반응형
1탄 : https://sunrinnote.tistory.com/172
[Tuist] Tuist를 활용하여 SwiftUI 클린 아키텍쳐를 적용한 모듈로 나눈 후 Github에 업로드 하기
Tuist라는 툴이 있다. 모듈화, 패키지 의존 관리, 타겟 관리 등등을 지원해준다고 한다. Tuist 자체를 소개해주는 블로그는 많이 있으니 건너뛴다. https://nsios.tistory.com/183 https://nsios.tistory.com/195 목표
sunrinnote.tistory.com
자동완성이 안되는 이유를 알았다. 그것은.. public을 안줘서..ㅠ
tuist edit으로 edit 페이지에 들어간 다음 Tuist/ProjectDescriptionHelpers/Project+Templates 코드를 다음과 같이 수정한다.
import ProjectDescription /// Project helpers are functions that simplify the way you define your project. /// Share code to create targets, settings, dependencies, /// Create your own conventions, e.g: a func that makes sure all shared targets are "static frameworks" /// See https://docs.tuist.io/guides/helpers/ public enum ModuleType: String, CaseIterable { case DoForestApp case DoForestCommon case DoForestCommonUI case DoForestData case DoForestDomain case DoForestEntity case DoForestMain case DoForestRoomDetail case DoForestSetting var dependencies: [TargetDependency] { switch self { case .DoForestApp: return [ .with(.DoForestMain), .with(.DoForestRoomDetail), .with(.DoForestSetting), .with(.DoForestData) ] case .DoForestCommon: return [] case .DoForestCommonUI: return [] case .DoForestData: return [ .with(.DoForestDomain) ] case .DoForestDomain: return [ .with(.DoForestEntity) ] case .DoForestEntity: return [] case .DoForestMain: return [ .with(.DoForestDomain), .with(.DoForestCommon), .with(.DoForestCommonUI) ] case .DoForestRoomDetail: return [ .with(.DoForestDomain), .with(.DoForestCommon), .with(.DoForestCommonUI) ] case .DoForestSetting: return [ .with(.DoForestDomain), .with(.DoForestCommon), .with(.DoForestCommonUI) ] } } } private let infoPlist: [String: InfoPlist.Value] = [ "CFBundleShortVersionString": "1.0", "CFBundleVersion": "1", "UIMainStoryboardFile": "", "UILaunchStoryboardName": "LaunchScreen" ] public extension Project { /// Helper function to create the Project for this ExampleApp static func app(name: String, platform: Platform, additionalTargets: [String]) -> Project { var targets = makeAppTargets(name: name, platform: platform, dependencies: additionalTargets.map { TargetDependency.target(name: $0) }) targets += additionalTargets.flatMap({ makeFrameworkTargets(name: $0, platform: platform) }) return Project(name: name, organizationName: "yourssu.com", targets: targets) } static func app(_ type: ModuleType) -> Project { let name = type.rawValue let dependencies = type.dependencies return Project(name: name, organizationName: "yourssu.com", targets: [ Target(name: name, platform: .iOS, product: .app, bundleId: "com.yourssu.\(name)", infoPlist: .extendingDefault(with: infoPlist), sources: ["Sources/**"], resources: ["Resources/**"], dependencies: dependencies ) ]) } static func framework(_ type: ModuleType) -> Project { let name = type.rawValue let dependencies = type.dependencies return Project(name: name, organizationName: "yourssu.com", targets: [ Target(name: name, platform: .iOS, product: .framework, bundleId: "com.yourssu.\(name)", infoPlist: .extendingDefault(with: infoPlist), sources: ["Sources/**"], resources: ["Resources/**"], dependencies: dependencies ) ]) } // MARK: - Private /// Helper function to create a framework target and an associated unit test target private static func makeFrameworkTargets(name: String, platform: Platform) -> [Target] { let sources = Target(name: name, platform: platform, product: .framework, bundleId: "com.yourssu.\(name)", infoPlist: .default, sources: ["Targets/\(name)/Sources/**"], resources: [], dependencies: []) let tests = Target(name: "\(name)Tests", platform: platform, product: .unitTests, bundleId: "com.yourssu.\(name)Tests", infoPlist: .default, sources: ["Targets/\(name)/Tests/**"], resources: [], dependencies: [.target(name: name)]) return [sources, tests] } /// Helper function to create the application target and the unit test target. private static func makeAppTargets(name: String, platform: Platform, dependencies: [TargetDependency]) -> [Target] { let platform: Platform = platform let infoPlist: [String: InfoPlist.Value] = [ "CFBundleShortVersionString": "1.0", "CFBundleVersion": "1", "UIMainStoryboardFile": "", "UILaunchStoryboardName": "LaunchScreen" ] let mainTarget = Target( name: name, platform: platform, product: .app, bundleId: "com.yourssu.\(name)", infoPlist: .extendingDefault(with: infoPlist), sources: ["Targets/\(name)/Sources/**"], resources: ["Targets/\(name)/Resources/**"], dependencies: dependencies ) let testTarget = Target( name: "\(name)Tests", platform: platform, product: .unitTests, bundleId: "com.yourssu.\(name)Tests", infoPlist: .default, sources: ["Targets/\(name)/Tests/**"], dependencies: [ .target(name: "\(name)") ]) return [mainTarget, testTarget] } } public extension TargetDependency { static func with(_ type: ModuleType) -> Self { let name = type.rawValue return .project(target: name, path: .relativeToRoot("DoForest/\(name)")) } }
이제 각 모듈 안에 있는 Project.swift 파일을 모조리 수정한다.
App모듈인 DoForestApp 폴더 안에는 다음과 같이.
// // Project.swift // ProjectDescriptionHelpers // // Created by 정종인 on 2023/04/03. // import Foundation import ProjectDescription import ProjectDescriptionHelpers let project = Project.app(.DoForestApp)
Framework 모듈인 DoForestMain에는 다음과 같이.
import Foundation import ProjectDescription import ProjectDescriptionHelpers let project = Project.framework(.DoForestMain)
좋다 좋다!
tuist graph로 모듈 구조를 한번 보자.
이렇게 생겼는데, 여기서 DoForestCommon을 DoForestDomain에 연결할 것이다.
ModuleType을 다음과 같이 수정해준다. 다른거 바꿀 필요 없이 이것만 바꾸면 적용이 된다!!public enum ModuleType: String, CaseIterable { case DoForestApp case DoForestCommon case DoForestCommonUI case DoForestData case DoForestDomain case DoForestEntity case DoForestMain case DoForestRoomDetail case DoForestSetting var dependencies: [TargetDependency] { switch self { case .DoForestApp: return [ .with(.DoForestMain), .with(.DoForestRoomDetail), .with(.DoForestSetting), .with(.DoForestData) ] case .DoForestCommon: return [ .with(.DoForestDomain) ] case .DoForestCommonUI: return [] case .DoForestData: return [ .with(.DoForestDomain) ] case .DoForestDomain: return [ .with(.DoForestEntity) ] case .DoForestEntity: return [] case .DoForestMain: return [ .with(.DoForestCommon), .with(.DoForestCommonUI) ] case .DoForestRoomDetail: return [ .with(.DoForestCommon), .with(.DoForestCommonUI) ] case .DoForestSetting: return [ .with(.DoForestCommon), .with(.DoForestCommonUI) ] } } }
이런 모듈 구조가 되었다. 굳굳!!
추가로, Workspace.swift도 수정해주자.
import ProjectDescription import ProjectDescriptionHelpers private let projects: [Path] = ModuleType.allCases.map { moduleType in "DoForest/\(moduleType.rawValue)" } let workspace = Workspace( name: "DoForest", projects: projects )
전체 코드:
반응형'iOS Dev' 카테고리의 다른 글
Apple Developer Academy @ POSTECH 3기 합격 후기 (1) 2023.07.20 마이크로피처 아키텍처란? (µFeatures Architecture) (1) 2023.07.15 [Tuist] Tuist를 활용하여 SwiftUI 클린 아키텍쳐를 적용한 모듈로 나눈 후 Github에 업로드 하기 1탄 (0) 2023.04.05 iOS 엔지니어링 '잘' 하기 (feat. 클린 아키텍쳐) (2) 2023.03.25 UITableViewCell에 Button을 올렸는데 터치 자체가 안될 때 (0) 2022.08.08