본문 바로가기

TIL

[TID] #10 - Tuist로 DemoApp 설정하기, Globs Pattern으로 SourceFile 관리하기

오랜만에 올리는 글이라 새로 알게 되어 올리고 싶은 내용이 많지만, 지금 당장 잘 써질 것 같은 주제는 Tuist를 이용한 DemoApp 설정인 것 같다.

 

얼마 전에 진행한 sync-swift에 좋은 발표들이 많았고, 요즘 관심이 있는 modular architecture에 대한 내용도 꽤 있었다. 그 중에서 KangHoon님의 발표 내용이 도움이 많이 되었고, DemoApp을 구현해보고 싶다는 생각이 들었다.

https://github.com/OhKanghoon/sync-swift-2022

 

일단은 이전에 봐둔 소스코드가 있어서, Project를 extension하여 bool parameter를 통해 demoApp이 필요하면 target을 추가해주는 식으로 진행하려 했다. 아래 코드가 그 뼈대이다.

public extension Project {
    static func makeModule(
        name: String,
        platform: Platform = .iOS,
        product: Product,
        packages: [Package] = [],
        dependencies: [TargetDependency] = [],
        sources: SourceFilesList = ["Sources/**"],
        resources: ResourceFileElements? = nil,
        infoPlist: InfoPlist = .default,
        resourceSynthesizers: [ResourceSynthesizer] = .default,
        hasTest: Bool = false,
        hasDemoApp: Bool = false
    ) -> Project {
        return project(
            name: name,
            platform: platform,
            product: product,
            packages: packages,
            dependencies: dependencies,
            sources: sources,
            resources: resources,
            infoPlist: infoPlist,
            resourceSynthesizers: resourceSynthesizers,
            hasTest: hasTest,
            hasDemoApp: hasDemoApp
        )
    }
}

첫 번째 시도 : main App에 demoapp 생성하기

단순하게 생각해서 main App에서 hasDemoApp을 true로 처리하여 demo app을 생성하게 하려고 했다. 아무 생각없이 코드를 짰더니 app은 app에 의존할 수 없다는 에러가 떴다. 충분히 생각해봤으면 미리 예상할 수 있었을 결과인데 또 손부터 움직여버렸다..

두 번째 시도 : DSKit Module에 demo app 생성하기

DSKit은 static framework라 문제가 없을 줄 알았는데, 이번에는 resourceSynthesizers가 자동으로 생성해주는 각종 열거형들을 참조하고 있던 Source File들에서 빌드 에러가 났다. DSKit에 데모 앱을 생성하면, 이 앱은 DSKit의 Source에는 의존성을 가지지만, resourceSynthesizers가 만들어준 열거형에는 접근이 불가능했다.

 

따라서 참조하는 Source 파일에 resourceSynthesizers가 만들어주는 Derived 경로를 추가해줬는데, 이 방법 또한 invalid redeclaration 에러로 인해 막혔다. module을 return하는 메서드가 동일하게 생성되어 일어난 결과였다.

 

최종적으로 아래와 같이 해결했다.

 

let demoAppTarget: Target = {
    let demoSource: SourceFilesList = ["Demo/Sources/**"]
    let demoGlobs: [SourceFileGlob] = [.glob("Sources/**", excluding: ["Sources/UIFont+.swift"])]
    let demoSources: SourceFilesList = SourceFilesList(globs: demoGlobs + demoSource.globs)
    let demoResources: ResourceFileElements = ["Demo/Resources/**", "Resources/**"]
    return makeDemoAppTarget(name: name, sources: demoSources, resources: demoResources)
}()

let schemes: [Scheme] = hasDemoApp
? [.makeScheme(target: .debug, name: name), .makeDemoScheme(target: .debug, name: name)]
: [.makeScheme(target: .debug, name: name)]

var targets: [Target]
switch (hasTest, hasDemoApp) {
case (true, true):
    targets = [appTarget, makeTestTarget(name: name, isDemo: true), demoAppTarget]
case (true, false):
    targets = [appTarget, makeTestTarget(name: name)]
case (false, true):
    targets = [appTarget, demoAppTarget]
case (false, false):
    targets = [appTarget]
}

 

위 코드를 짜면서 두 가지 수확이 있었다.

1. 지금까지 알게모르게 공식 문서의 친절한 설명에 의존하는 경향이 있었다. 구글과 공식 문서에 나와 있지 않은 문제를 스스로 해결하기 위해서, tuist repository의 구현부를 공부하여 내가 생각한 방법이 가능한 방법인지 고민하는 시간을 가졌다. 오픈 소스도 내 코드 들여다보듯이 깊숙히 보자!

 

2. globs patterns에 대해 알게 되었다. 파라미터에 자꾸 globs globs 하길래 이게 뭐지? 했는데 https://facelessuser.github.io/wcmatch/glob/

아주 유명한 녀석이었다... 역시 CS나 배경지식의 크기가 내 응용력과 상관관계를 가지는 것 같다.. 열심히 공부하자!

얼마 전에 와일드카드를 통해서 경로를 지정하는 이러한 패턴에 대해 공부하고 싶었는데, 이를 뭐라 칭하는지도 모르고 어떻게 검색해야 할지도 몰라서 헤매고 있었다. 그런데 뜻밖의 경로에서 glob pattern에 대해 알게 되어서... 여튼 기분이 좋다!

 

마지막으로!

 

첫 번째 시도에서 demoApp 생성하기를 실패했다. 당시에는 이를 해결할 방법이 없을 것 같아서 다음 방법을 시도했는데, app에 대해 의존성을 가지는 것이 아니라 app이 가지는 source, dependency만 그대로 가져오면 될 일이었다. 다음에 그렇게 구현해보겠다!

 

PR 요약

https://github.com/sopt-makers/SOPT-Stamp-iOS/pull/16

 

[Feat] #15 - Tuist로 DemoApp 생성 by L-j-h-c · Pull Request #16 · sopt-makers/SOPT-Stamp-iOS

🌴 PR 요약 🌱 작업한 브랜치 feature/#15 🌱 PR Point 1. 총 2가지 DemoApp 생성 : SOPT-Stamp-Demo / DSKitDemoApp 아래와 같이 스킴에서 선택하여 빌드할 수 있습니다. 2. 두가지 Demo App 구현 방식 위의 그래프를

github.com

여튼 그래서 팀원들에게 공부하고 구현한 내용을 공유했다. 함께 논의해보고 싶은 사항은 demo App의 종류, 의존성 구조의 재정립이다. 강훈님의 발표자료를 보고 구조 개선의 필요성을 느꼈기에... 쉬운 일은 아니겠지만 얼른 리팩토링 하고 싶다.

 

일단 다음 할 일로 xcconfig을 이용해 build settings을 다루기!가 남아있기에 차근차근 해 나갈 예정이다.