본문 바로가기
WWDC

WWDC 19 - Combine In Practice

by vapor3965 2021. 6. 19.

목차


    • 세션보면서 정리한 내용입니다. 해석이 잘못된 경우가 있을수있으니 발견하시면 댓글로 남겨주시면 감사하겠습니다🙏🏻

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

     

    Combine in Practice - WWDC 2019 - Videos - Apple Developer

    Expand your knowledge of Combine, Apple's new unified, declarative framework for processing values over time. Learn about how to...

    developer.apple.com

     

     


     

    관련내용

    • 에러핸들링, cancelation,스케쥴링, 좋은디자인패턴 방법을 배워보자.
    • SwiftUI와결합한방법도배워보자.

    👍요약


    굉장히 타입에 엄격하다. String?타입에는 String은 못간다…..?
    가장핵심은, 선언형이라는것. 반환타입을 계속가짐므로써, 체인형 프로그래밍을 가능하게해준다!!!!
    publisher를 통해 subscriber에 전달하는데, 그 과정속에서 다양한 operator 를 통하여 메인쓰레드에서전달할지, 다른 Output, failure로 만들지, 전달을 지연시킬지, failure할경우 다른방법을사용할것인지 등등 다양한 레시피를 만들어낼수있다.

    • 각 체인이 stream이라고 보면된다.
    • 위에서부터아래로흐르며, publisher(upstream)에서 subcriber(downstream)로 흐르도록되어있다.

    👍Publisher

    • Output과 failure를 가지고, stream을통해 subscriber에게전달한다.
    • 아주다양한 operator들이있다

    👍Subscriber

    • Input과 Failure를 가지고있고, receive들을 어떻게 처리하냐에따라 다양하게 구현할수도있다.
    • 또는, operator를 이용하여, 간결하게 subscription을 만들수있다.
    • .assign
    • .sink

    👍Operator 

    • Publisher이면서, Output과 failure를 적절히 변형시키고, subscription을 만들수도있고,
    • scheduler와 같은작업, 실패할경우에 대한 작업 등 굉장히많다.
    • Operator를 통해 입맛대로 다양하게 Publisher를 만들어내고, subscription을 작성하도록 하게해준다.

    👍CombineLatest

    • 여러개의 publisher들을 가지고, 하나의결과로 만들기위해서 필요한 Publisher이다.

    👍값을 보내줄수있는, publisher가 되기위해서는,

    1. 타입자체에 👍@Published 를 붙임으로써, 값을가지는타입이면서, Publisher가 되게할수있따!
    2. 또는 Publishers.Subjects ( ) 에 output, failure를 원하는 타입들로 지정하여, send( output ) 할수있다.
      • 👍Subject는
        • passThrough(값을저장하지않음),
        • currentValue (마지막값을저장)로 나뉜다.
    3. 또는, 각 프레임워크에서 제공하는 👍**Publisher를 사용할수있습니다.
      • NotificationCenter, URLSessionTask,

    즉, 타입,객체 자체에관찰하면서 값을얻고싶다면, @Published
    단순히 데이터만을보내고싶다면, Subjests를 사용하는게,
    1번은 2번으로도 구현이가능하고, 2번대신 타입을사용하여되고, 둘다할수있다.

    • Introducing Combine이 조금 어려웠는데, 이런 실제예를보면서 확실히 더알겠다!!
      • 둘다 굉장히 어렵기도했어서, 시간이좀걸리긴했다.
    • operator 를 통하여 다양한 Output으로 바꿔주고, 또한 .decode operator도있따!
    • failur과 관련한 operator도 많다.
    • schedule 과 관련한 operator도많다. - 딜레이, 일정속도안에서만, 다른큐에서,
    • Subscribe하는 방법은 크게 4가지로나뉜다.
      • .assign operator
      • .sink operator
      • Subjects
      • SwiftUI와 결합한방법.
    • 여러개의 비동기처리를 하기위해서는 CombineLatest로, Publisher들을 하나로 묶는다.

     


     

    간단한 Combine

    • 시간이지남에따라 value를 처리하는 unified, declarative API이다.
    • 즉 우선, Subscriber가 요청을해야 만들어진다.

    NotifciationCanter는 publisher를 가지고잇는데,
    중요한점은 Publisher의 Output은 NOtification이고,
    Failure는 Never이다.

    이제 전달하고자는 사람은dictionary info를 이용해서 보낼것이다!

    • 그러므로, 우리가사용할데이터를 변환한다.

    만약 Data가 JSON형식이라면, tryMap을 통해
    JSON형식으로반환하면서 ,
    중요한것은 Failure가 Error로바뀐다는점이다!!!!!

     

     

    👍.decode operator


    하지만?! 띠용!! 이렇게 더 간단한 API가 있다는저어어엄~!

    많은타입이 failure를 never를갖지만, 이는 실패할수도있다는듯이면서, 이전stream에서 이미 failure가 핸들링됐다는뜻으로볼수있다.

    또한, Failure가 없다라고 assert함으로써, Never로 바뀌게할수있다.

    • 하지만, 만약 failure이 존재한다면 이는 크래쉬난다.

     

     

    다양한 failure 관련한 operators가있다.

    유용한점은 catch인데,

    • failure가있을경우, 이전 Upstream publisher를 끊고, 새로운 Publisher와 subscribe 하여 새롭게 한다 .

    이렇게 바꿔줌으로써, failure는 Never가된다.

    하지만, 이는 한번failure하고나면 이전 publisher는 없어진다.

    그럼 이전publisher도 유지하고, failure할경우도 유지하는방법은없나?

    고것은 바로 flatMap

    • 이렇게 할수있다……

    • publisher() + kyepath operator를 통해 Output: String, failure: Never로 publisher로 바꿀수있다.

    👍Scheduled Operations

    • 언제,어디로 이벤트가전달될지 정하도록 도와주는 operator다!
    • RunLoop, DispatchQueue에도 지원되며,
    • 특정시간까지 지연시킬수도있다.
    • 특정rate보다 빠르지않게 전달할수도있다 - throttle
    • receive 특정 큐, 쓰레드에 전달되어질수있도록한다.
      • 그러므로, 이는 UI가 업데이트되어야하는상황에서 아주잘어울리겠다
    • 아래는 늘 mainQueue에서만 전달되도록 하게한다.
      • 또한 Output, failure타입은 변하지안흔ㄴ다.

    👍그러므로, Publisher와 아주다양한 operator를 통해 입맛대로 Output, failure를 바꾸고, 새로운 publisher로만들거나, 동기,비동기로 될수있다.

    Subscriber

    자, 그럼이제 receving하는 Subscriber를 보자

    • 3개의메서드가있는데, 이메서드가 호출되는 순서는 잘정의되어있고, 3가지룰을따른다.

    3 Rules


    우선, subscriber를 호출하면, 그의반응에따라, Publisher는 receive(subscription:) 을 호출한다.

    그리고, 0번또는 많은 value들을 downstream을통해 subscriber에게 보낼때, receive(_:) 를 호출한다.

    그리고, Publisher가 끝났거나, failure가발생했을때, recevie(completion:)을 호출한다.

    이것을받으면 더이상 value는 발생하지않는다.

    요약하면, Subscriber는 아래와같이 3개메서드를 받게된다
    ( 오른쪽으로이동하는순 )

    그렇다고 반드시3개가무조건호출된다는건아닌다.
    많은 stream이 infinte할수있기때문이다. notificationcenter처럼.
    그러므로 이때는 completion이 호출되지안흔ㄴ다.

     

     

    apple은 굉장히많은다양한 subscribers를 제공한다.

     

    첫번쨰 subscription으로,

    .assign이있다.

    위에서계속하면,
    assign이라는 kepath를 통해서, 아주간단한 subscription형태를 추가함으로써 Subscriber를 추가할수있다.
    또한, 이 operator는 cancellation을 생성하면서, cancle하도록할수도있다.

    바로 특정객체의 kepath를통해 프로퍼티에 바로 subscribe시켜버린다.

    Cancellation

    • Combine형태로 만들었고,
    • Publisher가 event를 전달하기전에 subscription을 terminate할수있는 것은 종종 장점이되기때문이다.

    deinit되어질때 자동적으로 cancel을 호출하도록해주는 장점이있다.

    • 덕분에 cancel call을 많이줄일수있을것이다.
    • 그저 이유용한것에 의지하라!

    또 다른 Subscription이 있다.

     

    .sink operator이다.

    와주 퐌타스틱한것이다.
    sink 를 통해서, 클로저안에서 마음대로 trickName을 마음대로 할수있다.

    • 오호!!! assign은 특정 객체에 붙이는것이였다면, 이는 escaping closure처럼 내마음대로 하는거네?!!
      마찬가지로, cancellable 을 반환함으로, terminate할수있다.

    세번째 subscription이있다.

     

    Subject

    • 약간하이브리드로써,약간의 publisher와 subscriber의 행동을 가지고있다.
    • 멀티케스팅을 지원한다
      • 즉, 멀티 subscriber에게 브로드캐스팅한다!!
    • 무조건적으로 보내는점이 있다.


    publisher에서출발해서, subject로도가능하다.

    Subject는 두개의종류로나뉜다.

    Passthrough, currentValue

     

    Passthrough Subjects

    • value를 저장하지않는다.
    • 즉, subscribe해야, 그때부터 value를 받을수있다.
    • 즉 subscribe하지않는다면, 이전의지나온 value들은 받을수없다.

    CurrentValue Subjects

    • 받은value중에 가장마지막을 기억하고있으면서, subscriber에게 그것을 받을것인지 기회를 준다.

    아주간단하게, Output, failure을 정의하여 Subjects를 만들수있다.
    그리고기존의 publisher가 해당 sujbects를 subscribe하도록한다.

    마찬가지로, subscription - sink를 사용할수있다.



    또한 무조건적으로보내는 send 를 사용할수있다.


    .share()


    🤔 shared() -만약 subject에 너무많이도착한다면? 새로운 subjects를 주입할수있다. - passthroughsubject이다.


    subjects는 정말유용한놈이다.

     

     

     

    그리고 , 네번째이자마지막인 subscriber

    SwiftUI와 결합

    ( ⚠️ Xcode 11부터는 deprecatede됌.)


    SwiftUI는 subscriber를 가지고있다……워메…

    그러므로, 우리는 단지 when, how (어떻게데이터가변할지)만 설명하는 Publisher만 제공해주면되는것이다……

    • 커스텀타입은 단순히 해당프로토콜을채택하면된다.

    추가적인 SwiftUI에서 data flow는
    Data Flow in SwiftUI -2019 를참고하라.

    잠시 맛보기로하면,

    이러한 데이터가있을때,

    간단하게 Subjests로 BindableObject 를 채택한다.

    • 그리고, Output타입을 Void로 지정했는데, 그이유는 프레임워크가 body메서드안에서 자동적으로 타입을 유추하기때문이다?????

    • 이렇게, 간단히 변경되면 send를 호출할수잇다.

    • 그리고 @ObjectBinding을 통해, 자동적으로 publisher를 발견하고, subscribe하게해준다…….대박…
    • 그리고 body안에 그냥간단하게 property를 참조하면끄으읕….

     

     

     

     

     

    비동기와관련한 Combine

     

    아래와같이, 아이디를 서버와체크하고,
    패스워드가 두개가일치하는지체크하고,
    세개가모두맞다면버튼을활성화해야한다.

    자 , 우선 패스워드두개를 보자.
    패스워드에 IBAction target 을 달아놓는다.

    두개는 각 동기적인 상황인데 어떻게 둘을 한꺼번에묶을수있을까?

    @Published를 통해, 둘중하나에 Publisher를 추가할수있따.


    👍@Published

    • Swift5.1에 소개된 프로퍼티wrapper다.
    • 프로퍼티에 publisher를 추가해준다.

    $password 를(dollar prefix) 통해, wrapped value에 접근한다.


    마지막에 password를 변경하게되면,
    subription이 호출될것이다.


    👍CombineLatest

     

    우리는 두개의비밀번호 publisher를 통해, 하나의 password를 가져야한다.
    그러므로, 그둘을 잇는 CombineLatest를 사용한다!

    • Publishers.CombineLatest ( …. ) 로 이용한다.

    와우…….결국 Output = String?, failur = Never인 Publisher를 을 반환하도록 하는구나……


    또한! 여기에 추가적으로, 아주간단한패스워드는 패스워드로원치않을경우, map을 추가해줄수웄디ㅏ……..


    우리는 이를통해 또 다른 publisher와 사용해야하므로,
    CombineLatest Publisher가 아닌, AnyPublisher로 만들어주자.

    1.요약하면, String타입을 Pulbisher로 property wrapper했고,

     

    2.그리고 둘을묶기위해, CombineLatest & business logic 를 사용했고,

    3.map과 eraseTOAnyPublisher를 통해


    4. 결국 Publisher를 얻었따!

     

     

     

     

    자이제ㅔ 서버통신이남아있느넫,
    우리는 매번 사용자가 입력할때마다 서버엥확인하고싶지않다.
    왜냐? 매번요청하고,그건안좋으니ㅏㄲ,

    이는 Debounce 를 통해 해결할수잇따.

    👍Debounce

    • 일정속도보다 빠른경우는 받지않도록 한다.

    가운데에 debounce를 꼄으로써 적당한주기로보낼수있다.

    👍removeDuplicates


    근데, 만약에 타이핑했는데, 결국 이전과 똑같은 값이라면?

    • 마지막값을기억하여 똑같은 값이나올경우 이를 처리하지않는다. !!!!

    이렇게~할수있다.

    그리고, 아직 서버와통신하지는않았다.
    서버와통신하는 함수를 아래와같다고하자.
    그리고 우리는 이를 publisher안에넣고싶다.

    flatMap을 통해서, 넣어볼수있겠다.

    Future

    Future는 promise 클로저를 주는데,
    이클로저는 success or failure만 갖는 result타입이디ㅏ.

    이렇게, 비동기적으로 usernameAvaiable을 호출하고,
    promise에 성공하면 success 이름으로, 실패해도 sucess nil로 전달한다!


    자, 이제 둘다 끝났다.
    그럼이제 두개를 비교하여 버튼활성화를 해보자


    아까했듯이! 우리는 CombineLatest operator 를 사용한다!
    이렇게, 아이디와 비밀번호로 Output을 만들고,


    위의 publisher를
    map을통해 Output을 boolean으로바꾸고,
    main thread에서 받아오도록하며,
    subscprion을추가해준다.
    .assign으로 바로 signupButton에 할당해버린다~!


    즉 요약하면,
    이렇게된다.
    이것이 Combine의 모든것이다.

    연습해봐라~!

    'WWDC' 카테고리의 다른 글

    WWDC 19 - Modern Swift API Design  (0) 2021.06.22
    WWDC 19 - Optimizing App Launch  (0) 2021.06.22
    WWDC 19 - Introducing Combine  (0) 2021.06.19
    WWDC 20 - App essentials in SwiftUI  (0) 2021.06.19
    WWDC 20 - Data Essentials in SwiftUI  (0) 2021.06.17

    댓글