[Combine] 01_Hello, Combine!
iOS/SwiftUI

[Combine] 01_Hello, Combine!

728x90

Combine은 앱이 이벤트를 처리하는 방법에 대해 선언적 접근 방식을 제공한다.
여러 delegate callback 또는 completion handler closure를 구현하는 대신 주어진 이벤트 소스에 대해 single processing chain을 작성할 수 있다.
chain의 각 부분은 이전 단계에서 받은 요소에 대해 어떠한 조치를 취하는 결합연산자들을 의미한다.

비동기 프로그래밍

동기식 코드

begin
   var name = "Bo-Young"
   print(name)
   name += " Park"
   print(name)
end

항상 같은 결과 반환

비동기식 코드

--- Thread 1 ---
begin
  var name = "Tom"
  print(name)

--- Thread 2 ---
name = "Billy Bob"

--- Thread 1 ---
  name += " Harding"
  print(name)
end

프로그램을 실행할 때마다 다른 결과가 나타날 수 있음

→ 변경 가능한 상태 관리는 필수적

Foundation, UIKit/AppKit

기존의 비동기 프로그래밍 방식

  • NotificationCenter: 사용자가 기기 방향을 변경하거나, 소프트웨어 키보드가 화면에 표시/숨김 처리될 때와 같이 특정 이벤트가 발생할 때마다 코드를 실행합니다.
  • Delegate Patten: 다른 object를 대신하여 또는 함께 작동하는 object를 정의할 수 있습니다. 예를 들어 AppDelegate 에 새로운 remote notification이 도착하면 어떻게 작동할 것인지 정의할 수 있지만 이 코드가 언제 실행될지, 몇 번 실행될지는 전혀 알 수 없습니다.
  • Grand Central Dispatch, Operations: 수행할 작업을 추상화하는데 도움이 됩니다. 이를 사용하여 코드가 순차적으로 실행되도록 예약하거나 우선 순위가 다른 여러 대기열에서 동시에 여러 작업을 실행하도록 할 수 있습니다.
  • Closures: 전달가능한 코드 뭉치를 만들어서 다른 object가 해당 코드를 실행할지 여부, 횟수 및 컨텍스트를 결정할 수 있습니다.

💡 전체 앱 코드가 실행될 순서를 가정하는 것은 사실상 불가능
     비동기 코드와 리소스 공유는 재현이나 추적이 어렵고, 궁극적으로 수정하기도 어려운 문제를 일으킬 수 있습니다.

 

Combine

혼란스러운 비동기 프로그래밍 세계를 질서정연하게 정리하는데 도움이 되는 새로운 언어를 Swift 생태계에 도입하는 프레임워크

Foundation에서 SwiftUI에 이르기까지 다양한 시스템 framework는 Combine에 의존하며 "전통적인" API의 대안으로 Combine 으로의 통합을 제공합니다.

이미 만들어져있는 API를 제거하고 대체하는 것을 목표로 하지 않고, Combine 으로 통합되어 서로 비동기식으로 커뮤니케이션하려는 앱의 모든 유형이 Combine을 사용할 수 있도록 합니다.

Combine의 근간

선언형, 반응형 프로그래밍

Combine은 Rx와 다르지만 Reactive Stream과 유사한 표준을 구현합니다.

Reactive Stream은 Rx와 몇 가지 주요한 차이점이 있지만 둘 다 대부분의 핵심 개념이 유사합니다.

Combine 기초

Publishers

  • Subscribers와 같이 하나 이상의 대상에게 시간이 지남에 따라 값을 방출할 수 있는 유형입니다.
  • 여러 이벤트들을 다음 세 가지 유형으로 생성됩니다.
    1. Publishers의 제네릭 유형인 Output
    2. 성공적인 완료
    3. error와 함께 완료. Publishers의 Failure
  • 0개 이상의 output 값을 가질 수 있으며, 성공 또는 실패로 인해 완료되면 더 이상 이벤트를 생성하지 않습니다.

Int 값을 방출하는 Publishers를 타임 라인에 시각화한 것입니다.

  • Combine의 Publishers를 사용하여 앱의 모든 작업을 처리할 수 있습니다.
    • 델리게이트를 추가하거나 completion callback을 주입하는 대신 Publishers를 사용할 수 있습니다.
  • error 처리 기능이 내장되어 있습니다.
  • Publishers protocol은 두 가지 유형을 가집니다.
    • Publisher.Output은 publisher의 방출 값입니다. 만약 publisher가 Int에 특화되었다면 String 이나 Date 타입을 방출할 수는 없습니다.
    • Publisher.Failure는 publisher가 뱉을 수 있는 error 타입입니다. 만약 publisher가 절대 실패하지 않는다면, 이를 Never 라는 실패 타입으로 표현할 수 있습니다.
    → 주어진 publisher를 구독할 때 어떤 값이 떨어질지, 실패한다면 어떤 에러가 떨어질지 예상 가능합니다.

Operators

  • Publisher protocol로 선언된 메서드로, 선언된 Publishers와 동일하거나 새로운 Publishers로 반환합니다.
  • 여러 Operator를 차례로 호출해서 체인을 연결할 수 있기 때문에 매우 유용합니다.
    • 이들은 매우 독립적으로 구성가능하기 때문에 하나의 구독 사이에서 아주 복잡한 로직을 구현하도록 서로 결합될 수 있습니다.
  • 항상 Input 및 Output을 가지고 있으며 일반적으로는 이를 upstream, downstream이라고 합니다.
    • Output이 다음 Input 유형과 일치하지 않으면 결합할 수 없습니다.
    • 이를 통해 공유 상태를 피할 수 있습니다.

