How to returning data from a void async call in Swift function
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
You cannot synchronously return data from a function whose result is produced later by asynchronous work. In Swift, the correct answer is to change the API shape: either use a completion handler that receives the value later, or use async and return the value from an async function.
Why a Void Async Callback Cannot Return Immediately
Suppose you start a network request inside a function and try to return the result directly. That fails because the function ends before the asynchronous operation completes.
This is the wrong mental model:
The callback runs later, so result is returned too early.
Use a Completion Handler in Older or Callback-Based Code
A completion handler is the classic Swift pattern:
Now the caller receives the data at the correct time instead of pretending it was available immediately.
Use async and await in Modern Swift
In modern Swift, the cleaner version is to make the function itself async and give it a real return type:
The caller then uses:
This is still asynchronous, but the code reads more like a direct return value flow.
Choose the API Shape That Matches the Codebase
A practical rule is:
- use completion handlers when integrating with older callback-based APIs
- use
asyncandawaitfor modern Swift code where you control the function signature
The key design point is that asynchronous data must be delivered asynchronously. There is no safe trick that turns it back into a true immediate return value.
Avoid Shared Mutable State as a Workaround
Developers sometimes try to store the result in a property and read it later. That may be valid in some architectures, but it is not a replacement for a proper async API contract. Shared mutable state usually hides timing issues rather than solving them.
Bridge Old and New Styles Carefully
If you are migrating from completion handlers to async and await, keep the boundary explicit. One function can wrap the older callback-based API, but the caller should still see a clear asynchronous return path.
The Caller Must Participate
Returning data from async work is not only an implementation detail inside the function. The caller must also adopt an asynchronous pattern, whether that means using a callback, await, or task coordination.
Common Pitfalls
- Trying to return a value before the asynchronous callback has executed.
- Filling a local variable inside a callback and expecting the outer function to wait automatically.
- Keeping a
Voidfunction signature when the real API should communicate a result asynchronously. - Mixing completion handlers and
asynccode without a clear boundary. - Using shared state as a substitute for a proper asynchronous return path.
Summary
- You cannot synchronously return data from asynchronous work that finishes later.
- Use a completion handler in callback-based Swift code.
- Use
asyncandawaitwhen you can define an async function that returns the value. - Change the function contract instead of fighting the timing model.
- Treat asynchronous results as asynchronous all the way to the caller.

