Guide
Fetching Data with URLSession and Returning Result<Data, Error> in Swift
Learn how to wrap a URLSession GET request in a Swift function that returns Result<Data, Error>, validates HTTP status, handles errors, and provides recovery strategies.
Published by Tasadduq Burney
25 Aug 2025, 06:36 UTC
3 min66.4K views0

Desired outcome
Write a reusable function that performs a GET request with URLSession, validates the HTTP response, and delivers either the response data or an error through a Result value supplied to a completion handler.
Prerequisites
- Swift 5 or later (the
Resulttype is part of the standard library). - Xcode 11 or newer (provides the required SDK).
- Import
Foundationto accessURLSessionand related types. - The caller must supply a valid
URLand be prepared to handle the completion on a background thread (the delegate queue of the session).
Focused procedure
- Create a
URLRequestfrom the supplied URL, optionally configuring timeout or cache policy. - Instantiate a data task with
URLSession.shared.dataTask(with:request)and provide a completion handler that receivesdata,response, anderror. - If the
errorparameter is non‑nil, forward it as.failure(error)and exit the handler. - Attempt to downcast
responsetoHTTPURLResponse. If the cast fails or the status code is outside the range 200‑299, complete with.failure(URLError(.badServerResponse)). - Guard against a nil
datapayload; if missing, complete with the same.badServerResponseerror. - When all checks succeed, call the completion with
.success(data). - Finally, invoke
task.resume()to start the request.
import Foundation
func fetchData(from url: URL,
completion: @escaping (Result) -> Void) {
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.timeoutInterval = 30.0
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let httpResponse = response as? HTTPURLResponse else {
completion(.failure(URLError(.badServerResponse)))
return
}
guard (200...299).contains(httpResponse.statusCode) else {
completion(.failure(URLError(.badServerResponse)))
return
}
guard let data = data else {
completion(.failure(URLError(.badServerResponse)))
return
}
completion(.success(data))
}
task.resume()
}
Expected checks and recovery options
- Status‑code validation: The helper treats any HTTP response outside 200‑299 as a failure; you may adjust the range to accept redirects or other success codes if your API requires it.
- Missing data: A nil
datawith a successful status code is still considered an error (.badServerResponse) because the contract expects a payload. - Transient errors: Errors such as
URLError.timedOutor.networkConnectionLostcan be retried by re‑invokingfetchDataafter a short delay or with exponential back‑off. - Fallback values: Before propagating a failure, you may attempt to read a cached response (e.g., from
URLCache) and return that data as a.successif staleness is acceptable. - Threading: The completion runs on URLSession’s internal delegate queue. If you need to update UI, dispatch to
DispatchQueue.maininside the completion or at the call site.
Verification (no test execution claimed)
- Unit‑test approach: inject a mock
URLSession(via dependency injection or protocol) that returns predefinedDataorURLErrorinstances; assert that the completion receives the matchingResultcase. - Integration check: call the function against a known endpoint such as
https://httpbin.org/getand verify that theResultis.successwith non‑emptyDataand a status code in the 200‑299 range. - Error‑path check: disconnect the network or request an invalid host (e.g.,
https://nonexistent.invalid) and confirm that the completion yields a.failurewhose error domain isURLError.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.