iOS
sendAsynchronousRequest
iOS Development
Networking
bug fixing

IOS Issues with sendAsynchronousRequest

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

sendAsynchronousRequest from NSURLConnection belongs to an older iOS networking style and is a common maintenance problem in legacy codebases. The main issue is not only that it is deprecated, but also that modern apps usually need clearer control over response validation, cancellation, threading, and testability than this API encourages.

What the Legacy API Looks Like

A typical old Objective-C pattern looks like this:

objective-c
1[NSURLConnection sendAsynchronousRequest:request
2                                   queue:[NSOperationQueue mainQueue]
3                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
4    if (error) {
5        NSLog(@"error: %@", error);
6        return;
7    }
8
9    NSLog(@"received %lu bytes", (unsigned long)[data length]);
10}];

It looks simple, but production networking code almost always needs more structure than a one-shot completion block.

Why It Causes Trouble

Several problems appear repeatedly in code built around this API:

  • UI updates and network logic are mixed together
  • non-nil data is treated as success without checking HTTP status
  • cancellation and timeout behavior are weakly modeled
  • request configuration becomes scattered across controllers
  • the API is deprecated, so newer platform idioms do not build around it

In other words, the problem is often architectural as much as syntactic.

Move to URLSession

The normal replacement is URLSession, which gives you explicit request tasks and a cleaner structure.

swift
1import Foundation
2
3final class APIClient {
4    private let session: URLSession
5
6    init(session: URLSession = .shared) {
7        self.session = session
8    }
9
10    func fetchText(from url: URL, completion: @escaping (Result<String, Error>) -> Void) {
11        let task = session.dataTask(with: url) { data, response, error in
12            if let error {
13                completion(.failure(error))
14                return
15            }
16
17            guard let http = response as? HTTPURLResponse,
18                  200..<300 ~= http.statusCode else {
19                completion(.failure(NSError(domain: "HTTP", code: 1)))
20                return
21            }
22
23            let text = String(decoding: data ?? Data(), as: UTF8.self)
24            completion(.success(text))
25        }
26
27        task.resume()
28    }
29}

That is already easier to reason about than the old helper-style call.

Be Explicit About Main-Thread Work

Network callbacks are not UI code. If the result updates the interface, make the handoff explicit.

swift
1client.fetchText(from: url) { result in
2    DispatchQueue.main.async {
3        switch result {
4        case .success(let text):
5            print(text)
6        case .failure(let error):
7            print(error)
8        }
9    }
10}

That makes the threading model visible instead of accidental.

Validate the Response, Not Just the Data

One of the classic bugs in legacy networking code is assuming that non-nil data means success. But HTTP error responses often still come with a body.

That is why the response should be cast to HTTPURLResponse and checked by status code before the payload is treated as valid application data.

Cancellation and Timeouts Matter

Modern networking usually needs request lifetime control. URLSession gives you that directly.

swift
1let config = URLSessionConfiguration.default
2config.timeoutIntervalForRequest = 15
3config.timeoutIntervalForResource = 30
4let session = URLSession(configuration: config)

And cancellation is explicit:

swift
1let task = session.dataTask(with: url) { _, _, _ in }
2task.resume()
3
4task.cancel()

That is far better than hoping an old request simply becomes irrelevant without side effects.

Common Pitfalls

The most common mistake is swapping in URLSession but keeping the same weak error handling as before.

Another mistake is leaving network code buried inside view controllers, which preserves the same design problems under a new API name.

It is also easy to forget explicit main-thread dispatch for UI updates after asynchronous work.

Summary

  • 'sendAsynchronousRequest is deprecated and awkward for modern iOS networking.'
  • 'URLSession is the right replacement for most legacy code.'
  • Always validate HTTP response status, not just the presence of returned data.
  • Handle threading, cancellation, and timeout behavior explicitly.
  • Use migration as a chance to improve network structure, not just rename APIs.

Course illustration
Course illustration

All Rights Reserved.