How do you make a simple GET request using `URLSession` with `async`/`await`?
iOS interview question for Intermediate practice.
Answer
Making a simple HTTP GET request using URLSession with Swift's modern async/await concurrency is quite straightforward: Steps: 1. Import Foundation: Ensure Foundation is imported (usually already there). 2. Create URL: Create a URL object for the endpoint you want to fetch. 3. Call URLSession.shared.data(from:): Use the shared URLSession instance and call its data(from:delegate:) method. Since this method is async and throws, you call it using try await. 4. Receive Result: await returns a tuple containing (Data, URLResponse) if the request is successful. 5. Check Response: Cast the URLResponse to HTTPURLResponse and check its statusCode (e.g., ensure it's 200). 6. Process Data: If the response is valid, process the received Data (e.g., decode JSON, convert to String). 7. Handle Errors: Wrap the call in a do-catch block to handle potential errors (invalid URL, network connection issues, non-200 status codes). Code Example: swift import Foundation // Define potential errors enum FetchError: Error { case invalidURL case networkError(Error) case invalidResponse(statusCode: Int) case invalidData } // Define a Codable struct for the expected JSON response struct Post: Codable, Identifiable { let userId: Int let id: Int let title: String let body: String } // Async function to perform the GET request func fetchPosts() async throws - [Post] { // 1. Create URL guard let url = URL(string: "[https://jsonplaceholder.typicode.com/posts](https://jsonplaceholder.typicode.com/posts)") else { throw FetchError.invalidURL } print("Fetching posts...") do { // 2. Call data(from:) using try await let (data, response) = try await URLSession.shared.data(from: url) // 3. Check Response Status Code guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1 throw FetchError.invalidResponse(statusCode: statusCode) } // 4. Process (Decode) Data do { let decoder = JSONDecoder() let posts = try decoder.decode([Post].self, from: data) print("Successfully fetched and decoded \(posts.count) posts.") return posts } catch { throw FetchError.decodingError(error) } } catch let error as FetchError { // Re-throw specific FetchError throw error } catch { // Wrap other potential errors (e.g., network connection) throw FetchError.networkError(error) } } // --- Example Usage (e.g., in a SwiftUI Task or other async context) --- / Task { do { let fetchedPosts = try await fetchPosts() // Use the posts... } catch { print("Error in task: \(error)") } } / This async/await approach simplifies asynchronous network calls significantly compared to older completion handler patterns.
Explanation
The data(from:delegate:) method returns a tuple (Data, URLResponse). It's important to check the URLResponse (casting it to HTTPURLResponse) to verify the HTTP status code (e.g., 200 OK) before processing the Data.