그동안 제대로 정리하지 않고 애매하게 알고 있던 내용들을 글로 정리하기로.
지피티가 그렇게 써주니까 그런가보다 한 것들이 많았는데, 당연한 얘기지만 원리를 이해해야 스스로 쓸 수 있기 때문에...
let myClosure: (String) -> Int = { input in
return input.count
}
보다시피 Int를 반환
Void
를 사용:let myClosure: (String) -> Void = { input in
print(input)
}
->
뒤에 자료형이 명시된 경우, 반드시 반환값을 제공해야 함. 반환하지 않으면 컴파일 에러 발생.completion
은 싱글턴 패턴의 shared
와 같이 관용적으로 쓰는 단어일 뿐이고, 실질적인 기능은 @escaping
키워드가 한다.
@escaping
키워드를 사용 해 completion의 코드블록을 실행시킬 수 있다.예시 코드
func performTask(completion: @escaping () -> Void) {
DispatchQueue.global().async {
completion() // 비동기 작업 완료 후 실행된다
}
}
Result<>
타입func fetchData(completion: @escaping (Result<String, Error>) -> Void) {
이런 코드를 쓸 때, 솔직히 Result<Success, Failure> 이거 뭔지 모르고 썼음...
그래서 switch result .success .failure 왜 이렇게 쓰는지도 몰랐다...
enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}
이렇게 정의가 되어있던 거였음.
다음과 같이 쓴다면, 행하는 작업의 결과가 성공이면 String을 반환하고, 실패면 Error를 반환한다는 것.
func fetchData(completion: @escaping (Result<String, Error>) -> Void) {
DispatchQueue.global().async {
let success = true
if success {
completion(.success("Data fetched successfully")) // 성공 반환
} else {
completion(.failure(NSError(domain: "Error", code: -1, userInfo: nil))) // 실패 반환
}
}
}
// 호출
fetchData { result in
switch result {
case .success(let data):
print("Success: \(data)")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}
저 호출부에서 let data, let error는 Result의 .success, .failure에서 연관된 값을 꺼내오는 변수이다. Result<,>는 제네릭이므로 저 안에 어떤 값이 들어있는지에 따라 달라진다. 연관값이 필요 없다면, _
을 통해 무시할 수도 있다.
방금 서술했듯이 Result
의 성공 타입은 제네릭이므로, 위의 String
외에 다른 타입도 반환할 수 있다.
func fetchNumber(completion: @escaping (Result<Int, Error>) -> Void) {
completion(.success(42)) // Int 타입 성공 반환
}
확실히 글로 정리해야 머릿속에서 더 짜맞춰진다.