Subscribers

  • 모든 구독은 subscriber와 함께 종료됩니다.
  • 방출된 값이나 완료 이벤트를 처리하는 역할을 합니다.

  • 현재 Combine은 두 가지 내장 Subscribers를 제공하고 있습니다.
    • sink : subscriber는 output 값과 완료를 수신할 수 있는 closure를 제공합니다.
    • assign : subscriber는 개발자가 별도의 코드를 작성하지 않아도 결과 값을 데이터 모델이나 UI 컨트롤의 특정 속성에 바인딩하여 데이터를 표시할 수 있도록 해줍니다.
  • 데이터에 특정 요구 사항이 있는 경우, Subscribers를 커스터마이즈하는 것이 Publishers를 커스터마이즈하는 것보다 쉽습니다.
    • Combine은 커스터마이즈된 도구를 사용할 수 있도록 지원합니다.

Subscriptions

  • Combine의 Subscription은 객체, 퍼블리셔, 오퍼레이터, 서브스크라이버의 전체 체인을 나타냅니다.
    • 이 구독은 퍼블리셔를 "활성화"하며, 구독이 종료되면 퍼블리셔는 값을 방출하지 않습니다.
  • 구독을 사용하면 비동기 이벤트 체인을 한 번만 선언할 수 있으며, 사용자 정의 코드와 에러 처리를 통해 해당 체인을 조작할 수 있습니다.
    • 모든 코드를 Combine으로 구현한다고 상상해보면, subscription을 통해 전체 앱의 로직을 구현할 수 있습니다.
    • 한 번 구현이 완료되면 데이터를 푸시하거나 가져오거나 객체를 호출할 필요 없이 모든 것이 자동으로 실행됩니다.
  • Combine은 Cancellable 프로토콜을 제공하여 subscription을 취소하고 관리할 수 있습니다.
    • 이 프로토콜을 준수하는 객체는 메모리 관리를 위해 사용됩니다.
    • 따라서 subscription 코드는 Cancellable 객체를 반환하고, 해당 객체를 해제할 때 subscription도 자동으로 취소되고 메모리에서 해제됩니다.
  • UIViewController 속성에 subscription을 저장하여 subscription의 수명을 쉽게 관리할 수 있습니다.
    • 이렇게 하면 해당 UIViewController가 메모리에서 해제될 때 subscription도 자동으로 취소됩니다.
    • 또는 Set<AnyCancellable> 속성을 사용하여 여러 subscription을 관리할 수도 있습니다.
    • 이 속성이 메모리에서 해제되면 해당 속성에 저장된 모든 subscription도 자동으로 취소되고 메모리에서 해제됩니다.

Combine의 장점

  • 비동기 코드에 다른 추상화를 추가하여 안전성과 편의성을 제공하는 것을 목표로 합니다.

Combine의 장점은 다음과 같습니다.

  1. 통합된 시스템 수준의 추상화: Combine은 시스템 레벨에서 통합되어 사용자에게 공개되지 않는 언어 기능을 API 형태로 제공합니다.
  2. 테스트 용이성: Combine은 비동기 작업을 추상화하여 테스트 가능한 "operator"를 제공합니다. 이를 통해 이전 스타일의 비동기 코드에서 발생하는 테스트 양을 줄일 수 있습니다.
  3. 구성과 재사용성: 모든 비동기 작업은 Publisher라는 동일한 인터페이스를 통해 이루어지므로 구성과 재사용성이 강화됩니다.
  4. 쉬운 operator 구성: Combine은 쉽게 구성 가능한 operator를 제공합니다. 사용자가 새로운 operator를 만들 경우 다른 Combine 코드와 즉시 결합되고 사용할 수 있습니다.

Combine은 이러한 장점을 통해 안전하고 편리한 비동기 코드 작성을 도와줍니다.

앱 구조

  • Combine은 앱의 구조를 크게 영향주지 않는 framework입니다.
    • 다양한 앱 구조 패턴인 MVC, MVVM, VIPER 등 어디에서든 사용할 수 있습니다.
    • Combine 코드를 추가할 부분에서만 선택적으로 사용할 수 있으며, 필요한 경우에만 추가하면 됩니다.
    • 데이터 모델 변환, 네트워킹 계층 조정 또는 추가된 새로운 기능 등의 부분에서 Combine을 사용하여 시작할 수 있습니다.
  • Combine과 SwiftUI를 함께 채택하는 경우, 약간 다른 접근 방식이 필요할 수 있습니다.
    • 이 경우, MVC 아키텍처에서는 Controller를 제거하는 것이 권장됩니다.
    • 반응형 프로그래밍을 사용하여 데이터 모델에서부터 View까지 제어하는 경우 특별한 Controller가 필요하지 않습니다.
    • SwiftUI와 Combine은 같은 공간에서 함께 사용될 때 더욱 간단하게 작동합니다.

Summary

  • Combine은 선언적이고 반응적인 비동기 이벤트 처리를 위한 프레임워크입니다.
  • Combine은 비동기 프로그래밍 도구를 통합하고 변경 가능한 상태를 처리하며, 에러 처리와 같은 기존의 문제를 해결하기 위해 설계되었습니다.
  • Combine은 Publisher(이벤트 생성), Operator(이벤트 처리 및 조작), Subscriber(결과 활용 및 작업 수행)이라는 세 가지 주요 유형을 중심으로 동작합니다.
728x90

'iOS > SwiftUI' 카테고리의 다른 글

[SwiftUI] Property Wrapper @  (0) 2023.06.13
[SwiftUI] Stack, LazyStack, Grid, List, Form  (0) 2023.06.13