Error
프로토콜을 채택하는 모든 타입을 사용하여 에러를 나타낸다.
에러를 던지기 위해서 throw
를 사용하고 에러를 던질 수 있는 함수를 나타내기 위해서 throws
를 사용한다.
함수에서 에러가 발생하면 함수는 즉시 반환되고 함수를 호출한 코드가 에러를 처리한다.
func send( job : Int, toPrinter printerName : String ) throws -> String {
if printerName == "Never Has Toner"{
throw PrinterError.noToner
}
return "Job sent"
}
에러
를 처리하는 방법은 여러가지가 있다.do-catch
do{
let printerResponse = try send(job : 1040, toPrinter : "Bi Sheng")
// 앞에서 send 함수에 'throws'를 명시해줬기 때문에 try 키워드를 앞에다가 붙여야 한다.
}catch{
print(error)
}
// JobSent
catch
블럭 case
이후에 하는 것처럼 catch
이후에 패턴을 작성한다.do{
let printerResponse = try send(job : 1440, toPrinter : "Gutenberg")
}catch PrinterError.onFire{
print("~~~")
}catch let printerError as PrinterError{
print("Printer error : \(printerError)")
}catch{
print(error)
}
// "Job sent"
try?
nil
이다. 옵셔널 값
을 포함한다. let printerSuccess = try? send(job : 1884, toPrinter : "Mergenthaler")
let printerFailure = try? send(job : 1885, toPrinter: "Never Has Toner")
defer
: 함수를 반환하기 직전에 함수의 다른 모든 코드 다음에URLSessionError
: URLSession에서 발생하는 일반적인 에러로 네트워크 요청이 실패했을 때 나타난다.
<세부에러>
URLError
.notConnectedToInternet
인터넷 연결이 없는 경우
URLError
.timedOut
요청이 타임아웃된 경우
URLError
.cannotFindHost
호스트를 찾을 수 없는 경우
URLError
.cannotConnectToHost
호스트에 연결할 수 없는 경우
URLError
.networkConnectionLost
네트워크 연결이 도중에 끊어진 경우
URLError
.badServerResponse
서버로부터 잘못된 응답을 받은 경우
URLError
.unsupportedURL
지원되지 않는 URL을 사용하는 경우
이건 패스
DecodingError
.dataCorrupted
JSON 데이터가 손상된 경우 발생
DecodingError
.keyNotFound
JSON에서 필요한 키를 찾을 수 없는 경우
DecodingError
.typeMismatch
JSON데이터의 타입이 일치하지 않는 경우
DecodingError
.valueNotFound
JSON에서 필요한 값을 찾을 수 없는 경우
예시코드
do{
let result = try ~~api 호출하는 함수
}catch let corruptError = DecodingError.dataCorrupted{
print("Corrupt 에러 : \(corruptError)")
}catch{
print("정의되지 않은 에러 : \(error)")
}
enum NetworkError : Error{
case invalidURL
case noData
case decodingFailed
case custom(message : String)
}
@escaping
키워드는 클로저
가 함수의 실행이 끝난 후에도 호출될 수 있음을 나타내는 속성이다. 클로저
가 함수 외부에서도 사용되거나 저장될 수 있음을 명시하는데 사용된다.@escaping
속성을 사용한다.* 이렇게 설명을 들었는데도, 솔직하게 @escaping
이 이해가 안된다.
@escaping
이 필요한 이유는 클로저가 함수의 실행이 끝난 후에도 호출될 수 있음을 명시적으로 나타내기 위해서이다.
구체적으로, 클로저가 함수 외부에서 참조되거나 저장되어 함수가 반환된 이후에도 클로저가 사용될 때 @escaping
이 필요하다. 이를 통해서 swift는 메모리 관리와 관련된 최적화를 올바르게 수행할 수 있다.
비동기작업
비동기 작업에서는 함수가 종료된 후에도 클로저가 실행될 수 있다.
예를 들어서, 네트워크 요청의 결과를 처리하는 클로저는 네트워크 응답이 도착할 때까지 대기하기 때문에 함수가 종료된 후에 실행된다.
(참고
) swift에서
함수는 Named Closure
익명함수는 Unnamed Closure ( 보통 클로저라고 하면 이를 말한다 )
비동기작업
에서의 예시코드를 한번 보자. func fetchData(
from urlString : String,
completion : @escaping ( (Result<Data, Error>) -> Void ) throws -> Void
// 여기에 escaping을 쓰는 이유는 unnamed closure인 익명함수를 다시 재사용할 것이기 때문이다. 일회용이 아니라고 적어놓은 것
){
guard let url = URL(string : urlString) else{
// 만약에 urlString을 URL에 인자로 준게 NIL이 뜬다면
completion(.failure(error))
return
// guard let은 return을 꼭 해줘야함. switch의 case 문이라고 생각해야댐
}
// guard let을 통과한 상수는 전역 변수로서 사용할 수 있다.
let task = URLSession.shared.dataTask(with : url){
data, response, error in
if let error = error{
// 만약에 error가 발생한다면
completion(.failure(error))
// 에러 처리해주고
return
// 함수 종료
}
guard let httpResponse = response as? HTTPURLResponse,
(200..299).contains(httpResponse.statusCode) else{
// 만약에 response가 nil이거나 statuscode가 200대가 아닐 경우에
completion(.failure(NetworkError.custom(message:"Invalid response from server")))
return
}
guard let data = data else{
// 만약에 data가 안들어오면
completion(.failure(NetworkError.noData))
return
}
completion(.success(data))
}
task.resume()
}
do{
let result = try fetchData(from : "api 주소"){
result in
switch result{
case .success(let data) :
print("Data Received: \(data)")
case .failure(let error) :
print("Error occurred: \(error)")
}
}
}catch{
print("error : \(error)")
}
throws
키워드를 달아놓으면, 그 함수를 호출할 때 try
와 에러에 대한 처리를 강제한다.그러면 다음과 같은 구조가 된다.
do{
const result = try ~~~~
}catch{
...
}
try
, try?
, try!
를 쓸 수 있는데 한번 보자.try
는 일반적인 경우에 사용된다.
try?
는 안전한 에러처리를 위해서 사용된다.
try!
는 에러가 발생하지는 않을 것이라고 확신할때 사용하나, 사용하지말자
(스스로를 믿지마라)
try?
try?
는 에러가 발생하면 nil
을 반환한다. 에러가 발생하지 않으면 옵셔널 타입
으로 값을 반환한다.do-catch
문에서 쓸일은 없을 것 같고 다음과 같이 사용 이후에 if let
에서 사용한다.func someFunctionThatThrows() throws -> String{
// 에러를 던질 가능성이 있는 함수 (throws)
return "Success"
}
let result = try? someFunctionThatThrows()
if let result = result{
print("정상 처리 ~~~\(result)")
}else{
print("에러")
}
try!