Danny의 iOS 컨닝페이퍼
article thumbnail
반응형

1. AsyncSequence 란

기존의 Sequence는 한 번에 하나씩, 단계별(step)로 진행할 수 있는 값 목록(list of values)입니다.

여기에 비동기성추가한 것이 바로 AsyncSequence입니다.

 

 

2. 내부 구현부

먼저 AsyncSequence 내부를 살펴봅시다.

<swift>
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) @rethrows public protocol AsyncSequence { // AsyncIterator는 반복을 위해 AsyncIteratorProtocol을 구현해줘야 하네요. associatedtype AsyncIterator : AsyncIteratorProtocol // 사용할 요소의 타입을 정해줄 수 있는 것 같네요. associatedtype Element where Self.Element == Self.AsyncIterator.Element // 반복동작을 위해 위에 만든 AsyncIterator를 리턴합니다. func makeAsyncIterator() -> Self.AsyncIterator }

구조는 Async(비동기)라는 키워드만 붙었지 Sequence와 완전히 동일한 것 같네요.

 

Sequence에 대한 자세한 정보는 [iOS/Swift] 프로토콜 Sequence  을 참고해 주세요.

 

 

3. 개요

Sequence로 요소를 반복하는 가장 일반적인 방법은 for문(for-in loop)을 사용하는 것입니다.

대신 비동기 작업추가되므로 await 통하여 대기 지점을 만들어 줘야 되겠죠.

<swift>
for await value in AsyncSequenceType {}

 

AsyncSequence종료되는 조건으로는

  1. next() 작업에서 리턴 값으로 nil을 반환할 때
  2. 에러가 발생할 때 (throwing an error)

비동기적으로 작업을 한다는 의미는 실패를 할 수 있다는 의미를 갖고 있습니다. (즉. 에러를 던질 수 있죠)

 

이제 아래에서 예제에서 반복을 만들고 종료하는 법을 같이 확인해 봅시다.

 

 

4. 커스텀 타입에서 프로토콜 AsyncSequence 적용 방법

Sequence와 거의 동일한 방법으로 커스텀 타입을 만듭니다.

 

[iOS/Swift] 프로토콜 Sequence 커스텀 타입을 만드는 방법공식문서 를 참고해 주세요.

<swift>
struct Danny: AsyncSequence { typealias Element = Int func makeAsyncIterator() -> DannyAsyncIterator { return DannyAsyncIterator(count: 5) } } struct DannyAsyncIterator: AsyncIteratorProtocol { var count: Int mutating func next() async -> Int? { if count < 0 { return nil } defer { count -= 1 } return count } } let danny = Danny() // 비동기 작업이므로 Task로 묶어서 사용 Task { for await value in danny { print(value) } } // 5 // 4 // 3 // 2 // 1 // 0

AsyncIteratorProtocol(반복자)를 만들 때, next()에서 count가 0이 될 때까지 반복하게 만들어 줬습니다.

(0이 되면 nil을 만나게 되므로 반복이 종료됩니다.)

 

또한, 비동기 작업이므로 Task로 묶어서 사용해 줬습니다.

 

 

4.1. AsyncSequence와 AsyncIteratorProtocol 합쳐서 사용하기

사용자 타입을 Sequence로 만들 때는 ElementmakeAsyncIterator()생략이 가능했지만,

 

🤦 AsyncSequence에서는 기본 구현이 제공이 안되므로 꼭 구현을 해줘야 합니다.

<swift>
struct Danny: AsyncSequence, AsyncIteratorProtocol { typealias Element = Int var count: Int mutating func next() async -> Int? { if count < 0 { return nil } defer { count -= 1 } return count } func makeAsyncIterator() -> Danny { return self } } let danny = Danny(count: 5) Task { for await value in danny { print(value) } } // 5 // 4 // 3 // 2 // 1 // 0

 

 

4.2. 에러처리와 같이 사용하기

일단 에러를 만들기 위해 enum 생성을 해줬습니다.

<swift>
enum NumberError: Error { case lessThanZero var printMessage: Void { switch self { case .lessThanZero: print("Error: 음수를 감지함!") } } }

 

크게 바뀐 점은 없고, 다만 반복 구현을 하는 next() 메서드를 만들어 줄 때,

에러 구현을 위해 async 뒤에 명시적으로 throws 키워드를 붙여주고, throw를 통해 에러를 받아 줍시다.

 

마지막으로 실행 시 trydocatch 에러 처리만 해 주면 됩니다.

<swift>
struct Danny: AsyncSequence, AsyncIteratorProtocol { typealias Element = Int var count: Int // throws, throw를 통해 에러를 정의 mutating func next() async throws -> Int? { if count < 0 { throw NumberError.lessThanZero } defer { count -= 1 } return count } func makeAsyncIterator() -> Danny { return self } } let danny = Danny(count: 5) // try, do, catch로 에러 처리 Task { do { for try await value in danny { print(value) } } catch { NumberError.lessThanZero.printMessage } } // 5 // 4 // 3 // 2 // 1 // 0 // Error: 음수를 감지함!

count가 0이 되는 순간 throw에러를 반환하고 반복이 멈추는 것을 확인할 수 있습니다.

 

 

다음에는 AsyncSequence와 비슷하지만, 더욱 쉽게 사용 가능한 AsyncStream에 대하여 알아보겠습니다.

 

이렇게 오늘은 AsyncSequence에 대하여 알아보았습니다.

부족한 설명이지만, 조금은 이해 가셨나요?

틀린 내용이 있다면 언제든지 지적해 주시면 감사히 받겠습니다. 🫠
읽어주셔서 감사합니다 😃

 

 

5. 참고

 

Apple Developer Documentation

 

developer.apple.com

 

Apple Developer Documentation

 

developer.apple.com

 

[iOS/Swift] 프로토콜 Sequence

Sequence란? 순차적이고 반복적인 동작을 할 수 있게 만드는 프로토콜입니다. 즉, 순차적인 나열이죠. 선언 protocol Sequence 개요 설명은 공식문서 기반으로 할 예정이므로 자세한 내용을 알고 싶다면

ios-daniel-yang.tistory.com

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!