다른 언어를 사용하다가 ios쪽 개발을 할때 코루틴과 같은 시퀀셜한 비동기 처리가 없는점이 매번 아쉬웠는데.. swift 5.5 부터 async await 가 지원되기 시작했다. 당연하게도 애플쪽 하위호환이란건 바라지도 말아야 하는지라...
그나마 xcode 13.2 부터 사용 가능하니 내부 샘플 프로젝트등에 적용해 보려고 대강 내용만 정리해 본다.
ios15 이하를 타겟으로 하는 경우 import _Concurrency 와 같이 비공식적으로 사용해야 한다. (이마저 없었으면 한 2년 뒤에나 사용할 듯)
async
@MainActor
func getSomething() async -> [Int] {
}
비동기 메쏘드는 메쏘드명 다음에 async 라는 키워드로 정의된다.
메인 쓰레드에서 동작해야 하는 경우 @MainActor 어노테이션으로 해당 메쏘드가 메인 쓰레드에서 동작하도록 지정할 수 있다.
await
@MainActor
func getSomething() async -> [Int] {
// 별도의 비동기 작업
let items = await fetchData()
return items
}
await 가 호출되면 비동기 처리가 완료될때까지 실행이 멈추게 되는데(suspend), 이때 시스템은 해당 쓰레드를 다른 작업에 사용하게 된다. await 를 사용하기 위해서는 메쏘드 혹은 블럭이 async 로 지정되어야 하므로, 보통 async 메쏘드 내부에서만 사용하게 된다.
async 메쏘드가 아닌 일반 메쏘드에서 호출하는 경우 별도의 async를 구성하는 객체를 정의하거나, 제공되는 Task 를 사용한다.
(swiftUI 에서는 .task{ } 를 사용)
func doWork() {
Task {
let result = await getSomething()
update(result)
}
// deprecated async { }
// async {
// await getSomething()
// }
}
async 메쏘드가 아닌 곳에서 await function() 을 호출하면 'async call in a function that does not support concurrency' ,
combine에서는 'Cannot pass of type (Output) async to parameter expecting synchronous function type' 과 같은 에러가 발생한다.
기존 콜백 방식을 async 형식으로 호출
대부분 주변의 메쏘드들은 여전히 콜백을 사용하니 콜백 메쏘드들 async 로 변환해 주어야 한다.
withCheckedContinuation, withCheckedThrowingContinuation 을 사용해 기존 콜백의 결과를 async 로 리턴할 수 있다.
func getSomething() async -> [Int] {
await withCheckedContinuation { continuation in
doSomethingWithCallback(completion: continuation.resume(returning:))
}
}
continuation.resume(returning:)
continuation.resume(throwing:)
continuation.resume(with: Result)
func getSomething() async throws -> [Int] {
await withCheckedContinuation { continuation in
DispatchQueue.main.async {
do {
// 비동기 작업
} catch {
// 에러인 경우
continuation.resume(thorwing: Error)
return
}
continuation.resume(returning: [])
}
}
}
여러 작업의 동시 실행
async 작업을 병렬로 동시에 수행한다. async let 을 통해 작업을 선언하고, await를 어떤 타입으로 결과를 받을지 지정한다. 작업들이 완료되면 await 뒤에 정의된 데이터 타입으로 return 된다.
func doSomething() async {
async let job1 = work1()
async let job2 = work2()
let results = await [job1, job2]
}
TaskGroup 을 사용한 병렬 처리
기본적인 사용방법은 그룹에 task 를 추가하고, for await in 으로 결과를 가져와 리턴한다.
TaskGroup은 next(), waitForAll(), map, compactMap 같은 컬렉션 메쏘드들을 제공하므로, 태스크들을 선택해 리턴할 수 있다.
await withTaskGroup(of: ChildTaskResult.Type, returning: GroupResult.Type) { taskGroup in
// 리턴할 데이터
var result: GroupResult.Type
// 그룹에 각 태스크 추가
taskGroup.addTask {
return await taskFunc()
}
// 각 결과 확인
for await taskResult in taskGroup {
// GroupResult.Type으로 리턴할 데이터 처리
}
// GroupResult.Type 리턴
return result
}
URLSession
func fetchImage(url: URL) async throws -> UIImage {
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw specificError
}
guard let image = UIImage(data: data) else {
throw specificError
}
return image
}
'프로그래밍 > iOS,macOS' 카테고리의 다른 글
[concurrency] Task, TaskGroup, task timeout (0) | 2023.02.28 |
---|---|
[swift] sorted array 에 값 추가 (0) | 2022.06.10 |
[Combine] 콜백 기반 여러 처리 결과를 배열로 받기 (0) | 2022.03.20 |
[SwiftUI] 오디오 레벨 에니메이션, Shape (0) | 2022.01.06 |
dataTaskPublisher 재시도 및 출력 타입 변경 (0) | 2021.10.16 |
Framework SPM 배포 (0) | 2021.09.24 |
URLSession.DataTaskPublisher (0) | 2021.09.17 |
카메라 데이터 수신을 위한 AVCaptureSession (0) | 2021.08.02 |
collection view 에서 load more 처리 (0) | 2021.07.20 |
UIPanGestureRecognizer 슬라이드 다운 뷰 (0) | 2021.07.01 |