[Combine] Combine 공식문서 살펴보기 +

봄바야·2022년 2월 18일
0

Combine

어떻게, 어디서 쓰이는지 간단하게 예시를 위주로 작성했습니다.

Combine
비동기 이벤트들을 커스텀하게 핸들링할 수 있게 하는 것.

Publishers

protocol Publisher

let provider = (1...10).publisher
  • publisher를 subsribe하는데는 sink() & assign(to:on)
    • sink : 결과값과 completion을 받도록 클로저를 구현할 수 있도록 제공 (like Rx의 subscribe)
      • sink란 클로저 기반의 동작이 있는 subscriber를 절대 실패하지 않게 하는 publisher와 연결하는 것. 그렇기 때문에 에러 타입은 Never 일때만 가능
    • assign : 커스텀 코드를 쓰지 않으며 output을 데이터 모델이나 UI control로 바로 바인딩함 (like Rx의 bind)
      • 에러가 생길 수 있으면 호출을 못 함. 그래서 replaceError(with:) 등을 위에 명시해줘서 에러를 처리하고 사용함.

enum Publishers

  • Publisher protocol Most of these types exist as extensions on the Publishers enumeration. For example, the map(:) operator returns an instance of Publishers.Map.
    : Publisher protocol은 대부분 Publishers enumeration의 확장형, map(
    :) 연산자는 Publishers.Map의 인스턴스를 반환함

struct AnyPublisher

AnyPublisher<String, Error> 
  • Publisher 프로토콜을 준수하는 구조체
  • send(_:) operator를 가지지 않기 때문에 publisher에 새로운 값을 추가하는 것이 불가능함

struct Published

@published var temperature: Double
  • Property Wrapper를 이용해 퍼블리셔로 간결하게 사용

protocol Cancellable

private let cancellable: Cancellable

class AnyCancellable

var bag = Set<AnyCancellable>() 
let subject = PassthroughSubject<Int, Never>() 
subject 
	.sink(receiveValue: { value in // receiveValue : 값을 받을 때 실행하는 클로저
   		print(value) 
    }) 
    .store(in: &bag)
  • Cancellable을 준수하는 type-erased 클래스로, 호출자가 subscription에 접근하지 않고 subscription을 취소하여 더 많은 항목을 요청하는 등의 작업을 수행할 수 있음
  • 취소 되었을 때, 제공된 closure를 실행하는 type-erasing cancellable object라고 할 수 있음

Convenience Publishers

class Future

let future = Future<String, Error> { promise in
    promise(.success("BOMS2"))
}
  • 단일 이벤트와 종료 or 에러를 제공하는 publisher
  • Error 없이 사용하려면 Future<String, Never>

struct Just

let just = Just<String>("BOMS2")
  • 단일 이벤트 발생 후 종료되는 publisher

struct Deferred

class Ex: Publisher {
    typealias Output = String
    typealias Failure = Never
}

let deferred = Deferred<Ex>{ () -> Ex in
    return Ex()
}
  • 구독할 때 publisher가 만들어지는 publisher

struct Empty

let empty = Empty<String, Never>()
  • 이벤트 없이 종료되는 publisher

struct Fail

let failed = Fail<String, Error>(error: NSError(domain: "error", code: -1, userInfo: nil))
  • failure(Error Domain=error Code=-1 "(null)") 이런식으로 에러와 함께 종료되는 publisher

struct Record

let record = Record<String, Error> { recoding in
    print(recoding)
    recoding.receive("BOM")
    recoding.receive("S2")
    recoding.receive(completion: .finished)
}
  • 기록해뒀다가 다른 subscriber에서 사용할 수 있는 publisher

Connectable Publishers

protocol ConnectablePublisher

let provider = (1...10).publisher.makeConnectable()

Subscribers

protocol Subscriber

let subscriber = CustomSubscrbier()
publisher.subscribe(subscriber)

enum Subscribers

struct AnySubscriber

let passThroughSubject = PassThroughSubject<Int, Never>()
let anySubscriber = AnySubscriber(passThroughSubject)

protocol Subscription

class SomeSubscriber: Subscriber {
... 생략 ...
  func receive(subscription: Subscription) {
    print("구독을 시작합니다.")
    subscription.request(.unlimited)
  }
... 생략 ...
}

