0
0
iOS Swiftmobile~15 mins

GET request with async/await in iOS Swift - Deep Dive

Choose your learning style9 modes available
Overview - GET request with async/await
What is it?
A GET request is a way for an app to ask a server for data, like fetching a list of photos or user info. Async/await is a modern way in Swift to write code that waits for this data without freezing the app. It makes the code easier to read and keeps the app smooth while waiting for the server.
Why it matters
Without async/await, apps can freeze or become unresponsive while waiting for data, making users frustrated. Async/await solves this by letting the app do other things while waiting, improving user experience and making code simpler to write and understand.
Where it fits
Before learning this, you should know basic Swift syntax and how to use functions. After this, you can learn how to handle errors in network calls, parse JSON data, and update the app's user interface with the fetched data.
Mental Model
Core Idea
Async/await lets your app ask for data and wait for the answer without stopping everything else from working.
Think of it like...
It's like ordering food at a restaurant and waiting at your table instead of standing at the counter; you can chat or check your phone while the kitchen prepares your meal.
┌───────────────┐
│ Start GET call│
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Await response│
│ (app stays    │
│ responsive)   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Process data  │
└───────────────┘
Build-Up - 6 Steps
1
FoundationUnderstanding GET requests basics
🤔
Concept: Learn what a GET request is and why apps use it to get data from servers.
A GET request asks a server to send back some information. For example, your app might ask for a list of books or weather data. This is done by sending a URL to the server, and the server replies with the data.
Result
You understand that GET requests are how apps fetch data from the internet.
Knowing what a GET request does helps you see why apps need to communicate with servers to show fresh information.
2
FoundationBasics of async/await in Swift
🤔
Concept: Introduce async/await as a way to write code that waits for tasks without freezing the app.
Async means a function can pause and wait for something, like data from the internet. Await means 'wait here until the task finishes.' Swift lets you mark functions as async and use await inside them to pause without blocking the app.
Result
You can write simple async functions that pause and resume smoothly.
Understanding async/await lets you write clearer code that handles waiting without making the app freeze.
3
IntermediateMaking a GET request with async/await
🤔Before reading on: do you think async/await automatically handles errors for you? Commit to yes or no.
Concept: Combine GET requests with async/await to fetch data cleanly and handle waiting.
Use Swift's URLSession.shared.data(from:) method with await to get data from a URL. This pauses the function until the data arrives, then you can use it. Example: func fetchData() async throws { let url = URL(string: "https://example.com/data.json")! let (data, _) = try await URLSession.shared.data(from: url) print(String(data: data, encoding: .utf8) ?? "No data") }
Result
The app fetches data from the internet without freezing, and you get the data to use.
Knowing how to combine async/await with URLSession makes network calls simple and readable.
4
IntermediateHandling errors in async GET requests
🤔Before reading on: do you think network errors crash the app automatically? Commit to yes or no.
Concept: Learn to catch and handle errors that happen during async GET requests to keep the app stable.
Network calls can fail if the internet is down or the server is unreachable. Use try and catch to handle these errors gracefully: func fetchData() async { do { let url = URL(string: "https://example.com/data.json")! let (data, _) = try await URLSession.shared.data(from: url) print(String(data: data, encoding: .utf8) ?? "No data") } catch { print("Failed to fetch data: \(error)") } }
Result
The app stays stable and shows an error message if the network call fails.
Handling errors prevents crashes and improves user experience by managing failures smoothly.
5
AdvancedUpdating UI after async GET request
🤔Before reading on: do you think you can update UI directly inside async functions? Commit to yes or no.
Concept: Learn how to update the app's user interface after getting data asynchronously, respecting SwiftUI or UIKit rules.
UI updates must happen on the main thread. After fetching data, switch to the main thread to update UI: func fetchAndDisplay() async { do { let url = URL(string: "https://example.com/data.json")! let (data, _) = try await URLSession.shared.data(from: url) let text = String(data: data, encoding: .utf8) ?? "No data" await MainActor.run { self.labelText = text } } catch { print("Error: \(error)") } }
Result
The app shows the fetched data on screen without threading issues.
Knowing to update UI on the main thread avoids bugs and keeps the app responsive.
6
ExpertOptimizing GET requests with async/await
🤔Before reading on: do you think multiple async GET requests run in parallel by default? Commit to yes or no.
Concept: Explore how to run multiple GET requests at the same time and handle their results efficiently.
By default, awaiting one request waits for it to finish before starting another. Use async let to start requests concurrently: func fetchMultiple() async throws { async let first = URLSession.shared.data(from: URL(string: "https://example.com/one.json")!) async let second = URLSession.shared.data(from: URL(string: "https://example.com/two.json")!) let (data1, _) = try await first let (data2, _) = try await second print("First: \(data1.count) bytes, Second: \(data2.count) bytes") }
Result
Multiple requests run at the same time, reducing total wait time.
Understanding concurrency with async let lets you optimize network calls for better performance.
Under the Hood
Async/await in Swift uses a system called continuations. When the code hits an await, it pauses the current function and frees the thread to do other work. Once the awaited task finishes, Swift resumes the function from where it left off. This avoids blocking the main thread, keeping the app responsive.
Why designed this way?
Before async/await, Swift used callbacks or completion handlers, which made code complex and hard to read. Async/await was introduced to simplify asynchronous code, making it look like normal sequential code while still being non-blocking. This design improves developer productivity and app performance.
┌───────────────┐
│ Start async   │
│ function      │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Hit await     │
│ (pause func)  │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Thread freed  │
│ for other work│
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Task finishes │
│ (resume func) │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: does async/await automatically retry failed network requests? Commit to yes or no.
Common Belief:Async/await automatically retries network requests if they fail.
Tap to reveal reality
Reality:Async/await only manages waiting for tasks; it does not retry failed requests unless you write code to do so.
Why it matters:Assuming automatic retries can cause apps to silently fail or behave unpredictably when network errors occur.
Quick: can you update UI from any thread inside async functions? Commit to yes or no.
Common Belief:You can update the user interface from any thread inside async functions.
Tap to reveal reality
Reality:UI updates must happen on the main thread; updating UI from background threads causes bugs or crashes.
Why it matters:Ignoring this leads to app crashes or UI glitches that are hard to debug.
Quick: do multiple awaits run concurrently by default? Commit to yes or no.
Common Belief:Multiple await calls run at the same time automatically.
Tap to reveal reality
Reality:Await calls run sequentially unless you explicitly start tasks concurrently using async let or Task groups.
Why it matters:Misunderstanding this causes slower apps because network calls wait unnecessarily.
Quick: does async/await replace all asynchronous programming patterns? Commit to yes or no.
Common Belief:Async/await replaces all older asynchronous patterns like callbacks and completion handlers.
Tap to reveal reality
Reality:Async/await simplifies many cases but some legacy APIs or complex patterns still require callbacks or other methods.
Why it matters:Expecting async/await everywhere can cause confusion when working with older code or libraries.
Expert Zone
1
Async/await in Swift integrates with structured concurrency, which helps manage task lifetimes and cancellation cleanly.
2
Using async let for concurrency requires careful error handling because one failing task cancels others in the same scope.
3
URLSession's data(from:) method supports async/await starting from iOS 15, so older versions need fallback approaches.
When NOT to use
Avoid async/await if you must support iOS versions before 15 or when working with APIs that do not support async/await. In those cases, use completion handlers or third-party libraries like Combine or RxSwift.
Production Patterns
In real apps, async/await is used with structured concurrency to fetch multiple resources in parallel, handle errors gracefully, and update UI on the main thread. Developers often wrap network calls in reusable functions and use models to parse JSON data after fetching.
Connections
Structured Concurrency
Builds-on
Understanding async/await helps grasp structured concurrency, which organizes multiple async tasks safely and predictably.
Event Loop (Computer Science)
Same pattern
Async/await relies on the event loop concept, where tasks pause and resume without blocking the main thread, similar to how browsers handle JavaScript events.
Restaurant Order System
Metaphor
The async/await pattern mirrors how orders are taken and prepared in a restaurant, helping understand waiting without blocking other activities.
Common Pitfalls
#1Freezing the app by calling async code synchronously.
Wrong approach:let data = try URLSession.shared.data(from: url).get() // blocking call on main thread
Correct approach:let (data, _) = try await URLSession.shared.data(from: url) // non-blocking async call
Root cause:Misunderstanding that async calls must be awaited and not forced to run synchronously on the main thread.
#2Updating UI from a background thread after async call.
Wrong approach:func fetch() async { let (data, _) = try await URLSession.shared.data(from: url) self.label.text = String(data: data, encoding: .utf8) // UI update off main thread }
Correct approach:func fetch() async { let (data, _) = try await URLSession.shared.data(from: url) await MainActor.run { self.label.text = String(data: data, encoding: .utf8) } }
Root cause:Not switching to the main thread before updating UI causes crashes or visual bugs.
#3Running multiple awaits sequentially when parallelism is needed.
Wrong approach:let (data1, _) = try await URLSession.shared.data(from: url1) let (data2, _) = try await URLSession.shared.data(from: url2)
Correct approach:async let data1 = URLSession.shared.data(from: url1) async let data2 = URLSession.shared.data(from: url2) let (d1, _) = try await data1 let (d2, _) = try await data2
Root cause:Not using async let or Task groups to run tasks concurrently leads to slower performance.
Key Takeaways
GET requests ask servers for data and are essential for apps to show fresh information.
Async/await lets your app wait for data without freezing, making code easier to read and apps smoother.
Always handle errors in async calls to keep your app stable and user-friendly.
Update the user interface only on the main thread after async tasks complete to avoid crashes.
Use concurrency features like async let to run multiple network requests in parallel and improve performance.