Home / Blog / From Combine to async/await: a six-week code diff

From Combine to async/await: a six-week code diff

I moved an iOS app from Combine to async/await. What I gained, what I lost, with real code numbers.

Combine shipped in iOS 13 (2019) as Apple’s answer to reactive programming. For four or five years it was my default on iOS projects too.

In 2024 I started a new project on async/await and began migrating the Combine code in an older project on the side. Six weeks in, the codebase is noticeably more readable and easier to maintain. But async/await isn’t the right tool everywhere.

Where async/await wins

1. Network calls. The biggest win. One network call in Combine took a Publisher, map, tryMap, subscribe, sink, and an AnyCancellable stored somewhere. In async/await it’s three lines.

Combine version: URLSession’s dataTaskPublisher, a tryMap for decoding, a catch for errors, receive(on:) to get back on the main thread, a sink, and keeping the returned cancellable alive in a store.

Async/await version: let (data, _) = try await URLSession.shared.data(from: url) plus a decoder. That’s it.

2. Sequential operations. Three API calls in sequence is a flatMap chain in Combine, unnecessarily tangled. In async/await you write it like normal await lines.

3. Error handling. Every Combine operator has its own error type and you end up wrapping things in tryMap. Async/await uses Swift’s native throw/catch.

4. Debuggability. Stack traces actually look right. Tracking an error through Combine meant bouncing across five or six operators.

Where Combine still wins

Async/await didn’t solve everything. Combine is still the better tool for:

1. Event streams. User types in a text field, you fire a search 500ms later. In Combine that’s debounce, throttle, removeDuplicates in five lines. In async/await you roll it yourself with Task cancellation and Task.sleep.

2. Combining multiple data sources. Publishers.CombineLatest merges three publishers and gives you their latest values. You can do it with TaskGroup in async/await, but it’s a lot more verbose.

3. Reactive UI outside SwiftUI. On UIKit projects Combine is still the standard, with @Published properties and bindings.

4. Teams coming from RxSwift. Combine maps closely to the Rx mental model. If your team is comfortable with it, forcing a switch to async just drops productivity.

My migration strategy

Over six weeks I moved in this order:

Weeks 1 to 2: new code in async/await, old Combine code untouched. Both worlds coexist.

Weeks 3 to 4: migrate the network layer. URLSession.dataTaskPublisher out, URLSession.data in. ViewModels start exposing async functions.

Weeks 5 to 6: ViewModels swap @Published properties for the @Observable macro (iOS 17+) or plain properties. Tasks start from user actions.

What I kept in Combine: search debounce and keyboard events. I left those deliberately because async/await would be uglier.

Codebase size comparison

After six weeks:
– Combine imports: 24 down to 4
– AnyCancellable arrays: 18 down to 2
– Total LOC: down about 12% (same functionality)
– New-dev onboarding time: down 40% (as far as I can measure it)

But the biggest win isn’t measurable: reading code, “what is this doing?” takes two seconds instead of thirty.

Which projects is it right for?

Starting a new iOS project with an iOS 15+ target, async/await is the default. Keep Combine for genuinely reactive streams.

On an existing Combine-heavy project, skip the aggressive rewrite and move in stages:
1. New code in async/await
2. Migrate the network layer
3. Have ViewModels expose async APIs
4. Reactive UI bindings (search, keyboard) last, or never

Six weeks was enough for a 3000-LOC project. On a larger codebase it’s a 3 to 6 month job.

A popular take that’s misleading

On Twitter you see “Combine is dead, async/await won” a lot. Half true, mostly misleading. Apple isn’t deprecating Combine, just not putting it in new frameworks. Your existing code keeps working, and there’s a better default for new code.

Stay agnostic, pick per situation. Don’t migrate code to chase blog hype, migrate it for your team’s productivity.

Have a project on this topic?

Leave a brief summary — I’ll get back to you within 24 hours.

Get in touch