[SwiftUI] Async/Await

Junyoung Park·2022년 8월 28일
1

SwiftUI

목록 보기
56/136
post-thumbnail
post-custom-banner

How to use async / await keywords in Swift | Swift Concurrency #3

Async/Await

구현 목표

  1. Async, Await를 사용할 때의 스레드 환경 파악하기
  2. Actor를 활용한 스레드 환경 조정하기

구현 태스크

  1. GCD의 종류에 따른 스레드 환경 확인하기
  2. sleep 등 비동기 함수를 실행한 이후 스레드 환경 파악하기
  3. MainActor 내부의 스레드 상황 파악하기

핵심 코드

    func addSomething() async {
        try? await Task.sleep(nanoseconds: 2_000_000_000)
        let something1 = "Something1 : \(Thread.current)"
        await MainActor.run(body: {
            self.dataArray.append(something1)
            
            let something2 = "Something2 : \(Thread.current)"
            self.dataArray.append(something2)
        })
    }
    
...
           Task {
                await viewModel.addAuthor1()
                await viewModel.addSomething()
                let finalText = "FinalText : \(Thread.current)"
                viewModel.dataArray.append(finalText)
            }
  • Task.sleep 메소드는 throw 가능한 비동기 함수로 해당 코드를 실행한 이후의 스레드는 메인 스레드가 아님
  • MainActor 내부에서의 스레드 환경은 언제나 메인 스레드(애플 프레임워크 내부의 싱글턴 객체)
  • 해당 코드 실행은 Task를 통해 await 대기 가능

소스 코드

import SwiftUI

class AsyncAwaitBootCampViewModel: ObservableObject {
    @Published var dataArray = [String]()
    
    func addTitle1() {
        dataArray.append("TITLE1 : \(Thread.current)\nIs Main? : \(Thread.isMainThread)")
    }
    func addTitle2() {
        DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
            let title2 = "TITLE2 : \(Thread.current)\nIs Main? : \(Thread.isMainThread)"
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.dataArray.append(title2)
                self.dataArray.append("Title3: \(Thread.current)\nIs Main? : \(Thread.isMainThread)")
            }
        }
    }
    
    func addAuthor1() async {
        let author1 = "Author1 : \(Thread.current)"
        self.dataArray.append(author1)
        try? await Task.sleep(nanoseconds: 2_000_000_000)
        // after sleep -> thread : not main thread
//        try? await doSometing()
        // await -> suspension point
        let author2 = "Author2: \(Thread.current)"
        // after sleep -> thread : main thread
        await MainActor.run(body: {
            self.dataArray.append(author2)
            let author3 = "Author3: \(Thread.current)"
            self.dataArray.append(author3)
        })
        await addSomething()
    }
    
    func doSometing() async throws {
        print("do Something")
    }
    
    func addSomething() async {
        try? await Task.sleep(nanoseconds: 2_000_000_000)
        let something1 = "Something1 : \(Thread.current)"
        await MainActor.run(body: {
            self.dataArray.append(something1)
            
            let something2 = "Something2 : \(Thread.current)"
            self.dataArray.append(something2)
        })
    }
}
  • 현재 코드가 실행되는 스레드 환경을 프린트
  • Task.sleep을 통해서는 현 스레드(메인)가 다른 스레드로 바뀌지만 doSomething이라는 함수를 통해서는 바뀌지 않음 → async 함수라 할지라도 스레드 환경이 코드에 따라 달라짐
struct AsyncAwaitBootCamp: View {
    @StateObject private var viewModel = AsyncAwaitBootCampViewModel()
    var body: some View {
        List {
            ForEach(viewModel.dataArray, id:\.self) { data in
                Text(data)
                    .font(.headline)
                    .fontWeight(.bold)
            }
        }
        .onAppear {
            Task {
                await viewModel.addAuthor1()
                await viewModel.addSomething()
                let finalText = "FinalText : \(Thread.current)"
                viewModel.dataArray.append(finalText)
            }
        }
    }
}
  • UI 모델

구현 화면

현재 UI를 담당하는 있는 데이터 서비스의 코드가 실행되는 스레드가 메인 스레드인지 확인 필수. 동일한 객체에 비동기적으로 접근하고 있는 코드가 두 개 이상이라면 actor 클래스를 통해 동기화 적용 가능하다는 데 주의하자

profile
JUST DO IT
post-custom-banner

0개의 댓글