본문 바로가기

Swift 공부

Tuist , Swinject , Clean Architecture , MVVM - Coordinator

Tuist가 뭔지 너무궁금해!! 로 시작해서 민소네님의 블로그를 보고 Swinject도 너무 궁금해!! 하다보니 

모듈화를 해보고싶어!! 가 됬고 어떻게 계층을 나누고 모듈화를 해야하나 싶어서 일단 Clean Architecture를 이용해서 Domain과 네트워크 영역을 나눠보자!! 로 시작된 내 프로젝트...

 

시작한지는 생각보다 꽤 시간이 지났는데 평일에는 기초공부 , 주말에 지금 작성하고있는 프로젝트를 만들어야지로 시작했는데

이직기간과 맞물리다보니 평일에 기초공부와 과제 , 주말엔 면접 준비 및 개발과제 하다보니 조금씩 조금씩 미뤄줘서 이제야 완성해서 글을 쓰게 된다

 

해당 프로젝트를 만들면서 기존의 MVVM - Coordinator패턴으로 이뤄졌던 프로젝트에 Tuist , Swinject , Clean Architecture 를 추가하는 작업을 진행하였다

 

Tuist

 

우선 가장 먼저 시작할 수 있는 Tuist부터 시작을 했는데 이게 시작부터 나에게 큰 고통을 주었다 가장 큰 문제는 정보의 부족 ㅜㅜ

Tuist를 기존에 사용했던 블로그 자료를 보면 1.x or 2.x 을 이용하여 진행을 하였는데 내가 받은 버전은 3.x 버전 특히 라이브러리 쪽에서 deplecated 된다는 함수들이 많아 힘들었고 각각의 문법들이 조금씩 달라져서 그 부분들을 Doc에서 찾아 사용하는게 어려웠다 그리고

dependencies 를 만들어 필요한 라이브러리들을 넣어서 사용해야하는데 이 부분도 넣는거 까지는 어찌저찌 했는데 각 프로젝트에 배치를 하는 방법도 어려워서 이 부분도 상당히 많은 삽집들이 이뤄졌다 

 

import ProjectDescription

let dependencies = Dependencies(
    
    swiftPackageManager: .init([
        .remote(url: "https://github.com/ReactiveX/RxSwift.git", requirement: .upToNextMajor(from: "6.5.0")),
        .remote(url: "https://github.com/RxSwiftCommunity/RxDataSources.git", requirement: .upToNextMajor(from: "5.0.0")),
        .remote(url: "https://github.com/RxSwiftCommunity/RxGesture.git", requirement: .upToNextMajor(from: "4.0.3")),
        .remote(url: "https://github.com/onevcat/Kingfisher.git", requirement: .upToNextMajor(from: "7.2.0")),
        .remote(url: "https://github.com/Moya/Moya.git", requirement: .upToNextMajor(from: "15.0.0")),
        .remote(url: "https://github.com/SnapKit/SnapKit.git", requirement: .upToNextMajor(from: "5.0.1")),
        .remote(url : "https://github.com/devxoul/Then", requirement: .upToNextMajor(from: "2.0.0")),
        .remote(url : "https://github.com/Swinject/Swinject.git" ,  requirement: .upToNextMajor(from: "2.8.0")),
        //        .remote(url: "https://github.com/Alamofire/Alamofire.git", requirement: .upToNextMajor(from: "5.6.0")),
        .remote(url: "https://github.com/RxSwiftCommunity/RxAlamofire.git", requirement: .upToNextMajor(from: "6.1.0")),
    ], baseSettings: .settings(
        configurations: [
            .debug(name: "Debug"),
            .release(name: "Release")
        ])
    ),
    
    platforms: [.iOS]
)

 