enum Subscriptions

Subjects

protocol Subject

public protocol Subject : AnyObject, Publisher {
  func send(_ value: Self.Output)
  func send(completion: Subscribers.Completion<Self.Failure>)
  func send(subscription: Subscription)
}
  • Subject
    • @Published 와는 다르게 Protocol 선언에 사용이 가능하다. (Property wrapper는 프로토콜 안에서 사용이 불가능하다)
    • @Published 와는 다르게 error type 사용이 가능하다.

class CurrentValueSubject

let subject = CurretValueSubject<Int, Never>(1)
  • 초기값이 있다.
  • 구독이 시작되는 시점에 초기값이 게시가 되기 때문에 연결이 되자마자 값이 구독자에게 전달.
  • single value를 래핑하고 값이 변경될 때마다 새 element를 publish하는 subject.
    PassthroughSubject와 달리 CurrentValueSubject는 가장 최근에 publish된 element의 버퍼를 유지.
  • CurrentValueSubject에서 send를 호출하면, 현재 값도 업데이트 되므로, 값을 직접 업데이트 하는 것과 같음.

class PassthroughSubject

let subject = PassthroughSubject<String, Never>()
  • 초기값이 없다.
  • 구독이 시작되고, 그 후에 값이 게시가 되었을 때 구독자에게 값이 전달된다
  • subscribers가 없거나 현재 demand가 0이면 value를 삭제(drop)함

Schedulers

protocol Scheduler

  [1,2,3,4,5,6,7,8,9,10].publisher
      .subscribe(on:DispatchQueue.main) // 전체 Publisher의 기본 동작을 메인 스레드에서 진행한다.(메인 스레드에서 동작)
      .delay(for: 2, scheduler: DispatchQueue.global()) // 백그라운드에서 2초간 delay를 준다. 이때 스레드도 바뀐다.(백그라운드 스레드에서 동작)
      .map({"\($0)"}) // Int를 String으로 바꿔준다.(백그라운드 스레드에서 동작)
      .receive(on: DispatchQueue.main) // 이후 동작은 메인 스레드에서 진행한다.
      .sink(receiveValue: { s in 
          self.label.text = s // 값을 받아서, label에 세팅한다(메인 스레드에서 동작)
      }).store(in: &self.cancelBag)
  • receive(on: ) : publisher로 부터 element를 수신할 scheduler를 지정하는 역할. (downstream의 실행 컨텍스트를 변경, subscribe(on:)은 upstream에 영향.)
  • DispatchQueue, OperationQueue, RunLoop, ImmediateScheduler가 다 Scheduler를 채택하고 있어서 전부 사용 가능

struct ImmediateScheduler

  • 즉시 실행

protocol SchedulerTimeIntervalConvertible

Observable Objects

protocol ObservableObject

class ViewModel: ObservableObject {  
  @Published var models: [Model] = [] 
  init(count: Int) { 
    for _ in 0..<count { 
    	models.append(Model(id: Int.random(in: 0...100))) 
    } 
  } 
}

class ObservableObjectPublisher


RxSwift vs Combine

  • RxSwift로 작성한 코드와 Combine으로 작성한 코드 비교
    • Combine의 assign(to:)은 Rx의 bind(to:)
    • Combine의 sink는 Rx의 subscribe와 비슷한 개념
    • bind와 subscribe 차이 :
      • bind의 경우 error가 발생했을때 처리하는 구문이 없이 에러가 발생하고,
        subscribe를 사용할 경우 onError인자를 통해 에러가 발생했을 때 예외처리를 할 수 있다.
// Rx
let disposeBag = DisposeBag()

Observable.just(1)
	.subscribe(onNext: { data in
    	print(data)
	})
	.disposed(by: disposeBag)
// Combine
let cancellables = Set<AnyCancellable>()

Just(1)
	.sink { data in
		print(data)
	}
	.store(in: &cancellables)

Change Thread

// RxSwift
ObserveOn = It works only downstream
SubscribeOn = It works upstream & downstream

// Combine
receiveOn(on: ) = It works only downstream
Subscribe(on:) = It works only upstream


debounce와 throttle의 차이


0개의 댓글