티스토리 뷰

728x90
반응형

이 포스팅을 이해하려면 RxSwift vs Combine - 스펙 / 성능 / 개념 비교를 읽고 오세요 ☺️

 

Rxcocoa(for UIKit) + RxSwift 로 짰던 코드를 

-> UIKit + Combine

-> SwiftUI + Combine 

 

이렇게 두 가지 버전으로 바꿔보았습니다 :) 

 

우선 Model/ViewModel 쪽 코드는 큰 차이가 없었습니다 

APIManager 안에 있는 fetch 함수를 예로 봅시다 (isbn number를 받아서 책정보를 주는 함수입니다)

 

Observalbe을 AnyPulisher로 바꾸면 끝입니다 

 

 

 

그 다음 APIMananger의 fetch 함수를 부르는 ViewModel 쪽 코드를 봅시다

여기도 subscribe를 sink로 바꿔주면 끝입니다 

 

 

 

하지만 UIBinding 쪽 코드는 Rx를 빼고 Combine을 넣었을 때 그렇게 깔끔하지 않았습니다

두 가지 Case를 보면서 살펴봅시다 

 

Case 1 : SearchBar Text 글자씩 관찰하여 ViewModel search함수 호출한
             데이터를 받아 tableview reload
하기

 

[1] RxCocoa + RxSwift 

먼저 처음에 Rx로 개발한 코드를 봅시다

(간단 버전이므로 tableview나 disposebag 같은 변수들이 빠져있는 것들이 있으니 참고해서 보아주세용 👏)

 

 

bindSearchBar함수를 봅시다

searchbar.rx.text로 searchbar의 text를 관찰해서 viewModel의 search함수를 호출해줍니다 

 

bindTableview함수를 봅시다

viewmodel의 데이터 (publications)를 tableview에 바인딩 시켜줘서 데이터가 변하면 tableview가 reload 됩니다 

 

 

viewModel은 이렇게 생겼습니다 

 

 

 

 

[2] UIKit + Combine

 

이번엔 Rx를 빼고 combine을 넣어봅시다

(@Published 키워드가 붙은 것은 Publisher라고 생각하시면 됩니다)

 

searchbar와 searchText를 따로 선언해준 것을 볼 수 있구요, delegate 연결해서 searchText를 업데이트 시켜주는 코드가 들어가게 됩니다 

 

viewModel 쪽도 Publisher로 바꿔준 것 말고는 거의 변한 것이 없습니다 

 

 

 

[3] SwiftUI + Combine 

 

이번엔 UIKit 대신 SwiftUI를 쓰는 것 까지 해봅시다 

SwiftUI를 써보기 전 간단한 개념 조금만 알고 가봅시다 

 

1) onReceive

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {

    /// Adds an action to perform when the given publisher emits an event.
    ///
    /// - Parameters:
    ///   - publisher: The publisher to subscribe to.
    ///   - action: The action to perform when an event is emitted by
    ///     `publisher`.
    /// - Returns: A view that triggers `action` when `publisher` emits an
    ///   event.
    public func onReceive<P>(_ publisher: P, perform action: @escaping () -> Void) -> SubscriptionView<P, Self> where P : Publisher, P.Failure == Never
}

SwiftUI의 View들은 onReceive라는 것을 가지는데 onReceive의 Publisher에 넣어준 스트림의 이벤트가 발행될때마다 action이 실행됩니다  Rx의 view.rx 와 같다고 생각하면 됩니다 

 

2) @State 

 

@State를 붙이면 SwiftUI는 변화를 관찰하다가그 state를 사용하는 view의 body를 Automatically update시켜줍니다 

@State property wrappers are being managed by a CurrentValueSubject from Combine. (사람들의 추측)

 

3) @ObjectBinding

 

@State: Used for when changes occur locally to your SwiftUI view
@ObjectBinding: Binds your SwiftUI view to an external data source

 

State와 같은 개념인데 viewModel과 같이 해당뷰 안에 있지 않은 외부의 소스를 관찰할 때 이 키워드를 써줍니다 

 

 

그럼 이제 코드를 봐봅시다 

 

searchText를 @State 키워드를 붙여주었고 viewModel에 @objectBinding을 붙여주었습니다

searchText를 TextField와 바인딩해주었고 (swiftUI에는 searchbar가 없어서 textfield로 해주었습니다)

textfield 에 한 글자 씩 들어올때마다 onReceive의 액션(viewModel의 search호출)이 실행됩니다 

 

 

viewModel은 이렇게 생겼는데요

viewModel의 데이터가 업데이트될 때마다 didchange.send를 하게 되는데 이 viewModel를 가지고 있는 view의 body를 업데이트 시켜줘~ 하는 것 입니다 

 

그래서 데이터(publications)가 바뀔 때 view의 List가 자동으로 업데이트 되게 됩니다 

 

 

Case 2: 5개의 textfield 관찰하여 모두 empty 아닌 순간 Confirm Button 누를 있게 하기

 

 

 

[1] RxCocoa + RxSwift  

 

기존에 Rx로 짰던 코드를 먼저 봅시다 

 

textfield.rx.text 해서 만든 observable 다섯개를 연산해주는 코드가 나옵니다 

 

[2] UIKit + Combine 

 

그 다음 Rx를 빼고 Combine을 넣어봅시다 

 

 

textfield와 textfield의 text가 empty인지 아닌지를 나타내는 불리언 값 선언을 따로 해줘야합니다 (저는 이 두 개가 분리되는게 별로라고 생각해요)

그리고 textfield가 바뀌는 것을 text에 넣어주는 코드가 들어가게 됩니다 

 

[3] SwiftUI + Combine

 

SwiftUI로 까지 바꾸어봅시다 

 

관찰할 텍스트를 textfield에 바인딩해주었습니다

isConfirmValid가 바뀔 때마다 버튼 enable여부가 바뀝니다 

 

코드가 간단하고 깔끔하네요 :-) 

 

 

느낀 점 

  • Rx와 Combine은 거의 같습니다. 하지만 조금씩 다른 부분이 있어서 완전히 같다고 생각하고 쓰면 예상치 못한 문제를 만날 수 있을 것만 같아요....!  
    ex. BehaviorSubject와 CurrentValueSubject
    ex. subscribeOn

  • iOS 13이상이라면 RxSwift를 빼고 Combine을 쓸 수는 있습니다 
    하지만 RxSwfit를 Rxcocoa와 함께 쓰는 것 처럼 Combine은 SwiftUI와 함께 쓰는 것이 코드가 깔끔합니다 

  • 덧붙여서 Combine은 SwiftUI와 함께 써야 Apple이 Combine을 만든 의도를 살릴 수 있습니다 예를 들어 애플에서 viewModel을 BindableObject 프로토콜 따르게 만들라고 가이드를 해줬는데 (WWDC 영상에서) BindableObject는 SwifUI 소속이랍니다 

 

반응형
댓글