- Error Handling (챕터 16) - 지훈
Never
Failure 타입이 Never인 퍼블리셔는 절대 실패할 수 없다는 것을 나타냅니다.
public typealias Failure = Never// Just 등의 publisher에서 사용
- 퍼블리셔가 절대 실패하지 않음을 컴파일 타임에 보장
- 에러 처리 로직이 필요 없음
특징
- 간단한 sink 구독 가능 (에러 핸들링 클로저 불필요)
Just("Hello")
.sink(receiveValue: { print($0) })// 에러 핸들링 없이 값만 처리
Never Failure 퍼블리셔들
- sink
- Just
- Empty
- @Published 프로퍼티 래퍼
- Timer.publish()
assign(to:on:)
func assign<Root>(
to keyPath: ReferenceWritableKeyPath<Root, Self.Output>,
on object: Root
) -> AnyCancellable
publisher로부터 받은 값을 object의 프로퍼티에 할당해주는 메소드.
중복이니까 이부분은 패스!
setFailureType
실패하지 않는 publisher를 실패할 수 있는 publisher로 변환해야 하는 경우에 사용됩니다.
func setFailureType<E>(to failureType: E.Type) ->
Publishers.SetFailureType<Self, E> where E : Error
upstream publisher의 failure type을 바꾸기 위해 사용한다.
대신 upstream publisher의 failure type은 Never 여야 한다!
(실패할 수 있는 upstream의 에러 타입을 바꾸어야 한다면 mapError(_:) 사용)
예시
enum MyError: Error {
case ohNo
}
let publisher = Just("안녕하세요")
.setFailureType(to: MyError.self) // 실패 가능성 추가
publisher.sink(
receiveCompletion: { completion in
switch completion {
case .failure:
print("오류 발생!")
case .finished:
print("완료!")
}
},
receiveValue: { text in
print("받은 텍스트: \\(text)")
}
)
//출력
//받은 값: 안녕하세요
//성공적으로 완료되었습니다!
두 publisher의 Failure 타입이 같아야 하기 때문에, 에러 타입을 맞추기 위해서 사용된다.
또한 setFailureType 은 타입에만 영향을 미치기 때문에 실제 에러는 발생하지 않는다.
assertNoFailure
assertNoFailure는 "이 데이터 스트림은 절대 실패하면 안됨" 이라고 말하는 메서드이다.
func assertNoFailure(
_ prefix: String = "",
file: StaticString = #file,
line: UInt = #line
) -> Publishers.AssertNoFailure<Self>
upstream publisher가 실패하면 fatal error를 일으키고, 아니라면 받은 input 값을 모두 다시 publish하는 메소드
- 테스트 중 내부 무결성을 확인하고 싶을 때 사용 → publisher가 실패로 종료될 수 없음을 확인할 때 유용!
- 개발, 테스트뿐 아니라 배포 버전에서도 fatal error를 일으키기에 개발시에만 사용하는걸 권장
예시
public enum MyError: Error {
case genericSubjectError
}
let subject = CurrentValueSubject<String, Error>("initial value")
subject
.assertNoFailure()
.sink(receiveCompletion: { print ("completion: \\($0)") },
receiveValue: { print ("value: \\($0).") }
)
subject.send("second value")
subject.send(completion: Subscribers.Completion<Error>.failure(MyError.genericSubjectError))
// Prints:
// value: initial value. : 초기값
// value: second value. : 1번쨰 보낸거
// The process then terminates in the debugger as the assertNoFailure operator catches the genericSubjectError. : 에러받앗네? 나죽어~
subject 가 세 번째로 genericSubjectError 라는 에러를 보냈고, fatal exception이 발생해서 프로세스가 중단됩니다!
try 연산자들~
- map / tryMap
- scan / tryScan
- filter / tryFilter
- compactMap / tryCompactMap
- removeDuplicates / tryRemoveDuplicates
- reduce / tryReduce
- drop / tryDrop
이 operator들은 publisher의 Failure 를 Swift의 Error 타입으로 바꿔줍니다~!
중복되는 내용이 많으니 패스!
Catching and retrying
replaceError
스트림에서 받은 에러를 특정한 값으로 대체(replace)하는 operator
func replaceError(with output: Self.Output) -> Publishers.ReplaceError<Self>
Swift의 nil-coalescing operator(??)과 비슷하게 작동합니다
예시
let publisher = someFailablePublisher
.replaceError(with: "데이터를 불러올 수 없습니다")// 에러가 발생하면 대체 메시지 던져줌
.sink { print($0) }
// 실패하는 네트워크 요청예시
URLSession.shared.dataTaskPublisher(for: url)
.map { String(data: $0.data, encoding: .utf8) ?? "" }
.replaceError(with: "네트워크 오류 발생")
.sink { print($0) }
에러를 다시 새로운 publisher로 감싸고 downstream subscriber로 전달해줄때 catch(_:) 가 사용됩니다.
catch
func `catch`<P>(_ handler: @escaping (Self.Failure) -> P) -> Publishers.Catch<Self, P> where P : Publisher, Self.Output == P.Output
upstream publisher로부터 받은 에러를 다른 publisher로 교체하는 메소드
예시
let mainPublisher = URLSession.shared.dataTaskPublisher(for: primaryURL)
.catch { error -> AnyPublisher<Data, Never> in
// 주 서버가 실패하면 백업 서버로 전환
return URLSession.shared.dataTaskPublisher(for: backupURL)
.map { $0.data }
.catch { _ in Just(Data()) } // 최후의 폴백
.eraseToAnyPublisher()
}
retry
func retry(_ retries: Int) -> Publishers.Retry<Self>
retry는 실패한 작업을 지정된 횟수만큼 재시도한다.
- retries : 재시도할 횟수
- publisher 반환
let SOPTPublisher = URLSession.shared.dataTaskPublisher(for: url)
.retry(3) // 최대 3번까지 재시도
.map { result in
return String(data: result.data, encoding: .utf8) ?? ""
}
.catch { error in
return Just("서비스를 이용할 수 없습니다. 잠시 후 다시 시도해주세요.")
}
덧)
캐치랑 리트라이는 하나만 쓰는게 아니라 같이쓸때 더 좋을거같아요
let mainPublisher = someFailablePublisher
.retry(2) // 먼저 재시도를 해보고
.catch { error in
// 재시도가 실패하면 백업 퍼블리셔로 전환
return backupPublisher
}
.replaceError(with: "모든 복구 시도 실패")
'IOS - Swift > Combine' 카테고리의 다른 글
[iOS/Combine] Transforming Operators (4) | 2024.12.05 |
---|---|
subscription의 이해 (0) | 2024.11.25 |
Publisher - Just, Empty, Failure (1) | 2024.10.03 |
Combine - Publisher 기초 (2) | 2024.10.01 |
[iOS/Swift] Combine - 시작하기 (Publisher ,Operator, Subscriber) (1) | 2024.04.17 |