iljawascoding
iljawascoding

Reputation: 1110

How to implement a function returning an AsyncSequence, whose results relies on another AsyncSequence

I'm trying to implement a function which returns an AsyncStream of BlurredImage objects. The functions relies on another function sourceImages(), which itself is an AsyncStream.

I get this error on line 2 of my snippet:

Cannot pass function of type '(AsyncStream<BlurredImage>.Continuation) async -> Void' to parameter expecting synchronous function type

What's the correct way to implement this?

    func blurredFaces() -> AsyncStream<BlurredImage> {
        return AsyncStream<BlurredImage> { continuation in
            for await image in sourceImages() {
                blurrImage(image) { blurredImage in
                    continuation.yield(blurredImage)
                }
            }
            continuation.finish()
        }
    }

    func sourceImages() -> AsyncStream<SourceImage> {
    ...
    }

I'm on Xcode 13.4.1

Upvotes: 1

Views: 1363

Answers (2)

bzyr
bzyr

Reputation: 457

AsyncStream.init expects an input of type (AsyncStream<BlurredImage>.Continuation) → Void, which is not async.

But you can wrap its body in a Task to do async operations.

return AsyncStream<BlurredImage> { continuation in
    Task {
        for await image in sourceImages() {
            blurrImage(image) { blurredImage in
                continuation.yield(blurredImage)
            }
        }
        continuation.finish()
    }
}

Or as @Sweeper suggested, you can use map. Based on your blurredFaces return type signature, you want to map AsyncStream<SourceImage> (result of sourceImages) to AsyncStream<BlurredImage>.

In case for some reason you want to keep its return type as that, I haven't found a built-in direct map from an AsyncStream<T1> to an AsyncStream<T2>, so I posted this related SO question. In this answer, I shared the AsyncStream extension that I created for this use case. I am still waiting for feedbacks for this solution.

Upvotes: 3

Sweeper
Sweeper

Reputation: 271420

You seem to be doing an mapping operation here. In that case, you can use map and return an AsyncMapSequence:

func blurredFaces() -> AsyncMapSequence<AsyncStream<SourceImage>, BlurredImage> {
    sourceImages().map { image in
        await withCheckedContinuation { continuation in
            blurrImage(image) { blurred in
                continuation.resume(returning: blurred)
            }
        }
    }
}

If you can make blurrImage async, then this can be even shorter:

func blurredFaces() -> AsyncMapSequence<AsyncStream<SourceImage>, BlurredImage> {
    // you can even inline this, and get rid of blurredFaces
    sourceImages().map(blurrImage)
}

Upvotes: 2

Related Questions