SwiftUI CleanArchitecture

조영민·2022년 6월 19일
0

MVVM-C CleanArchitecture with RxSwift

로 진행한 프로젝트 https://github.com/July911/ChildOfWeather 를 Combine, SwiftUI 로 바꾸는 작업을 진행 중
비교해서 알아보자.

DataLayer SwiftUI

final actor DefaultCityListRepository: CityListRepository {
    
    let cityList: [City]
    
    init() {
        self.cityList = self.fetchCity()
    }
    
    nonisolated func fetchCityList() -> AnyPublisher<[City], Never> {
        return Just(cityList).eraseToAnyPublisher()
    }

액터로 DataRace를 막아줄 예정이다.
지난글의 bundle의 asset Data를 가져오는 방법을 활용.
정말 생활포스팅이다.

import Combine

extension URLSessionServiceProtocol {
   
   func requestCombine<T: APIRequest>(request: T) -> AnyPublisher<T.ResponseType, Error> {
           return URLSession.shared.dataTaskPublisher(for: request.urlRequest!)
               .requestJSON()
   }

강제언레핑.. 죄송 !
dataTaskPublisher 라는게 있군 ! 해결 !

ps) 네트워킹의 성능을 높게하는 방법이 WWDC 2022에 있던디
포스팅 예정

DataLayer RxSwift

extension URLSessionNetworkService {

    func requestRx<T: APIRequest>(request: T) -> Observable<T.ResponseType> {
        return Observable.create { emitter in
      ~~~~~~

            return Disposables.create {
                task?.cancel()
            }
        }
    }
}

요렇게 해주었다

DomainLayer

동일.

PresentationLayer with RxSwift

final class SearchViewModel {
    // MARK: - Property 
    private let coordinator: SearchViewCoordinator
    private let searchUseCase: CitySearchUseCase
    // MARK: - Initializer
    init(
        searchUseCase: CitySearchUseCase,
        coodinator: SearchViewCoordinator
    ) {
        self.searchUseCase = searchUseCase
        self.coordinator = coodinator
    }
   // MARK: - Nested Type
    struct Input {
        let viewWillAppear: Observable<Void>
        let didSelectedCell: Observable<City>
        let searchBarText: Observable<String?>
        let viewWillDismiss: Observable<Void>
    }

요롷게 Input&Output 을 nested로 정의해준 모습이다.
구독은 ViewController에서 해준다.
Transform메서드를 통해 Input을 받아 Output을 내보낸다.

PresentationLayer with SwiftUI

protocol CityListViewModelInput {
    func fetchWeather(cityName: String)
}

protocol CityListViewModelOutput {
    var cities: [City] { get }
}

final class CityListViewModel: ObservableObject, CityListViewModelInput, CityListViewModelOutput {
    
    @Published var cities: [City] = []
    private let cityListUseCase: CityListUseCase
    private var bag = Set<AnyCancellable>()
    private let coordinator: Coordinator 
    
    init(
        cityListUseCase: CityListUseCase,
        coordinator: Coordinator
    ) {
        self.cityListUseCase = cityListUseCase
        self.coordinator = coordinator
    }
    
    func fetchCityList() {
        self.cityListUseCase.fetchCities()
            .assign(to: \.cities, on: self)
            .store(in: &self.bag)
    }

Input & Output을 Protocol로 빼준 모습이다.
ObservableObject를 채택해준모습이다.
@published 로 View에 표현할 데이터를 표현해준다.
DisposeBag이 아니라 AnyCancellable이다.
구독은? 여긴 ViewModel이 해준다 !

struct CityListView: View {
   
   @ObservedObject var viewModel: CityListViewModel
   

뷰모델을 요롷게 받아온다.
이전 포스팅에서 @ 들의 차이를 알아보았다. 궁금하면 앞으로가서 보길 권장.

ps) 추후 https://medium.com/@BIT_OFIT/swiftui-mvvm-with-a-statemachine-46530b2903b9 적용 예정
정말 좋은 글같다. 꼭 보길
감사합니다 뉴스레터.

세줄요약)
1. 다른거 짱 많음
2. 1주일간 스유/컨바인만 봄
3. 뉴스레터 무한 감사.

0개의 댓글