How can I make a function complete before calling others in an IBAction?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In an IBAction, calling one function after another only guarantees order for synchronous code. If the first function starts asynchronous work such as a network call, animation, or database request, the next line runs immediately unless you explicitly wait through a completion handler or async and await. The correct fix is to model the first operation as asynchronous, not to block the main thread.
First Identify Whether the Function Is Synchronous
If the first function is truly synchronous, then Swift already waits for it to finish before the next line executes.
Here updateUI() runs only after validateFields() returns. No extra control flow is needed.
The real problem appears when the first function starts asynchronous work and returns before that work finishes.
Use a Completion Handler for Callback-Based APIs
If your function finishes later, give it a completion closure and call the next step inside that closure.
This is the classic pattern for UIKit-era APIs. The second action runs only after the async work completes.
Prefer async and await in Modern Swift
If you control the async function and your deployment target supports it, async and await usually make the code much clearer.
This preserves readable top-to-bottom logic without nesting closures deeply.
Do Not Block the Main Thread
A common wrong idea is to "wait" inside the IBAction by using semaphores, sleep calls, or busy waiting. That freezes the UI and can deadlock if the async completion also needs the main thread.
Avoid patterns like:
or:
inside UI event handlers. UIKit expects the main thread to stay responsive.
Chain Multiple Steps Explicitly
If one action depends on another and then a third step depends on both, keep the dependency order explicit.
With completions:
With async and await:
The second style is usually easier to maintain once the project uses Swift concurrency consistently.
Decide Where the State Change Belongs
Sometimes the real issue is not waiting for a function. It is deciding when UI should change. For example, if a button starts a network request, you may want to:
- disable the button immediately
- show a loading state
- re-enable the button only in completion or after
await
That makes the sequencing visible to the user and prevents double taps from starting duplicate work.
Common Pitfalls
- Assuming the next line in an
IBActionwaits for asynchronous work automatically. - Blocking the main thread with sleep calls or semaphores.
- Calling UI updates from a background queue instead of the main thread.
- Nesting too many completion handlers without extracting helper methods.
- Mixing callback style and
asyncplusawaitinconsistently in the same flow.
Summary
- Synchronous functions already finish before the next line runs.
- Asynchronous functions need an explicit completion handler or
asyncplusawait. - In modern Swift,
asyncplusawaitis usually the clearest option. - Never block the main thread just to force order inside an
IBAction. - Model the actual dependency chain directly so UI updates happen at the correct time.

