[SwiftUI] Async Let

Junyoung Park·2022년 8월 28일
0

SwiftUI

목록 보기
58/136
post-thumbnail

How to use Async Let to perform concurrent methods in Swift | Swift Concurrency #5

Async Let

구현 목표

  • 여러 개의 데이터 패치를 동시에 실행할 경우 async에 따라 먼저 패치가 완료된 데이터부터 UI 적용 → 동시에 모든 패치된 데이터가 뜨도록 구현

구현 태스크

  1. Task 한 개 사용
  2. Task 여러 개 사용
  3. Task 내부 async let을 통해 여러 개의 데이터를 동시에 await 하기

핵심 코드

private func fetchImageAsyncLet() {
        // Load fetched Data at the same time
        Task {
            do {
                async let fetchImage1 = fetchImage()
                async let fetchImage2 = fetchImage()
                async let fetchImage3 = fetchImage()
                async let fetchImage4 = fetchImage()
                
                let (image1, image2, image3, image4) = try await (fetchImage1, fetchImage2, fetchImage3, fetchImage4)
                let images = [image1, image2, image3, image4]
                self.images.append(contentsOf: images)
            } catch {
                print(error.localizedDescription)
            }
        }
    }
  • 서로 다른 이미지 4개가 fetchImage라는 비동기 함수를 통해 리턴
  • async let을 통해 await를 할 때까지 대기 → 한 번에 (try) await를 통해 데이터 패치를 완료 → 업데이트 동시에 수행

소스 코드

import SwiftUI

struct AsyncLetBootCamp: View {
    @State private var images: [UIImage] = []
    @State private var navTitle = "AsyncLetBootCamp"
    let columns = [GridItem(.flexible()), GridItem(.flexible())]
    
    var body: some View {
        NavigationView {
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(images, id: \.self) { image in
                        Image(uiImage: image)
                            .resizable()
                            .scaledToFit()
                            .frame(height: 120)
                    }
                }
            }
            .navigationTitle(navTitle)
            .onAppear {
                fetchImageAsyncLet2()
            }
        }
    }
    
    private func getURL() -> URL? {
        guard let url = URL(string: "https://picsum.photos/1000") else { return nil}
        return url
    }
    
    private func fetchImage() async throws -> UIImage {
        guard let url = getURL() else { throw URLError(.badURL) }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            guard let image = UIImage(data: data) else {
                throw URLError(.badURL)
            }
            return image
        } catch {
            throw error
        }
    }
    
    private func fetchImageSingleTask() {
        Task {
            let image = try await fetchImage()
            self.images.append(image)
            let image2 = try await fetchImage()
            self.images.append(image2)
            let image3 = try await fetchImage()
            self.images.append(image3)
            let image4 = try await fetchImage()
            self.images.append(image4)
        }
    }
    
    private func fetchImageMultipleTasks() {
        Task {
            let image = try await fetchImage()
            self.images.append(image)
        }
        Task {
            let image = try await fetchImage()
            self.images.append(image)
        }
        Task {
            let image = try await fetchImage()
            self.images.append(image)
        }
        Task {
            let image = try await fetchImage()
            self.images.append(image)
        }
    }
    
    private func fetchImageAsyncLet() {
        // Load fetched Data at the same time
        Task {
            do {
                async let fetchImage1 = fetchImage()
                async let fetchImage2 = fetchImage()
                async let fetchImage3 = fetchImage()
                async let fetchImage4 = fetchImage()
                
                let (image1, image2, image3, image4) = try await (fetchImage1, fetchImage2, fetchImage3, fetchImage4)
                let images = [image1, image2, image3, image4]
                self.images.append(contentsOf: images)
            } catch {
                print(error.localizedDescription)
            }
        }
    }
    
    private func fetchTitle() async -> String {
        return "Fetched Title"
    }
    
    private func fetchImageAsyncLet2() {
        Task {
            do {
                async let fetchImage1 = fetchImage()
                async let fetchTitle = fetchTitle()
                let (image, title) = await (try fetchImage1, fetchTitle)
                self.images.append(image)
                self.navTitle = title
            } catch {
                print(error.localizedDescription)
            }
        }
    }
}
  • async let을 통해 서로 다른 여러 개의 async 이미지를 동시에 처리 가능하지만, 개수가 여러 개 이상 증가한다면 taskGroup을 사용할 필요가 있음
  • try를 하지 않는 경우는 throw를 통해 에러를 던지지 않는 함수의 리턴값일 경우(`fetchTitleasycn하게 문자열을 리턴하는 함수이지만 에러를 throw하지 않고 fetchImage1는 에러를 throw하기 때문에 fetchImageAsyncLet2에서 fetchImage1try로 받음)

구현 화면

  • 단일 Task 이미지 패치 과정 구현
  • 여러 개의 Task 이미지 패치 과정 구현
  • Async let을 통해 한 번에 데이터 패치 구현
profile
JUST DO IT

0개의 댓글