Irbisa · Cheatsheet
iOS Networking
URLSession, Codable, async APIs, interceptors, error handling
What is REST, and how do
GET,POST,PUT,PATCHandDELETEand the HTTP status codes differ?What happens when an app makes an HTTP request, and how do you send one with
URLSession?// GETlet url = URL(string: "https://api.example.com/users")!let (data, response) = try await URLSession.shared.data(from: url)guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { throw URLError(.badServerResponse)}…What is the difference between
URLandURLRequest?var components = URLComponents(string: "https://api.example.com/users/42")!components.queryItems = [URLQueryItem(name: "fields", value: "name,avatar")]let url = components.url! // just the address var request = URLRequest(url: url) // address + how to send itrequest.httpMethod = "PATCH"…What is the difference between a data task, a download task and an upload task in
URLSession?What is App Transport Security, and how do you allow a domain that only speaks plain HTTP?
<key>NSAppTransportSecurity</key><dict> <key>NSExceptionDomains</key> <dict> <key>legacy-cdn.example.com</key> <dict>…This search returns nothing for
"C++ & Swift"while the same text works in a browser. What is wrong with how the URL is built?func search(_ term: String, page: Int) async throws -> Data { let url = URL(string: "https://api.example.com/search?q=\(term)&page=\(page)")! let (data, _) = try await URLSession.shared.data(from: url) return data} // search("C++ & Swift", page: 1)What are the
URLSessionConfigurationtypes (default,ephemeralandbackground), and when do you use each?This loader is called for every image in a feed. What is wrong with it, and what does it cost?
final class ImageLoader: NSObject, URLSessionDataDelegate { func load(_ url: URL) async throws -> Data { let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil) let (data, _) = try await session.data(from: url) return data }}How would you structure a networking layer on top of
URLSession, and which errors should it distinguish?protocol HTTPTransport: Sendable { func data(for request: URLRequest) async throws -> (Data, URLResponse)}extension URLSession: HTTPTransport { // explicit: a method with a defaulted `delegate:` does not satisfy the requirement func data(for request: URLRequest) async throws -> (Data, URLResponse) {…When would you use
URLSessiondirectly, and when a library like Alamofire?Decoding fails with "Expected date string to be ISO8601-formatted" although
created_atlooks like a normal ISO date. What doesJSONDecoderobject to?nonisolated(unsafe) private let fractional: ISO8601DateFormatter = { let f = ISO8601DateFormatter() f.formatOptions = [.withInternetDateTime, .withFractionalSeconds] return f}()nonisolated(unsafe) private let plain = ISO8601DateFormatter() // no fractions…The backend adds a new post type and the whole feed goes blank instead of skipping one row. How do you make decoding survive it?
enum PostKind: String, Decodable { case text, photo, video, unknown init(from decoder: Decoder) throws { let raw = try decoder.singleValueContainer().decode(String.self) self = PostKind(rawValue: raw) ?? .unknown // new server value -> unknown…How do you cancel network requests and handle timeouts in Swift?
// Cancel-previous typeahead with debounce@MainActorfinal class SearchModel { private var task: Task<Void, Never>? private let api: SearchAPI var results: [Item] = []…Find the bug: when the backend returns an error page, the profile screen spins forever instead of showing an error.
func fetchProfile(id: Int) async throws -> Profile { try await withCheckedThrowingContinuation { continuation in LegacyAPI.shared.get("/profiles/\(id)") { data, error in if let error { continuation.resume(throwing: error) return } guard let data, let profile = try? JSONDecoder().decode(Profile.self, from: data) else { return } continuation.resume(returning: profile) } }}The connection drops mid-request. How should the networking layer react, and why not check reachability before every call?
let config = URLSessionConfiguration.defaultconfig.waitsForConnectivity = true // queue rather than failconfig.timeoutIntervalForResource = 120 // but not foreverconfig.allowsConstrainedNetworkAccess = false // skip Low Data Modelet session = URLSession(configuration: config, delegate: MonitorDelegate(), delegateQueue: nil)…Which failed requests do you retry, and how do you retry without stampeding the server?
func withRetry<T>( attempts: Int = 3, base: Duration = .milliseconds(300), isRetryable: (Error) -> Bool = APIError.isTransient, operation: () async throws -> T) async throws -> T {…How do you cache HTTP responses on iOS, and what do
URLCache,Cache-ControlandETageach do?let config = URLSessionConfiguration.defaultconfig.urlCache = URLCache(memoryCapacity: 32 << 20, diskCapacity: 256 << 20)config.requestCachePolicy = .useProtocolCachePolicylet session = URLSession(configuration: config) // Explicit conditional request, when you keep the copy yourself…How do you upload a file as
multipart/form-datawithURLSession?func uploadAvatar(_ image: Data, name: String) async throws -> Data { let boundary = "Boundary-\(UUID().uuidString)" var body = Data() func append(_ s: String) { body.append(s.data(using: .utf8)!) } // Text field…How do you test code that calls URLSession without touching the network?
final class StubURLProtocol: URLProtocol { nonisolated(unsafe) static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? override class func canInit(with request: URLRequest) -> Bool { true } override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } override func stopLoading() {}…When would you use a WebSocket instead of HTTP requests, and what does a client built on
URLSessionWebSocketTaskneed?final class PriceSocket { private let session: URLSession private var task: URLSessionWebSocketTask? init(session: URLSession = .shared) { self.session = session }…How does token authentication work in an app, and how do you refresh the access token when many requests fail with 401 at once?
actor TokenStore { private(set) var accessToken: String? private var refreshTask: Task<String, Error>? private let refresh: @Sendable () async throws -> String // calls /auth/refresh directly init(accessToken: String?, refresh: @escaping @Sendable () async throws -> String) {…An export endpoint streams 400 MB of NDJSON and
data(for:)gets the app killed for memory on older phones. What do you call instead?struct Row: Decodable { let id: String; let amount: Decimal } func importRows(from url: URL, session: URLSession, store: RowStore) async throws { let (bytes, response) = try await session.bytes(from: url) guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { throw URLError(.badServerResponse) // check before consuming…What is certificate pinning, how do you implement it on iOS, and what can go wrong?
final class PinningDelegate: NSObject, URLSessionDelegate { private let pins: Set<Data> // SHA-256 of each pinned key: current + backup init(pins: Set<Data>) { self.pins = pins } func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge,…A two-gigabyte video must finish downloading even if the user leaves the app. How do you do it?
final class Downloader: NSObject, URLSessionDownloadDelegate, @unchecked Sendable { static let shared = Downloader() var backgroundCompletion: (() -> Void)? // touched on the main queue only // Creating it again after a relaunch reconnects to the system's transfers lazy var session: URLSession = {…Auth, logging, retry and metrics all have to wrap every request without turning the client into a god object. How do you compose them?
typealias Send = @Sendable (URLRequest) async throws -> (Data, HTTPURLResponse) protocol HTTPMiddleware: Sendable { func intercept(_ request: URLRequest, next: Send) async throws -> (Data, HTTPURLResponse)}…