개인 프로젝트를 진행하다가 특이한 오류를 발견했다.
'_' is of type 'Never' which cannot be constructed because it is an enum with no cases
노란색 오류여서 빌드에 영향을 주지는 않겠지만, 처음 보는 오류였기 때문에 어떤 오류인지 알아보기 위해 검색을 해보았다.
그 결과, 이 오류는 Never타입을 사용했을 때 발생하는 오류라고 한다. 그러나 나는 Never타입을 사용했던 적이 없었기 때문에 이 오류가 왜 발생하는지 자세히 알아보고자 했다.
오류를 알아보기 전에 먼저 Never타입이란, Swift의 특별한 열거형(enum)으로 케이스를 전혀 가지고 있지 않은 타입이다.
즉, Never타입의 값은 절대로 존재할 수 없는 값으로, 해당 타입의 값을 반환하는 함수는 호출 후 절대 리턴하지 않거나, 항상 에러를 던지거나, 프로세스를 종료해야 함을 의미한다고 한다.
이 타입의 용도는 컴파일러에게 "이 코드는 절대로 정상적으로 완료되거나, 값을 방출하지 않는다"는 것을 명시하는 마커(Marker) 역할이라고 한다.
먼저 에러가 발생한 부분은 아래와 같다.

위 코드는 Reactor에서 mutate(action:) 메서드의 내부 구현부이다. 버튼을 눌렀을 때 현재 state의 content 값을 가져와 코어 데이터에 새로운 Diary 값을 저장하는 로직을 구현한 것인데, .map 연산자로 매핑하는 부분에서 에러가 발생했다.

와일드카드(_)가 무슨 타입인지 보기 위해 변수명을 삽입하면 위와 같이 Never 타입으로 설정되어 있다.
Never 타입의 내부가 어떻게 생겼는지 궁금해져서 구현부에 찾아가 보았다.

주석을 읽어보면 의미는 아래와 같다.
filure type of Never
이 타입이 비동기 작업(ex Combine, RxSwift)에서 실패 타입(filure type)으로 사용되었음
indication that it always succeeds
Never 타입은 에러 케이스가 될 수 없으므로, 해당 작업은 실패(error) 경로로 진입하는 것이 불가능함을 보장.
switch statement is therefore exhaustive
컴파일러는 .failure 케이스가 도달 불가능함을 알고 있으므로, 개발자가 해당 에러 케이스를 명시적으로 작성하지 않아도 모든 가능성을 처리한 것으로 간주
위 내용을 요약하자면, Never타입이 실패 경로를 가질 수 없는 타입이라는 것을 공식적으로 설명하고, 이로 인해 코드 작성 시 특정 에러 처리를 생략해도 안전함을 시사한다.
이 내용만 보면 내가 맞이한 에러는 굳이 어떻게 처리하지 않아도 문제가 되지 않음을 알 수 있다.
그러나 나는 노란 에러를 보는 것을 좋아하지 않기 때문에... 임의로 에러를 없애보려고 한다.
에러를 없애기 위해 다시 코드 분석부터 시작하려고 한다.
우선, 에러가 발생한 .map 부분을 기준으로 이전 코드들을 살펴보면 .asObservable과 saveNewDiary(content:)라는 메서드를 볼 수 있다.
saveNewDiary(content:) 메서드는 UseCase의 메서드로, repository protocol을 통해 repository에 새로운 데이터를 저장하도록 요청하는 메서드이다.

여기서 주의깊게 봐야할 부분은 메서드의 반환 타입인데, 이 메서드의 반환 타입은 Completable이다.
Completable은 RxSwift에서 제공하는 Observable 유형 중 하나로, 성공 또는 실패만 존재하고 데이터 요소(element)를 방출하지 않는 비동기 작업을 나타내는 타입이다.
즉, 작업이 완료됐다 혹은 실패했다는 사실만 알려주는 알림과 비슷한 동작을 한다.
원래는 Compleable 타입을 구독(subscibe)하여 반환된 결과(성공 or 실패)에 따라 UI를 업데이트 하거나 알림을 띄울 때 주로 사용한다.
그러나 이번에는 .concat 연산자에 포함시키기 위해 Observable 타입으로 만들 필요가 있었고, 때문에 구독하지 않고 .asObservable을 사용하여 Observable 타입으로 변환을 시킨 것이다.
문제는 여기서 발생했다. Observable은 Observable<Element>의 형식인데, Completable 타입을 .asObservable로 Observable 타입으로 변환시키게 되면 Element를 지니고 있지 않기 때문에 자동으로 Never타입이 되고 이 때문에 Observable<Never>를 반환하게 되는 것이다.
곰곰히 생각을 해봤는데, 어쨌든 Never타입은 사용하지 않는 타입이다. 중요한 것은 Completable의 결과(성공 or 실패)이고, .catch를 통해 failure가 발생했을 때의 상황을 대비했기 때문에 굳이 사용하려고 할 필요도 없다.(애초에 사용도 못한다.)
그렇다면 .map 연산자로 원하는 타입으로 변환시키기 전에, Never 타입을 Void 타입으로 변환하면 되지 않을까?

된다
명시적으로 Never 타입을 Void 타입으로 변환하였기 때문에 에러는 사라지고, 어차피 사용하지 않는 타입이기 때문에 구조상으로도 별 문제가 없다.
굳이 에러를 없애지 않아도 되지만, 에러를 보면 모두 없애고 싶은 마음이 있기 때문에 굳이굳이 없애보았다.
오늘은 처음 보는 에러를 해결해 보았다.
사실 치명적인 에러도 아니고, 놔둔다고 해서 문제를 일으키지 않는 코드이지만, 그냥 없애고 싶었다.
덕분에 Never 타입에 대해서도 알 수 있었고, 이렇게 트러블슈팅도 적을 수 있었으니 결과는 좋았다고 생각한다.