ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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
    )

    전체 코드:

    DoForest.zip
    0.50MB

    반응형

    댓글

Designed by Tistory.