WWDC/iOS

[iOS] WWDC19 - Combine in Practice (1)

유정주 2023. 1. 22. 21:39
반응형

Combine

Combine은 시간의 흐름에 따라 값을 처리하는 API 입니다.

자세한 내용은 이전 포스팅인 "WWDC19 - Introducing Combine"을 참고해 주세요.

 

이번 발표인 "WWDC19 - Combine in Practice"에서는 실제로 Combine을 사용해보겠습니다.

 

우리는 이 UI 스케치를 가지고 앱을 하나 만들 것입니다.

Trick Name에 마법 기술 이름을 넣을거에요.

 

NotificationCenter는 특정 Notification을 위한 Publisher로 등록할 수 있도록 지원합니다.

이렇게 생성된 Publisher의 Output 타입은 Notification 타입이고, Failure 타입은 Never입니다.

 

이제 우리는 Notification을 발행하는 Publisher를 만들었습니다.

 

하지만 우리는 Notification 타입의 Output이 아니라 Data 타입의 Output을 원합니다.

그래서 map을 이용해 Output 타입을 Notification 타입에서 Data 타입으로 변경해주었어요.

이 내용도 이전 포스팅에서 다뤘습니다.

 

한 가지 더 요청사항이 있습니다.

이 Data가 JSON 형태라고 하네요.

이때 Combine의 tryMap Operator를 사용하면 유용합니다.

일반적인 Map과 동일하지만 에러를 발생시킬 수 있습니다.

tryMap을 이용하니 디코딩에 성공하면 MagicTrick 타입을, 에러가 발생되면 Error을 반환할 수 있게 됐습니다.

 

이 디코딩 작업을 쉽게 해주는 것이 바로 decode Operator 인데요.

decode 오퍼레이터를 이용해 MagicTrick 타입으로 JSON 디코딩을 시도해서

MagicTrick 타입 Output 또는 Error를 발생시키는 Publisher를 생성했습니다.

 

Error Handling

이제 Publisher는 Error를 발생시킬 수 있으니,

이를 핸들링할 수 있어야 합니다.

Combine에서는 잠재적인 Failure에 대해 적절히 대응하는 것이 매우 중요합니다.

Publisher와 Subscriber에는 모두 자신이 받아들이거나, 자신이 발생시킬 수 있는 Failure에 대해 정의할 수 있습니다.

 

많은 타입들이 Failure 타입으로 Never를 가져서, stream에 들어가기 전에 에러를 처리하고,

이외에는 에러에 대응할 수 있는 다양한 Operator를 지원합니다.

 

assertNoFailure

예를 들어,

assertNoFailure는 Failure 타입을 Never로 변경해 줍니다.

 

Never는 Error가 절대 발생하지 않는다는 것을 의미하는데요.

만약 Error가 전달되면,

런타임 에러가 발생하게 됩니다 ㅠㅠ

 

Failure Handling Operators

Combine에서는 다양한 에러 핸들링 Operator를 지원합니다.

여기에서 catch는 유용하게 쓸 수 있는 Operator에요.

catch에 에러가 전달되면,

기존의 Upstream Publisher와 연결이 해제되고,

Recovery Publisher를 연결한 뒤에 이 Publisher가 방출하는 값을 Subscriber에게 전달합니다.

 

catch를 코드로 보면 이렇습니다.

에러가 발생했을 때의 동작을 클로저로 정의해서 등록합니다.

그럼 에러가 발생했을 때 클로저가 실행되면서 에러 핸들링을 하게 됩니다.

 

위에서 사용된 Just는 발행할 값이 이미 정해져있는 특별한 Publisher인데요.

이렇게 하면 Publisher는 절대로 실패하지 않게 됩니다. (무조건 특정 값을 방출하니까요!)

 

지금까지 알아본 Combine의 흐름을 정리해 봅시다.

1. Notification을 방출하는 Publisher를 생성합니다.

2. map을 이용해 Notification 타입을 Data 타입으로 변환합니다.

3. decode를 이용해 Data 타입을 JSON 디코딩을 하고, 이때 에러가 발생할 수 있습니다.

4. 에러가 발생하는 경우 기존 Publisher와 연결을 해제하고, 에러를 catch 하여 placeholder를 방출하게 합니다.

 

여기서 4번의 "기존 Publisher와 연결을 해제"를 봅시다.

우리가 원하는건 기존에는 기존 Publisher의 값을 전달하고, 에러가 발생할 때만 placeholder를 전달하고 싶습니다.

하지만 위의 동작은 아예 연결을 해제하죠.

 

flatMap

이럴 때 flatMap을 이용할 수 있습니다.

flatMap은 map과 비슷하게 동작하는데요.

내부에 Publisher를 생성해서 Subscriber에게 데이터를 전달합니다.

위 그림은 flatMap이 생성한 Publisher가 MagicTrick 타입의 값을 전달하고, 에러를 발생시키지 않는다는 의미입니다.

 

flatMap이 포함된 흐름을 그림으로 알아봅시다.

아래의 Just ~ catch는 flatMap이 생성한 Publisher 입니다.

decode Operator에서 에러가 발생하지 않으면, 정상적으로 디코딩된 결과를 Subscriber에게 전달합니다.

 

하지만 decode 과정에서 에러가 발생했다고 생각해 봅시다.

catch는 기존 Publisher와 연결을 끊고 Recover Publisher와 연결을 하는데요.

flatMap과 Subscriber의 연결은 그대로 유지되기 때문에,

기존 발생한 문제가 해결 되었습니다.

 

다음 번에 같은 흐름이 생긴다면, Just부터 catch까지 새로운 Publisher가 flatMap 안에서 생기겠죠? ㅎㅎ

그래서 우리가 원하는 동작인, 에러가 발생할 때만 catch의 Recovery Publisher를 이용할 수 있게 됐습니다.

 

코드를 보면,

map의 결과를 flatMap으로 적용하여,

flatMap 안에서 데이터를 디코딩하고, 에러가 발생하면 placeholder를 생성합니다.

 

이러면 절대로 Failure가 발생하지 않는 흐름이 생성됩니다.

 

마지막으로 publisher for Operator를 이용해서

디코딩 모델의 프로퍼티를 방출하게 했습니다.

 

Scheduled Operators

Combine에는 스케줄에 관련된 특별한 Operator도 존재합니다.

언제, 어디서 특정한 이벤트가 발생하는지 알 수 있는 Operator가 있고,

이들은 RunLoop와 DispatchQueue에 의해 지원됩니다.

 

예를 들어, delay는 어떤 이벤트를 전달하는 행위를 지연시킬 수 있고,

throttle은 이벤트의 전달이 일정한 속도 이상으로 빨라지지 않게 설정할 수 있습니다.

또, receive Operator를 이용해 Downstream 이벤트가 특정 스레드나 큐로 전달되도록 지정할 수도 있습니다.

 

마법 이름을 Label에 업데이트하기 위해 이름이 항상 메인 큐로 전달되도록 지정했습니다.

 

여기까지의 흐름을 그림으로 보면

이렇게 표현이 가능합니다.

 

이제 우리는 Publisher를 이용해 원하는대로 흐름을 구성할 수 있습니다.

 

 

 

 

 


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

 

 

 

반응형