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

AsyncSequence 란

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

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

 

 

내부 구현부

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

@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 을 참고해 주세요.

 

 

개요

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

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

for await value in AsyncSequenceType {}

 

AsyncSequence종료되는 조건으로는

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

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

 

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

 

 

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

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

 

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

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로 묶어서 사용해 줬습니다.

 

 

AsyncSequence와 AsyncIteratorProtocol 합쳐서 사용하기

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

 

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

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

 

 

에러처리와 같이 사용하기

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

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

 

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

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

 

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

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에 대하여 알아보았습니다.

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

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

 

 

참고

 

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

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