본문 바로가기
IOS - Swift/Combine

[iOS/Swift] Combine - 시작하기 (Publisher ,Operator, Subscriber)

by 게게겍 2024. 4. 17.

컴바하아인

 

Introducing Combine - WWDC19 - Videos - Apple Developer

Combine is a unified declarative framework for processing values over time. Learn how it can simplify asynchronous code like networking,...

developer.apple.com

컴바인을 사용하는 이유

텍스트 필드에 입력된 값을 다음 vc에 전달하는 코드가 있다고 가정할때,

1. Delegate 패턴

Delegate 패턴은 객체 간의 커뮤니케이션을 위해 인터페이스를 정의하고, 하나의 객체가 다른 객체를 대신해서 작업을 수행하도록 합니다.

WelcomeViewController.swift

protocol WelcomeViewControllerDelegate: AnyObject {
    func didReceiveNickname(_ nickname: String)
}

class WelcomeViewController: UIViewController {
    weak var delegate: WelcomeViewControllerDelegate?

    func updateUIWithNickname() {
        delegate?.didReceiveNickname("Nickname")
    }
}

extension SomeOtherViewController: WelcomeViewControllerDelegate {
    func didReceiveNickname(_ nickname: String) {
        // UI 업데이트
        print("Received nickname: \\(nickname)")
    }
}

2. 콜백 함수

클로저로 콜백을 준 경우

WelcomeViewController.swift

class WelcomeViewController: UIViewController {
    var onReceiveNickname: ((String) -> Void)?

    func fetchNickname() {
        //닉네임 가져오기
        onReceiveNickname?("Nickname")
    }
}

// 다른 VC
let welcomeVC = WelcomeViewController()
welcomeVC.onReceiveNickname = { nickname in
    // UI 업데이트
    print("Received nickname: \\(nickname)")
}

3. Completion 클로저

Completion 클로저는 비동기 작업이 끝났을 때 실행되는 클로저로, 작업의 결과를 클로저를 통해 반환합니다. 이 방식은 비동기 작업이 완료된 후 필요한 동작을 수행할 때 주로 사용됩니다.

WelcomeViewController.swift

class WelcomeViewController: UIViewController {
    func fetchNickname(completion: @escaping (String) -> Void) {
        // 닉네임을 가져오는 작업
        completion("Nickname")
    }
}

// 사용 예
let welcomeVC = WelcomeViewController()
welcomeVC.fetchNickname { nickname in
    // UI 업데이트
    print("Received nickname: \\(nickname)")
}

4. Combine

WelcomeViewController.swift

import Combine

class WelcomeViewController: UIViewController {
    var nicknamePublisher = PassthroughSubject<String, Never>()

    func publishNickname() {
        nicknamePublisher.send("Nickname")
    }
}

// 다른 뷰 컨트롤러에서 구독
let welcomeVC = WelcomeViewController()
var cancellables = Set<AnyCancellable>()

welcomeVC.nicknamePublisher
    .sink(receiveValue: { nickname in
        // UI 업데이트
        print("Received nickname: \\(nickname)")
    })
    .store(in: &cancellables)

결론

  • Delegate 패턴: 유연성이 떨어질 수 있다.
  • 콜백 함수 및 Completion 클로저: 쉽지만 콜백지옥에 빠지면 곤란하다
  • Combine: 모던하고 쌈@뽕하다.

Combine Overview

Event Stream (Event pipeline)

 

 

Publisher가 정보를 만들어서 내뱉으면 Subscriber가 최종적으로 정보를 받는 형태입니다.

이때 Operator가 작업해서 값을 변경할수도 안할수도 있는 그런 역할을 합니다.

 

3가지 주요 컴포넌트

1대1로 비교하기에는 무리가 있겠지만 대충 이런느낌으로 저는 이해했어요

  • Publisher
    유튜버 - 컨텐츠 생산자
  • Subscriber
    구독자 - 사용자
  • Operator
    편집자 - 조정자

