티스토리 뷰
안녕하세요 토니입니다.
이번에는 지난번 1편에 이어서 시작하도록 할게요.
1편은 간략하게 Combine 설명을 합니다.
Combine을 접하신 적 없는 분은 1편
2023.06.19 - [WWDC] - [WWDC] Combine 프로젝트에 적용하기 - 1편
을 먼저 보고오시는 것을 추천드립니다.
진짜 들어가기전에 빨간색은 Publisher, 파란색은 Subscriber, 노란색은 Operator로 표시해두었으니 염두해두고 읽어보세요!
이번편은 아래의 WWDC를 기반으로 합니다!
Combine in Practice - WWDC19 - 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


간달프씨가 앱을 만들어달라고 의뢰를 했네요
마법사 학교를 위한 새로운 앱을 만들고자 하는데요.
이 앱에는 다른 마법사들이 공유한 멋진 마술 트릭을 다운로드 할 수 있는 기능이 포함되길 원하는 것 같아요.

Combine을 사용하면 NotificationCenter가 Publisher와 함께 알림을 노출할 수 있거든요.
그래서 우리는 마법사 친구가 전달할 알림을 위한 Publisher를 생성합니다.
이 NotificationCenter Publisher는 알림을 전달하며, 실패할 수 없습니다.
우리는 Notification을 가지지만, 실제로 우리가 원하는 것은 다운로드한 마술 트릭을 설명하는 데이터죠.

친구가 데이터를 userInfo 딕셔너리에 넣었다고 말을 했구요.
다행히 Combine은 우리가 원하는 형식으로 notification을 내부에서 찾아내고 변환할 수 있는 map operator를 제공해요.
이제 우리는 오류를 발생시키지 않으며, 데이터를 출력하는 Publisher를 볼 수 있죠.

자 이제 데이터를 decode 하기 위해 tryMap이라는 새로운 Operator를 사용할 수 있어요.
tryMap을 잠깐 짚고 넘어가자면, map과는 다르게 tryMap은 제공된 error-throwing closure를 사용하여 upstream publisher의 모든 요소를 변환하는 친구입니다.
map과는 다르게 에러가 발생할 수 있다는 뜻이죠. tryMap의 경우, 클로저가 오류를 발생하면 publisher를 종료하게 됩니다.
그러니 closure가 에러를 던지면 tryMap, 아니면 Map. 간단하죠?
여기서는 decode가 실패할 수 있으니 tryMap을 쓴거겠죠? 쉽죠?
그럼 이제 출력형태는 마술트릭과 그리고 실패타입은 Error가 되겠네요.

근데 decode하는 작업은 매우 일반적인 작업이라, 이를 처리해주는 operator를 또 제공해준다하네요..
그냥 단순히 decode하고 호출해주면 됩니다.
그런데 이제는 실패가능한 publisher를 가지게 되었어요.
combine에서는 잠재적인 실패에 적절하게 반응하는것이 매우 중요하거든요.
그럼 어떻게 대응할 수 있는지 한번 볼까요?

모든 Publisher는 Failure를 가지고 있어요.
이제 이걸 Operator로 잘 다뤄서 Failure에 대한 대비를 해야 합니다.
많은 유형들은 실패 타입을 Never로 설명해요.
이는 실패를 이전 단계에서 처리해야할 것으로 기대한다고 하는건데요.
가장 간단한 방법에는 assertNoFailure 가 있죠.

자 보시면 실패타입이 Failure -> Never로 바뀌었어요.
그래서 Never로 바뀌면 뭐가 다른데😕
라고 생각하실 수 있을 것 같아요.
그림으로 한번 볼게요.
Publisher랑 Subscriber사이에 assertNoFailure Operator가 있는 상황이에요.
Publisher가 정상적으로 값을 발행하면 Subscriber가 잘 받을 수 있죠.
근데 아래처럼 Failure가 전달된다면???

Subscriber는 Failure를 받지도 못하고 assertNoFailure가 처리하는 거죠.
무슨뜻이냐고요?
프로그램은 종료되겠죠 뭐..
이러면 마법사 친구가 원하는 마법같은 결과는 아닐거에요 그쵸?
다행이 Combine에는 실패와 관련된 많은 Operator들이 있습니다😇

특히 유용한 Operator는 catch인데요.
catch는 클로저를 제공하여 원래의 upstream publisher에서 실패가 발생한 경우 복구 publisher를 정의할 수 있다고 해요.
살펴볼까요?
아까 그림에서 assertNoFailure 대신 catch를 사용해볼게요

또 우리의 바보같은 publisher는 Failure를 보내는군요.🥲

Failure를 받은 catch가 기존의 Publisher와의 연결을 끊는 모습이네요.
그런 다음 제공된 복구 클로저를 호출하고, 새로운 Publisher가 발행하는 값이 Subscriber에게 전달됩니다.
이렇게 하면 catch Operator를 사용하여 오류를 복구할 수 있고, 원래의 Publisher를 새로운 것으로 대체할 수 있습니다.
이제 코드로 볼까요?

assertNoFailure 대신 catch를 사용한 모습이고, Failure를 받은 경우 catch에서 Just가 실행되게 되네요.
다시한번 정리를 해볼게요.

decode Operator를 통해 Data를 마술 트릭으로 디코딩했어요.
근데 여기서 Never가 Error로 변했죠.
Error를 다뤄줘야하는데, assertNoFailure는 앱이 강제종료되니 고려하지 않아요.

