Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meet Asyncsequence #35

Open
kimscastle opened this issue Aug 18, 2023 · 0 comments
Open

Meet Asyncsequence #35

kimscastle opened this issue Aug 18, 2023 · 0 comments
Assignees
Labels
Swift 주제 WWDC21 WWDC 21년영상 의성

Comments

@kimscastle
Copy link
Contributor

kimscastle commented Aug 18, 2023

What is AsyncSequence

WWDC에서 AsyncSequence에 대해 설명할때 csv파일을 하나 로드한다고 가정을 하고 설명을 한다. 예를들어서 여러자료가있는 하나의 표를 url을 통해서 데이터를 불러온다고 가정해보면 우리는 항상까지는 아니어도 url을 통한 한번의 api호출을 통해 큰 덩어리의 데이터를 가져오고 그 데이터를 table이나 collection에 띄워주는 방식으로 구현을 해왔다

@main
struct QuakesTool {
    static func main() async throws {
        let endpointURL = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv")!

        // skip the header line and iterate each one 
        // to extract the magnitude, time, latitude and longitude
        for try await event in endpointURL.lines.dropFirst() {
            let values = event.split(separator: ",")
            let time = values[0]
            let latitude = values[1]
            let longitude = values[2]
            let magnitude = values[4]
            print("Magnitude \(magnitude) on \(time) at \(latitude) \(longitude)")
        }
    }
}

lines라는 메서드는 URL에 구현되어있는 내부메서드이고 return 타입이 AsyncLineSequence이다. 그렇다는 말은 sequence라는 뜻인데 sequence에대해서 간단하게 공부해보았다

스크린샷 2023-08-18 오후 6 33 29

Sequence

Sequence는 protocol인데 그전에 Swift에서는 Collection Type들인 Array, Set, Dictionary는 Collection프로토콜을 conform하고 있는데 이 Collection프로토콜이 Sequence프로토콜을 채택하고 있습니다

Sequence는 한번에 하나씩 진행할 수 있는 list of values인데 가장일반적인 방법으로는 for loop를 쓰는것이다

let oneTwoThree = 1...3
for number in oneTwoThree {
    print(number)
}
// Prints "1"
// Prints "2"
// Prints "3"

그렇다면 Sequence 프로토콜의 필수구현 메서드는 뭐가있을까 하고 보면

public protocol Sequence {
   ...
   func makeIterator() -> Self.Iterator
}

makeIterator이라는 메서드를 필수적으로 구현해야하고 그러기위해서는 Iterator라는 타입이 return되어야해서 IteratorProtocol을 conform하는 타입을 만들어야 합니다. 그래서 Sequence 프로토콜을 채택하는데 성공하게되면 연속적인 데이터가 완성되게되고 for loop에서 사용할 수 있게됩니다

for-in loop

컴파일러가 for loop를 만나게되면, 아래와같은 코드를 어떻게 변환하는지를 간단하게 알아보면 아래처럼 작동합니다

let arr = [1, 2, 3]
for value in arr { }

우선 arr를 Iterator로 만들어주고 next가 가능해지게되어서 while로 바꾸게됩니다, next가 nil이되면 while이 끝나고 for문이 종료되는것처럼

var iterator = arr.makeIterator()
while let value = iterator.next() {}

그래서 WWDC에서의 예제를 보면 아래와같은 코드를 만났을떄

for quake in quakes {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(queke)
    }
}

quakes를 iterator로 만들어주고 해당 return value의 next를 통해 loop를 도는 while문으로 변하게됩니다

var iterator = quakes.makeIterator()
while let quake = iterator.next() {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(queke)
    }
}

그리고 이를 Iterator 패턴이라고 합니다.

iterator pattern: 컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있는 방법을 제공

그렇다면 makeAsyncIterator를 사용해서 AsyncIterator를 만들게되면 기존에 makeIterator를 사용해서 Iterator를 만들어 사용하는 것과 어떤차이가 있을지를 아래코드로 보면 next를 await하는 방식으로 변하게 된다

var iterator = quakes.makeAsyncIterator()
while let quake = await iterator.next() {
    if quake.magnitude > 3 {
        displaySignificantEarthquake(quake)
    }
}

그리고 await의 경우엔 Task블럭에서 실행되어야하기 때문에

let iteration1 = Task {
    for await quake in quakes {
        if quake.magnitude > 3 {
            displaySignificantEarthquake(queke)
        }
    }
}

let iteration2 = Task {
    do {
        for try await quake in quakeDownload {
            if quake.depth > 5 { continue }
            if quake.location == nil { break }

            ...
        }
    } catch {

    }
}

iteration1.cancel()
iteration2.cancel()

이와같이 사용해야하고 error를 throw하는 경우엔 do catch문으로 error를 handling해줘야한다

Usage and APIs

스크린샷 2023-08-18 오후 6 54 06

그리고 또한 file handler를 통해 byte단위로 data를 호출할 수도 있고

스크린샷 2023-08-18 오후 6 55 14 처음에 봤던 예시처럼 line단위로 data를 호출할 수도 있다

Using URLSession

아래의 그림처럼 URLSession으로 byte단위로 데이터를 읽어올수있고 이를 for loop에서 sequence단위로 async하게 작동시킬수있다
스크린샷 2023-08-18 오후 6 56 10

Conform AsyncSequence

이 섹션에서는 나만의 AsyncSequence를 만드는 방법에 대해 알려줍니다.
응답할 필요가 없고 새로운 값이 발생한다는 것을 알려주는 거의 모든 것이 AsyncSequence를 만드는데 가장 적합한 후보가 될 수 있고 대표적인것이 핸들러 패턴입니다.

QuakeMonitor는 일반적인 핸들러 패턴의 예시이고 핸들러 속성과 시작 및 중지 메서드가 있는 클래스입니다. AsyncSequence를 적용하기에 가장적합한 후보라고합니다.
스크린샷 2023-08-18 오후 7 01 11
이것이 기존의 방식인데 아래와같이 바꿀수있습니다

class QuakeMonitor {
    var quakeHandler: (Quake) -> Void
    func startMonitoring()
    func stopMonitoring()
}

let quakes = AsyncStream(Quake.self) { continuation in
    let monitor = QuakeMonitor()
    monitor.quakeHandler = { quake in
        continuation.yield(quake)
    }
    continuation.onTermination = { @Sendable _ in
        monitor.stopMonitoring()
    }
    monitor.startMonitoring()
}

let significantQuakes = quakes.filter { quake in
    quake.magnitude > 3
}

for await quake in significantQuakes {
    ...
}
@kimscastle kimscastle self-assigned this Aug 18, 2023
@kimscastle kimscastle added WWDC21 WWDC 21년영상 Swift 주제 의성 labels Aug 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Swift 주제 WWDC21 WWDC 21년영상 의성
Projects
None yet
Development

No branches or pull requests

1 participant