— — —

  • Subscription
    • 구독티켓? 구독중인 버튼? - 구독자만 이용할수 있는 티켓

Publisher

 

Declares that a type can transmit a sequence of values over time.

 

타입이 시간 경과에 따라 일련의 값을 전송할 수 있음을 선언합니다.

  • 데이터를 전송(배출)할수 있다.
    • output: 발행하는 값의 종류, Failure : 오류값
    • Subscriber 가 요청한것 만큼 데이터를 제공
    • Failure 타입을 Never로 지정하면 오류를 반환하지 않는것 (Just가 그 예시중 하나)
  • 빌트인 Publisher인 Just, Future 가 있음
    • Just 는 값을 다룬다 → 고정된 값을 비동기처리할때 사용
    • Future 는 함수값을 다룬다 → 시간좀 걸리는 비동기 작업처리할때 사용
  • 자동으로 제공해주는것들 (addOberser 등 메서드를 붙이지 않고 바로 publish를 붙혀서 사용할수 있는것)
    • NotificationCenter
    • Timer
    • URLSession.dataTask

Subscriber

퍼블리셔로부터 입력을 받을 수 있는 유형을 선언하는 프로토콜입니다.

 

정의된 값을 보면

  • Publisher 에게 데이터 요청하는 프로토콜
  • Input, Failure 타입이 정의가 필요합니다
    • input은 구독자가 받는 값의 종류를 뜻합니다.
    • 오류를 받지 않는 경우 Never를 사용함
  • Publisher 구독후( receive(subscription:) ), 갯수를 요청함
  • 파이프라인을 취소할 수 있음
  • 빌트인 Subscriber인 assign 과 sink 가 있음
    • assign 는 Publisher가 제공한 데이터를 특정 객체의 키패스에 할당
    → Failure 타입이 Never일 때 사용가능
    • sink 는 Publisher가 제공한 데이터를 받을수 있는 클로져를 제공함
    → sink는 새로운 값과 완료 이벤트를 처리하는 데 사용됨

두개의 주고받는 관계

  1. Subscriber가 Publisher에 붙는다 ( 값을 요청한다 )
  2. 값이 붙엇으면(구독이 성공하면) subscription 라는걸 준다
  3. subscription가 몇개의 데이터가 필요하다고 요청을 한다
  4. 이후 Publisher가 필요한 만큼 값을 준다

Publisher는 값을 계속 찍어내는데 Subscriber는 그게 필요하니 가서 달라고 요청하는것

Subscription

A protocol representing the connection of a subscriber to a publisher.

subscriber와 publisher의 연결을 나타내는 프로토콜입니다.

  • Subscriber 가 Publisher가 연결됨을 나타내는 녀석
    • 쉽게 생각하면, Publisher 가 발행한 구독 티켓
    • 이 구독 티켓만 있으면, 데이터를 받을수 있음
    • 이 구독 티켓이 사라지면 구독 관계도 사라짐
  • Cancellable protocol을 따르고 있음
    • 따라서, Subscription 을 통해 연결을 Cancel 할수 있다

Operator

  • Publisher 에게 받은 값을 가공해서 Subscriber 에게 제공
  • Input, Output, Failure type 을 받는데 타입이 다를수 있음
  • 빌트인 오퍼레이터가 개많이 있음

참고

https://developer.apple.com/videos/play/wwdc2019/722/

https://developer.apple.com/videos/play/wwdc2019/721/

https://developer.apple.com/documentation/combine/using-combine-for-your-app-s-asynchronous-code

https://developer.apple.com/documentation/combine/replacing-foundation-timers-with-timer-publishers

https://developer.apple.com/documentation/combine/processing-published-elements-with-subscribers

https://sujinnaljin.medium.com/combine-sink-assign-3dc04b7b326f

https://developer.apple.com/documentation/combine/receiving-and-handling-events-with-combine

https://fastcampus.co.kr/dev_online_iosbible

https://developer.apple.com/videos/play/wwdc2019/722/

https://leetaek.tistory.com/69

https://todayssky.tistory.com/17