실행 도중 일시 중지할 수 있는 특수한 종류의 함수 또는 메서드
를 사용하는 방식.단순하게 생각하면 비동기 처리의 완료 시점까지 기다려준다.
비동기 함수를 표시하려면 함수 선언의 매개변수 뒤에 async 키워드를 작성
func listPhotos(inGallery name: String) async -> [String] {
let result = // ... some asynchronous networking code ...
return result
}
비동기 메서드를 호출하면 해당 메서드가 반환될 때까지 실행이 일시 중단.
일시 중단 지점을 표시하기 위해 호출 앞에 await를 붙인다.
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
위 코드의 실행과정
await 키워드를 사용한 listPhotos(inGallery:) 함수를 호출하고 해당 함수가 반환될 때까지 기다리는 동안 실행을 일시 중단
이 코드의 실행이 일시 중단되는 동안 동일한 프로그램의 다른 동시 코드가 실행, 해당 코드는 await로 표시된 다음 일시 중단 지점까지 또는 완료될 때까지 실행
listPhotos(inGallery:)가 반환된 후 이 코드는 해당 지점부터 실행을 계속한다. listPhotos(inGallery:)가 리턴해준 값을 photoNames에 할당.
다음 await는 downloadPhoto(named:) 함수에 대한 호출이기 때문에 이 코드는 해당 함수가 반환될 때까지 실행을 다시 일시 중지하여 다른 동시 코드가 실행할 기회를 제공한다.
downloadPhoto(named:)가 반환된 후 반환 값은 photo에 할당된 다음 show(_:) 호출 시 인수로 전달.
여기서 iOS에서 UI 업데이트와 별개로 이뤄지는 네트워킹 같은 비동기 처리를 하기 위해선 어떻게 해야할까?
await withTaskGroup(of: Data.self) { taskGroup in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
taskGroup.addTask { await downloadPhoto(named: name) }
}
}
let newPhoto = // ... some photo data ...
let handle = Task {
return await add(newPhoto, toGalleryNamed: "Spring Adventures")
}
let result = await handle.value
Task에 감싸진 코드들은 비동기로 실행하고 await 함수가 호출되고 나서 Task 내부에 있는 다음 코드를 호출
한다. Task 외부에 있는 코드는 Task의 종료를 기다리지 않는다.
Task은 실행 중 적절한 시점에 취소되었는지 여부를 확인하고 적절한 방식으로 취소에 응답한다.
수행 중인 Task에 따라 일반적으로 다음 중 하나를 의미한다.
사용방법
Task가 조기에 중지되는 마지막 지점을 이미 지나 실행된 경우 이 메서드를 호출해도 취소는 불가능.
네트워킹 비동기 처리를 CompletionHandler로 사용한 프로젝트를 간단하게 Concurrency로 변경해보기.
CompletionHandler
// completionHandler 방식에서 Urlsession 코드
func excute(completionHandler: @escaping (Result<[[String : Any]], Error>) -> Void) {
guard let url = self.url else { return completionHandler(.failure(APIError.noneUrlValue)) }
let task = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
guard error == nil else { return completionHandler(.failure(APIError.request)) }
guard let response = response as? HTTPURLResponse, 200 <= response.statusCode, response.statusCode < 300
else { return completionHandler(.failure(APIError.response)) }
guard let data = data else { return completionHandler(.failure(APIError.invalidData)) }
guard let jsonData = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String : Any]]
else { return completionHandler(.failure(APIError.decode)) }
completionHandler(.success(jsonData))
}
task.resume()
}
// completionHandler 방식에서 실제 네트워킹 사용
wiseSayingRequest.excute { result in
switch result {
case .success(let data):
DispatchQueue.main.async { [weak self] in
guard let wiseSaying = data[1]["respond"] as? String else { return }
self?.wiseSayingLabel.text = wiseSaying
.replacingOccurrences(of: "-", with: "–")
.split(separator: "–")
.map { String($0) }
.joined(separator: "\n\n")
}
case .failure( _):
break
}
}
```swift
// Concurrency 방식에서 Urlsession 코드
func excute() async -> (Result<[[String : Any]], Error>) {
guard let url = self.url else { return .failure(APIError.noneUrlValue) }
do {
let (data, response) = try await URLSession(configuration: .default).data(from: url)
guard let response = response as? HTTPURLResponse, 200 <= response.statusCode, response.statusCode < 300
else { return .failure(APIError.response) }
guard let jsonData = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String : Any]]
else { return .failure(APIError.decode) }
return .success(jsonData)
} catch {
return .failure(APIError.request)
}
}
// Concurrency 방식에서 실제 네트워킹 사용
Task.init {
let data = await wiseSayingRequest.excute()
switch data {
case .success(let data):
DispatchQueue.main.async { [weak self] in
guard let wiseSaying = data[1]["respond"] as? String else { return }
self?.wiseSayingLabel.text = wiseSaying
.replacingOccurrences(of: "-", with: "–")
.split(separator: "–")
.map { String($0) }
.joined(separator: "\n\n")
}
case .failure(_):
break
}
}
https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#ID643
https://developer.apple.com/documentation/swift/updating_an_app_to_use_swift_concurrency