Can I somehow do a synchronous HTTP request via NSURLSession in Swift
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
URLSession does not offer a normal synchronous API, and that is intentional. Network I/O is slow and unpredictable, so Apple’s networking stack is built around asynchronous execution rather than blocking the calling thread.
If what you really want is sequential-looking code, modern Swift already gives you that through async and await. In most cases, that is the right replacement for the old idea of a synchronous request.
Use the standard asynchronous API
The classic URLSession style uses a completion handler:
This is the design the framework expects. The call returns immediately, and the response arrives later through the callback.
Prefer async and await in modern Swift
If you want code that reads top to bottom, use Swift concurrency instead of trying to force URLSession into a blocking API.
This feels synchronous from the caller's point of view, but it does not block the main thread.
You can block a thread, but it is a workaround
If you truly must bridge async networking into a blocking API, a semaphore can do it. This is usually a legacy workaround and should not be your default design.
This blocks the current thread until the request finishes. It should never be used on the main thread of an iOS app.
Why synchronous blocking is usually the wrong goal
Blocking network code creates several problems:
- the UI can freeze
- cancellation becomes awkward
- timeouts and retries become harder to model
- deadlocks become easier to introduce in wrapper code
The requirement often turns out to be "I want simple control flow," not "I want to block a thread." async and await solve the first problem without creating the second.
When a blocking wrapper may be acceptable
In a small command-line tool or test harness, blocking a background thread can be tolerable. In an iOS app, especially in UI code, it is almost always the wrong tradeoff.
If older APIs in your codebase expect a synchronous result, the better long-term fix is usually to push the async boundary upward and let callers become async too.
Common Pitfalls
The most common mistake is using a semaphore-based wrapper on the main thread. That can freeze the app and trigger watchdog termination.
Another issue is treating "sequential-looking code" as proof that you need synchronous networking. In Swift, structured concurrency already gives you sequential control flow without blocking.
Developers also sometimes expose blocking networking APIs from reusable libraries. That makes it harder for callers to cancel work or integrate with concurrency later.
Finally, do not forget that URLSession callbacks and async APIs already handle networking in the intended platform model. Fighting that design usually makes the code worse, not simpler.
Summary
- '
URLSessionis designed for asynchronous networking, not synchronous blocking.' - Use completion handlers or, preferably,
asyncandawaitfor sequential-looking code. - A semaphore can simulate synchronous behavior, but it is a workaround.
- Never block the main thread waiting for a network response in an iOS app.
- If synchronous networking seems necessary, reconsider the API boundary first.

