KoreaMango 나무

[iOS App Dev Tutorials] SwiftUI - Persistence and Concurrency (4) 본문

iOS/iOS App Dev Tutorials

[iOS App Dev Tutorials] SwiftUI - Persistence and Concurrency (4)

KoreaMango 2022. 5. 18. 21:28
 

Apple Developer Documentation

 

developer.apple.com

4. Modernizing Asynchronous Code

Swift 5.5 는 async/await 과 구조화된 동시성을 포함한 새로운 동시성 기능 세트를 도입했다.

앞에서 했던 지속 데이터에서 작성한 로드 및 저장 방법은 비동기적이지만 새 비동기/대기 패턴과 호환되지 않는다. 이 튜토리얼에서는 다른 비동기 코드에서 메서드를 호출할 수 있도록 이러한 메서드에 대한 비동기 인터페이스를 만든다.

Section 1. Change the Deployment Target

(이거 삭제하고 바꾸는 사진 넣기)

Scrumdinger project (최상위 파일)을 클릭하고 target에 있는 프로젝트를 클릭하면, info 섹션에서 버전을 바꿀 수 있다.

Section 2. Create an Asychronous Load Method

Swift는 비동기 코드와 기존 콜백 기반 API들과 연결이 가능한 함수를 제공하고 있다. 이번 섹션에서는 그 함수들 중 하나를 사용할 것이다. CheckedThrowingContinuation 로 로드 메소드의 버전 비동기를 만든다.

static func load() async throws -> [DailyScrum] {
	try await withCheckedThrowingContinuation { continuation in
		load { result in
			switch result {
				case .failure(let error):
            continuation.resume(throwing: error)
      }
		}
  }
}

DailyScrum을 리턴하는 함수를 만든다. 매개변수 리스트 뒤에 async 키워드를 추가하는 것은 함수가 비동기라는 것을 나타낸다. throws 는 오류가 생겼을 때 오류를 처리해주는 곳으로 전달해준다는 뜻이다.

load 함수에 await 키워드를 사용해서 함수를 호출합니다. 그 다음 클로져에 제공할 continuation 을 넘겨준다. continuation 은 대기 함수 뒤에 있는 코드를 나타내는 값이다.

클로저안에 컴플리션 핸들러를 사용해서 존재하는 load 함수를 사용한다. 그 다음 result 의 케이스를 다루기 위한 switch를 추가한다.

로드에 실패하면 실패 케이스에서 에러를 continuation으로 보낸다. 시스템은 비동기 작업을 다시 시작하면 에러를 던져준다. continuation은 결과, 값을 리턴하는 메소드와 에러를 던져주는 메소드가 있다.

성공하면 continuation 클로저에 scrum을 보내준다. 시스템은 비동기 작업을 다시 시작하면 withCheckedThrowingContinuation 의 결과는 스크럼 배열이 된다.

Section 3. Create an Asynchronous Save Method

이 섹션에서는 새 비동기 버전 내에서 기존 메소드를 호출해 감싸줌으로써 저장 메소드에 대한 비동기 인터페이스를 만든다.

@discardableResult
static func save(scrums: [DailyScrum]) async throws -> Int {
	try await withCheckedThrowingContinuation { continuation in
			save(scrums: scrums) { result in
        switch result {
        case .failure(let error):
            continuation.resume(throwing: error)
        case .success(let scrumsSaved):
            continuation.resume(returning: scrumsSaved)
        }
    }
	}
}

우선 Int 값을 리턴하는 비동기적인 save 정적 함수를 만든다. 이 Int 값은 함수의 호출한 사람이 사용할 수 없는 값이다. @discardableResult 속성은 사용하지 않은 반환 값에 대해서 경고를 비활성화한다.

그 다음 await 키워드를 사용해서 withCheckedThrowingContinuation 함수를 호출한다.

그 다음 클로저에다가 기존의 save 함수를 컴플리션 핸들러로 호출하고, 결과를 switch로 받아내고 마무리한다.

Section 4. Use Tasks to Call Asynchronous Methods

이렇게 만든 save, load 함수를 직접 적용시켜보자.

NavigationView {
    ScrumsView(scrums: $store.scrums) {
        Task {
            do {
                try await ScrumStore.save(scrums: store.scrums)
            } catch {
                fatalError("Error saving scrums.")
            }
        }
    }
}
.task {
    do {
        store.scrums = try await ScrumStore.load()
    } catch {
        fatalError("Error loading scrums.")
    }
}

Task 는 scrumStore.save를 사용할 비동기 문맥을 생성한다.

do-catch 문을 사용해서 ScrumStore에 비동기적으로 save를 한다. 에러가 발생했을 경우는 catch에 작성한다.

SwiftUI는 비동기 태스크를 view에 연결하는 데 사용할 수 있는 태스크 한정자(.task)를 제공합니다. 시스템이 Navigation View를 만들 때 한정자(.task)를 사용하여 스크럼을 로드합니다.

do 절에서 await 중인 함수가 완료되면 스크럼에 새로운 값이 할당된다. scrum은 pulished property 이기 때문에 ScrumStore가 새로고침 되면, 이것을 observing 하는 View는 모두 업데이트 된다.