nyancoder

WWDC 2021 - Discover concurrency in SwiftUI 본문

WWDC/WWDC 2021

WWDC 2021 - Discover concurrency in SwiftUI

nyancoder 2021. 10. 19. 01:22

원본 영상: https://developer.apple.com/videos/play/wwdc2021/10019/

 

Concurrent data models

  • 이 예제에서는 SwiftUI를 사용하여 우주 사진을 다운로드하는 앱을 볼 것입니다.

  • 데이터 모델로는 SpacePhoto 구조체를 사용하여 이미지에 대한 정보를 저장합니다.
  • 구조체에는 제목, 설명, 이미지가 게시된 날짜, 이미지 URL과 같은 필드가 있습니다.
  • 서버 응답에서 인스턴스를 만들거나, 디스크에 저장할 수 있도록 Codable로 만듭니다.
  • ForEach 및 기타 데이터 기반 View에서 사용할 수 있도록 Identifiable로 만듭니다.

  • 사진의 목록을 표시하기 위해서 컬렉션을 가져와 보관할 Photos 클래스를 모델로 사용합니다.
  • Photos 클래스는 ObservableObject를 구현하여, SwiftUI View가 데이터가 업데이트될 때마다 자동으로 업데이트됩니다.
  • Published 속성을 사용하여 SpacePhotos 배열을 저장하고 있습니다.

  • PhotoView는 제목을 표시합니다.

  • CatalogView는 사진 목록이 표시됩니다.
  • List에서 ForEach를 사용하여 각 사진에 대한 PhotoView를 표시합니다.

  • SwiftUI가 ObservableObject와 상호작용하는 방식은 "run loop"라고 불리는 라이프 사이클을 통해서 이루어집니다.
  • run loop는 사용자로부터 이벤트를 수신하고, 모델을 업데이트할 수 있도록 한 다음, SwiftUI View에 렌더링 합니다.
  • 이러한 한 번의 업데이트를 "Run loop tick"이라고 부를 수 있습니다.
  • Photos객체를 예로 들면, updateItems를 호출하고 주 액터에서 실행됩니다.
  • "items"는 Published 속성이므로 값이 변경되면 objectWillChange 이벤트를 전달한 뒤 "items"에 보관됩니다.
  • SwiftUI가 objectWillChange를 보면, 스냅숏을 만들고 run loop의 다음 틱에서 스냅숏을 현재 값과 비교합니다.
  • 값이 다른 경우 SwiftUI는 View를 업데이트합니다.
  • objectWillChange, 저장소 업데이트, 실행 루프 틱이 모두 Main Actor에서 발생하기 때문에 순서대로 발생하는 것이 보장됩니다.

  • 모델 코드가 너무 많은 작업을 수행하는 경우 업데이트가 느려질 수 있습니다.
  • 예를 들어 fetchPhotos함수에서 다운로드가 완료되기를 기다리고 있으면 run loop tick의 호출을 막고 있기 때문에 업데이트가 일어나지 않아 멈춘 것처럼 보입니다.
  • 또 다른 경우는 DispatchQueue를 통해서 다른 스레드에서 fetchPhotos를 수행하고 아이템을 업데이트하는 경우가 있습니다.
  • 이 경우는 objectWillChange가 run loop tick 직전에 호출되어 SwiftUI가 스냅숏의 변경을 알아채지 못하여 UI가 업데이트되지 않을 수 있습니다.

  • 올바르게 업데이트가 되려면, SwiftUI는 objectWillChange, ObservableObject 업데이트, run loop tick의 순서로 이루어져야 합니다.

  • Swift 5.5 이전과 달리 상태를 업데이트하기 위해서 단순히 await만 호출하면 됩니다.

  • updateItems에서 fetchPhotos를 비동기로 호출하기 위해 해당 함수를 async로 만듭니다.
  • 그다음 fetchPhotos 또한 async 함수로 구성하고 호출 지점에 await를 붙여줍니다.
  • 이 과정에서 결과의 업데이트가 Main Thread에서 이루어지는 것을 보장할 수 있도록 @MainActor를 class에 적용합니다.

  • 이제 카탈로그가 표시될 때마다 updateItems를 호출하려고 할 때, 이전에는 onAppear를 사용했습니다.
  • 이제는 task함수를 이용하여 뷰 수명이 시작될 때 비동기로 작업을 호출할 수 있습니다.
  • 이러한 새 함수들의 작업의 수명은 View의 수명과 연결되어 있으므로 View의 수명이 끝나면 작업이 자동으로 취소됩니다.

  • AsyncImage API를 사용하면 원격 서버에서 쉽게 이미지를 로드할 수 있습니다.

  • AsyncImage에서는 로딩 중에 표시할 View를 위한 placeholder함수가 있습니다.
  • 또한 이미지의 로딩이 끝나면 원하는 크기로 리사이즈 작업을 처리할 수 있습니다.

  • 다음 예로 이미지를 저장하는 버튼을 만들면, 비동기 작업을 수행하기 때문에 async구문으로 저장 작업을 수행합니다.
  • 저장이 되고 있는 과정을 나타내기 위해서 isSaving 변수에 상태를 기록합니다.
  • label 항목에서 저장 중인지 여부를 체크하여 불투명도와 ProgressView() 표시 유무를 결정합니다.

  • 목록을 새로 고칠 수 있는 refreshable 함수를 통하여 비동기로 콘텐츠를 받아 업데이트할 수 있습니다.

목차: https://nyancoder.tistory.com/2

Comments