Networking in iOS: URLSession Deep Dive
Networking is fundamental to modern iOS apps. Let’s explore URLSession in depth.
Basic GET Request
func fetchUsers() async throws -> [User] {
let url = URL(string: "https://api.example.com/users")!
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw NetworkError.invalidResponse
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return try decoder.decode([User].self, from: data)
}
POST Request with JSON
func createUser(name: String, email: String) async throws -> User {
let url = URL(string: "https://api.example.com/users")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let payload = CreateUserRequest(name: name, email: email)
request.httpBody = try JSONEncoder().encode(payload)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw NetworkError.invalidResponse
}
return try JSONDecoder().decode(User.self, from: data)
}
Custom Session Configuration
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.timeoutIntervalForResource = 60
configuration.waitsForConnectivity = true
let session = URLSession(configuration: configuration)
Upload with Progress
func uploadImage(data: Data, progress: @escaping (Double) -> Void) async throws -> String {
let url = URL(string: "https://api.example.com/upload")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let (responseData, response) = try await session.upload(for: request, from: data)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw NetworkError.uploadFailed
}
let result = try JSONDecoder().decode(UploadResponse.self, from: responseData)
return result.url
}
Error Handling
enum NetworkError: Error {
case invalidURL
case invalidResponse
case httpError(Int)
case decodingError
case noData
case timeout
case noConnection
var localizedDescription: String {
switch self {
case .invalidURL: return "Invalid URL"
case .invalidResponse: return "Invalid response from server"
case .httpError(let code): return "HTTP error: \(code)"
case .decodingError: return "Failed to decode response"
case .noData: return "No data received"
case .timeout: return "Request timed out"
case .noConnection: return "No internet connection"
}
}
}
Master URLSession and you’ll be able to handle any networking requirement in iOS.