Callback function syntax in Swift
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Swift, what most people call a callback is usually a closure passed into a function. Understanding the syntax matters because the same feature shows up in completion handlers, asynchronous APIs, error handling, animation blocks, and many other parts of Swift code.
The Basic Closure Type
A callback that takes no arguments and returns nothing has this type:
A function can accept it like this:
This is the simplest form of callback syntax in Swift.
Callbacks with Parameters
Most real callbacks pass information back to the caller.
The closure type here is:
That means the callback receives a String and returns nothing.
Callbacks with Success and Failure Information
A common older Swift pattern is to pass optional result and optional error values into a completion handler.
This works, but modern Swift usually prefers Result because it is clearer.
Prefer Result for Typed Outcomes
This makes the contract explicit: the callback returns either a success value or a failure value, but not both at once.
Mark Escaping Closures Correctly
If the callback is stored or called after the function returns, Swift requires @escaping.
Without @escaping, the compiler assumes the closure is used only during the function call itself.
Trailing Closure Syntax
Swift supports trailing closures when the final argument is a closure. That is why callbacks often look like this:
instead of this:
Both are valid. The first is just more idiomatic when the closure is the last argument.
Async and Await Reduce Callback Noise
Modern Swift increasingly replaces callback-heavy APIs with async and await.
Even so, callback syntax still matters because many Apple and third-party APIs still use closures, and async code is often built on top of them.
Avoid Retain Cycles in Stored Callbacks
If a closure captures self and is retained by self, you can create a retain cycle. In those cases use a capture list.
This is not specific to callbacks, but callback-based designs hit this issue often enough that it belongs in the syntax discussion.
Common Pitfalls
The most common mistake is forgetting @escaping when the callback runs later. The compiler will usually catch this, but the reason matters.
Another issue is using the older optional-value plus optional-error pattern when Result would express the contract more clearly.
A third problem is misunderstanding trailing closure syntax and thinking it changes the meaning of the callback. It does not. It only changes how the call is written.
Summary
- In Swift, callbacks are usually closures passed as function parameters.
- A simple callback type looks like
() -> Voidor(Value) -> Void. - Use
@escapingwhen the callback outlives the function call. - Prefer
Resultover parallel optional success and error parameters. - Learn trailing closure syntax because it is the common style in Swift APIs.