let project = Project(
    name: "TuistTest",
    organizationName: "JYKang",
    settings: .settings(configurations: [
        .debug(name: "Debug"),
        .release(name: "Release")
    ]),
    targets: [
        Target(
            name: "TuistApp",
            platform: .iOS,
            product: .app,
            bundleId: "com.jyk.TuistApp",
            deploymentTarget : .iOS(targetVersion: "13.0.0", devices: .iphone),
            infoPlist: .extendingDefault(with: infoPlist),
            sources: ["Targets/TuistApp/Sources/**"],
            resources: ["Targets/TuistApp/Resources/**"],
            dependencies: [
                .external(name: "RxSwift"),
                .external(name: "RxDataSources"),
                .external(name: "RxGesture"),
                .external(name: "Kingfisher"),
                .external(name: "SnapKit"),
                .external(name: "Then"),
                .external(name: "Swinject"),
                .target(name: "NetworkPlatform")
            ]
        ),

 

이런식으로 사용되고있다

 

let infoPlist: [String: InfoPlist.Value] = [
    "CFBundleShortVersionString": "1.0",
    "CFBundleVersion": "1",
    "UILaunchStoryboardName": "LaunchScreen",
    "NSAppTransportSecurity" : ["NSAllowsArbitraryLoads":true],
    "UISupportedInterfaceOrientations" : ["UIInterfaceOrientationPortrait"],
    "UIUserInterfaceStyle":"Light",
    "UIApplicationSceneManifest" : ["UIApplicationSupportsMultipleScenes":true,
                                    "UISceneConfigurations":[
                                        "UIWindowSceneSessionRoleApplication":[[
                                            "UISceneConfigurationName":"Default Configuration",
                                            "UISceneDelegateClassName":"$(PRODUCT_MODULE_NAME).SceneDelegate"
                                        ]]
                                    ]
                                   ]

UIApplicationSceneManifest 이 부분이 Scenedelegate를 사용하는 설정값이다.

 

그리고 Tuist로 Scenedelegate를 이용하는 프로젝트를 만들려고 하는데 계속 뭔가 Info.plist의 설정값이 하나씩 안맞아서 

Scenedelegate로 시작이 안되고 ㅜㅜ 진짜 상당히 애먹으면서 Tuist를 사용했다. 이렇게 삽질을 계속하면서도 어캐 꾸역꾸역 내가 원하는대로 만들어지는걸 보면 뭐 다른설정값들도 삽질하면 할 수 있겠네! 라는 생각이 들었다.

 

추가로 더 해보고싶은것 :

infoPlist의 버전정보를 자동으로 올려주도록 만드는 코드와 , configuration을 Debug , Releaser가 아닌 내가 만든 configuration으로 만드는 것 

 

추가로 공부해야할것 :

staticLibrary , dynamicLibrary의 차이점

 

 

Clean Architecture 

클린아키텍처를 공부할 때 가장 많은 도움을 주었던 자료다

https://github.com/sergdort/CleanArchitectureRxSwift

 

GitHub - sergdort/CleanArchitectureRxSwift: Example of Clean Architecture of iOS app using RxSwift

Example of Clean Architecture of iOS app using RxSwift - GitHub - sergdort/CleanArchitectureRxSwift: Example of Clean Architecture of iOS app using RxSwift

github.com

https://github.com/kudoleh/iOS-Clean-Architecture-MVVM

 

GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoor

Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoordinator, DTO, Response Caching and one of the views in SwiftUI - GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Tem...

github.com

 

클린아키텍처의 이론적인 부분은 다른 블로그글들을 보면서 공부를... 진행부탁드리겠습니다...

 

기존의 프로젝트를 CleanArchirecture로 변경을 하는데 제일 어려웠던부분은 UseCase를 어떤식으로 사용하냐? 였다

데이터를 받는용도로만 쓰기에는 뭔가 아닌거 같고 공부를 해보면 클라이언트가 그 시스템을 통해 하고자 하는 것 정의 한것을 UseCase 라고하는데 여기서 시스템을 통해 하고자 하는 것의 범주가 어느정도인지를 정하기가 상당히 어려웠다 

예를 들어 인피니티 스크롤 페이지를 만드는데 데이터를 요청하는 부분을 Usecase로 만들것인지 아니면 데이터를 요청하고 인피니티 스크롤에 필요한 값들을 셋팅을 해주는 것 까지 Usecase로 엮어야 하는건지에 대한 고민이 상당히 많았다 Usecase가 명확해야 그 이외의 값들을 바꾸기에도 불편함이 없고 직접적으로 유저와 상호작용을 하는 로직이기에 이 부분을 고민하는게 상당히 힘들었다 (아직도 어떻게 써야하는지 잘 모르겠다 사이드프로젝트로 진행하다보니 많은 기능이 필요가 없고 단순한 기능만을 하기에 좀 더 큰 규모의 프로젝트에서 사용해볼 예정이기에 그때 새롭게 고민한것들을 다시한번 적어보겠습니다.)

 

클린아키텍처로 바꾸기위해 폴더의 구조를 변경하고 , Domain 영역 , Platform 영역을 변경하고 ViewModel 이 직접적으로 Network를 받고 있던것을 UseCase를 사용하는 것으로 변경하고 거기에 따라 Coordinator에서 UseCase를 주입해주는 방식으로 하나씩 바꾸다 보니 상당히 많은 코드의 수정이 이뤄지고 많은 시간들이 소모 되고 Swinject를 사용하는 부분까지 변경하다보니 많은 코드의 변화가 필요하고 의존성을 주입해주던 부분들을 변경해주고 하다보니 힘들었다 

 

그리고 처음부터 Domain , NetworkPlatform을 라이브러리화 시켜 만들어 진행하다보니 open ,  public , internal , fileprivate , private 의 이해 그리고 성능의 차이 등을 공부하고 final 키워드 등을 공부하다보니 상당히 많은 시간이 걸리게 되었다 (덕분에 배운거 많고 공부한게 진짜 많다) 

 

Swinject

swift 에서 의존성 주입을 해주기위해 사용하는 라이브러리 , 해당 라이브러리를 사용하면 의존성관계를 눈으로 보기 편하고 클래스간의 유연한 의존성을 도움을 준다고 하기에 사용을 해봤는데 이 부분은 사용하면서 특정 모듈들을 불러오기에 편하고 키워드로 Mock을 구분해서 가져올수있는 편안함도 있었지만 아직까지 이해도가 부족한것인지 그렇게 잘썼다? 라고 생각이 들지 않아 이 라이브러리는 추후에 Swinject를 사용하는 회사에 들어가기전 예습으로 코드를 써봤다? 정도의 느낌만 가져가고 추후에 실프로젝트에 사용하는 팀을 들어갔을때 좀더 자세히 공부해볼 예정이다.

MVVM - Coordinator

 

Presenter의 기본 구성이고 가장 보편적으로 많이 사용되는 구조이다.

 

 

해당 프로젝트의 깃허브 주소이다



혼자 열심히 찾아가며 하나씩 공부하면서 만든 프로젝트다 보니 상당히 부족한점이 많아 공유하기 부끄럽지만 프로젝트를 공유한다

 

혹시라도 이 프로젝트를 보면서 도움이됬거나 좀 더 좋은 구조로 변경할 수 있는 아이디어를 주신분들은 언제든 말씀해주시면 새롭게 공부해서

 

변경을 해볼 의지가있다..! 그리고 도움이 되신분들은 저의 첫 Star가 되어주세요...!

 

https://github.com/gnejfejf2/TuistTestApp

 

GitHub - gnejfejf2/TuistTestApp

Contribute to gnejfejf2/TuistTestApp development by creating an account on GitHub.

github.com

 

'Swift 공부' 카테고리의 다른 글

내부 DB 종류와 간단한 정리  (0) 2024.06.23
ReactorKit Pulse  (2) 2022.07.11
Hashable , Equatable , Identifiable  (0) 2022.04.18
Tuist 3.x 라이브러리 등록 방법  (0) 2022.04.17
Tuist 모듈화 공부 4편 클린아키텍처  (0) 2022.04.15