정의
- Result 타입은 Generic Enumeration으로 선언되어 있고,
경우에 따른 연관 값을 포함하여, 성공과 실패를 나타내는 값입니다.
선언
@frozen enum Result<Success, Failure> where Failure : Error
문제점
- Error Handler을 사용했을 때, Error와 Data를 인스턴스로 다루고 있지만, 런 타임에 error와 data의 값이 모두 nil일 수 있습니다.
- 또 다른 경우로는 실제로 에러를 처리하기 위해 필요한 상태보다 더 많은 상태가 생겨 불필요한 상태를 추가로 처리해야 한다는 것입니다. 하지만 data와 error에 대한 에러 처리를 할 경우엔 총 4가지의 경우만 발생할 수 있습니다.
- Data = True , Error = False
- Data = True , Error = True
- Data = False, Error = False
- Data = False, Error = True
사용법
- 다음은 Result 타입의 정의입니다.
enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}
- 앞의 예제를 Result 타입을 이용해 바꿔보겠습니다.
- Result를 사용하므로 모호한 상태의 처리가 없이 명시적이고 간결하게 에러 처리를 할 수 있습니다.
func load(then handler: @escaping (Result<Data>) -> Void) {
//...
}
load { result in
switch result {
case .success(let data):
// load된 data 처리
case .failure(let error):
// error 처리
}
}
Result의 다양한 사용법
1, 타입에러
- 에러를 더 안전하고 정확하게 처리하기 위해 연관된 에러를 정의해 사용할 수 있습니다.
- 앞의 예제 코드에서 새로운 error 케이스를 정의하겠습니다.
enum LoadingError: Error {
case networkUnavailable
case timedOut
case invalidStatusCode(Int)
}
- 에러 발생 시 LoadingError를 발생시키기위해 Result의 Error에 LoadingError 타입을 선언합니다.
typealias Handler = (Result<Data, LoadingError>) -> Void
- 생성한 Handler를 이용해 load함수를 선언합니다.
func load(then handler: @escaping Handler) {
//...
}
- load 함수의 성공, 실패 처리를 다음과 같이 할 수 있습니다.
- 에러 발생시 직접 정의한 에러의 종류에 따라 처리할 수 있습니다.
load { [weak self] result in
switch result {
case .success(let data):
self?.handle(data)
case .failure(let error):
switch error {
case .networkUnavailable:
self?.showErrorView(withMessage: .unavailable)
case .timedOut:
self?.showErrorView(withMessage: .timedOut)
case .invalidStatusCode(let code):
self?.showErrorView(withMessage: .statusCode(code))
}
}
}
2, Throw 처리
- 작업 결과의 처리를 try, do, catch에서 직접 할 경우, 다음과 같이 할 수 있습니다.
- 성공 시 결괏값을 반환하고, 실패 시 throw 처리를 통해 해당 블록에서 탈출시킬 수 있습니다.
extension Result {
func process() throws -> Success {
switch self {
case .success(let value)
return value
case .failure(let error)
throw error
}
}
}
do {
let result = try value?.process()
handleValue(result)
}
catch {
handleError(error)
}
3, 지연처리
- 에러 처리를 바로 하지 않고 나중에 하고 싶을 때 사용할 수 있습니다.
Result 타입을 사용하지 않았을 때 코드
var configString: String?
var configError: Error?
do {
configString = try String(contentsOfFile: "myfile.data")
} catch {
configError = error
}
func doSomethingWithConfig() {
guard let configString = self.configString else {
handle(configError)
}
}
Result 타입을 사용할 때 코드
let configuration = Result { try String(contentsOfFile: "myfile.data") }
func doSomethingWithCongifg() {
switch configuration {
case .success(let success)
handleSuccess(success)
case .failure(let error)
handlError(error)
}
}
4, Result 변형
- Result는 고차 함수를 지원합니다.
func generateRandomNumber(maximum: Int) -> Result<Int, Error> {
//...
}
let result = generateRandomNumber(maximum: 7)
let number = result.map { "Generated Random number is : \($0)" }
내용 출처
https://jusung.github.io/Result-%ED%83%80%EC%9E%85/
https://velog.io/@un1945/Swift-Result-Type
'iOS > iOS' 카테고리의 다른 글
Equatable (0) | 2022.06.17 |
---|---|
Hashable (0) | 2022.06.16 |
접근 제어자의 종류 (0) | 2022.06.11 |
NSCache와 NSDictionary의 차이를 설명하시오. (0) | 2022.06.09 |
NSCache (0) | 2022.06.08 |
댓글