Concurrency & Asynchronous Programming in Swift

May 20 2025 · Swift 6, iOS 18, Xcode 16

Lesson 04: Using AsyncStream

Demo

Episode complete

Play next episode

Next
Transcript

In this demo, you’ll update TheMet app to use an AsyncStream to update the UI and show the search results.

Open TheMet app in the Starter folder.

Open ContentView, you’ll notice there’s a @State property called objects, and another property for TheMetStore object.

Scroll down until you see the .onAppear() modifier. You’ll see the modifier is assigning newObjectsHandler in TheMetStore a closure that updates the UI with a new object. Take a look at what TheMetStore is doing.

TheMetStore file is providing updates on fetched objects via an optional closure. This approach is working, but you’re looking to upgrade the app to deliver events using Swift Concurrency. This is a good case to use AsyncStream.

In TheMetStore, add a method returning an AsyncStream.

func fetchObjects(for queryTerm: String) -> AsyncStream<Object> {

  return AsyncStream { continuation in
    let task = Task {
      if let objectIDs = try await self.service.getObjectIDs(from: queryTerm) {

        for (index, objectID) in objectIDs.objectIDs.enumerated()
        where index < self.maxIndex {
          if let object = try await self.service.getObject(from: objectID) {
            continuation.yield(object)
          }
        }
      }
    }

    continuation.onTermination = { _ in
      print("Task is cancelled")
      task.cancel()
    }
  }
}

Now, when the getObject on TheMetService returns, the object is yielded to the stream via the continuation.

Back in ContentView, update the view to use the new method. First, update the .task modifier to use the method when the view appears:

.task {
  do {
    for await object in store.fetchObjects(for: query) {
      objects.append(object)
    }
  }
}

Then, update the fetchObjectsTask to use the method as well:

fetchObjectsTask = Task {
  do {
    objects = []
    for await object in store.fetchObjects(for: query) {
      objects.append(object)
    }
  }
}

With the code now using AsyncStream, there’s no need to set the handler when the view appears. Remove it.

Run the app again and perform a search. Notice the app works just like before.

If you perform another search, it also works just like before.

See forum comments
Cinema mode Download course materials from Github
Previous: Instruction Next: Conclusion