catch Operator는 Failure를 받으면 기존 Publisher와의 연결은 끊고, 새로운 Publisher와 연결시키죠?
그게 Just 구요.
근데요, Recovery Publisher로 전환하면 더이상 기존 Publish를 받지 못하거든요. 구독이 종료되었으니까요.
근데 우리가 원하는 것은 디코딩을 실패하더라도 대체값을 사용하면서 원래의 Publisher와 연결을 유지하는 거 아닐까요??
놀랍게도 Combine에는 그런 Operator가 있습니다ㅎㅎ
이것을 우리는 flatMap 이라고 부르죠.

flatMap 안에서 decode도 catch도 just도 쓸거랍니다.

자 decode가 잘 된다면 문제가 없겠죠.
그럼 실패해서 폭탄을 catch에게 전달한다면?

catch는 기존 Just와 연결을 끊고 새로운 Recovery Publisher를 통해 새로운 값을 발행하겠죠???
근데 여기서 중요한 점은 Just와의 연결을 끊은 것이지, 기존의 Publisher와 Subscriber간 연결을 끊은게 아니에요
그러니까 이 Recovery Publisher가 flatMap에 반환되기 때문에, 가장 큰 작업은 절대 실패하지 않도록 보장하게 되죠.

위 그림과 같이요.
이제 코드를 봐볼까요?

flatMap 내부에 있는 decode가 실패하여 Failure가 발생한다 하더라도, catch는 기존 Publisher가 아닌 Just와의 연결을 끊는 것입니다. 그리고 그 결과를 flatMap이 return 하게 되죠.
결과적으로는 이는 절대 실패하지 않는 마술과도 같죠

자 이제 원래 하려고 했던 작업을 진행해보겠는데요.
특정 마술 묘기의 이름을 publish 해주면 됩니다.
그럼 여기까지 Publisher를 다양한 Operator로 구성하는 법을 알아봤는데요.
한번 프로젝트에 적용한 것을 볼까요??
상황은 간략하게 다음과 같아요.
1. 네트워크 통신
2. 통신 후 Decode
이 사이에 우리가 배운 Combine을 한번 적용시켜보도록 할게요!

request 메서드 입니다.
보시면 dataTaskPublisher를 사용한 것을 볼 수가 있죠.
누가 봐도 publisher 인것을 알수가 있죠???

이 publisher는 작업이 complete하면 data를, 실패하면 에러와 함께 종료를 publish하는 publisher라고 하네요.
그럼 얘는 Data/Error 니까 Map을 쓴다면 Error 발생 시 프로그램이 종료가 될 수 있겠죠??
그래서 tryMap을 사용했습니다. 아까 위에서 보았듯이 말이죠.
중간에 노랑색 박스로 decode Operator도 사용해주었구요.
파랑 박스에 mapError가 보이는데요.
아까 혹시 Failure handling Operator 기억하시나요?
assertNoFailure, catch 등등 있었잖아요
그 Operator 중 하나입니다.

mapError는 발생한 에러를 새로운 에러로 대체하거나, 발생된 에러를 캐치해서, 어떤 에러인지 식별할 때 사용할 수 있다고 해요.
이렇게 dataTaskPublisher에서 publish 하는 Data/Error를 모두 처리해주었으니 다음으로 넘어가볼까요?

이 부분은 제가 설계한 ItunesSearchAPI인데요.
빨간 박스부분만 볼까요?
실패하면 Fail, 그렇지않으면 request 입니다.
별거없죠?

자 이렇게 호출을 하겠구요

여기서 사용된 것을 한번 볼까요?
flatMap이 보이구요. 그리고 replaceError가 보이네요.
한번 들여다 볼까요?
아까 flatMap은 무슨 Operator라고 했는지 기억나시나요?
기존의 Publisher와 Subscriber의 연결이 끊기지 않게한다했죠?
첫번째 빨간박스의 fetchData는 NetworkError를 return 합니다.
바로 위 사진에서 알 수 있죠.
하지만, 전체 메서드는 Never를 반환하고 싶어합니다.
그럼 아까 배웠듯 Error를 Never로 바꾸는 과정을 거쳐야겠죠?
밑에 빨간 박스 보이시나요?
replaceError

이것역시 Error handling Operator의 종류 중 하나인데요.
말 그대로 error를 제공된 요소로 바꾸는 메서드입니다.
위 코드에서는 Error가 발생한다면 nil로 대체됩니다.
당연히 Publisher의 Element 타입과 일치해야합니다.

이 Operator는 single replacement element를 전송하고 오류를 처리하는 경우에 유용합니다.
위의 예시와 같이 nil. 아니면 뭐 Bool 일경우 false와 같이 그런 single element일 경우를 말하는 거죠.
그런데 이와 같은 상황이 아니라면 catch를 사용해서 replacement publisher를 사용하는 것이 좋다고 하네요.
어떤가요?
좀 도움이 되셨나요?
Combine을 간략하게 살펴보고 프로젝트에 적용을 해보았는데요.
공식문서가 많은 도움이 된 것 같아요.
여러분도 wwdc를 보고 공식문서를 활용해서 프로젝트에 적용시켜보시길 바랍니다!
'WWDC' 카테고리의 다른 글
| [WWDC] Combine 프로젝트에 적용하기 - 1편 (0) | 2023.06.19 |
|---|
- Total
- Today
- Yesterday
- 코딩테스트
- Codable
- Publisher
- Operator
- Decodable
- dataSource
- Combine
- 프로젝트에 적용
- 롯데 렌탈
- subscriber
- UITableViewDelegate
- 롯데그룹 공개채용
- 야곰
- ios
- 공개 채용
- 야곰 아카데미
- UITableViewDataSource
- Parse
- 개발자 취업
- 의존성 주입
- 운영체제
- 프로그래머스
- Swift
- 반효경
- DIP
- 취준
- Encodable
- 화면 이동
- osi7계층
- 스토리보드 분할
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |