Client HTTP en Swift : effectuer des requêtes correctement

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course
```html

Introduction : pourquoi un client HTTP correct est important pour votre application Swift

Les applications iOS et macOS modernes ne peuvent rarement se passer d'interaction avec des serveurs distants. Qu'il s'agisse de charger des données depuis une API REST, d'envoyer des analytics ou de travailler avec des services cloud — le client HTTP en Swift est le fondement de la communication réseau. Cependant, de nombreux développeurs, surtout les débutants, commettent des erreurs typiques : ils ignorent la gestion des erreurs, n'utilisent pas les fonctionnalités modernes du langage (async/await) ou créent du « code spaghetti » à partir d'appels réseau. Dans cet article, nous allons voir comment écrire un client HTTP fiable et performant en Swift, en suivant les meilleures pratiques.

1. Les bases de URLSession : le cœur des requêtes réseau

Dans la bibliothèque standard de Swift, le framework Foundation et la classe URLSession sont responsables de la communication réseau. C'est un outil puissant qui prend en charge la configuration des sessions, la mise en cache, les requêtes en arrière-plan et bien plus encore.

1.1. Création d'un client HTTP de base

Commençons par une structure simple qui effectuera des requêtes GET. Utilisons une énumération pour décrire les erreurs possibles et un protocole pour la flexibilité :

import Foundation

enum HTTPClientError: Error {
    case invalidURL
    case noData
    case decodingError(Error)
    case networkError(Error)
    case statusCode(Int)
}

protocol HTTPClientProtocol {
    func get<T: Decodable>(from urlString: String, responseType: T.Type) async throws -> T
}

final class HTTPClient: HTTPClientProtocol {
    private let session: URLSession
    private let decoder: JSONDecoder
    
    init(session: URLSession = .shared, decoder: JSONDecoder = JSONDecoder()) {
        self.session = session
        self.decoder = decoder
    }
    
    func get<T: Decodable>(from urlString: String, responseType: T.Type) async throws -> T {
        guard let url = URL(string: urlString) else {
            throw HTTPClientError.invalidURL
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        
        let (data, response) = try await session.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw HTTPClientError.networkError(NSError(domain: "Invalid response", code: -1))
        }
        
        guard (200...299).contains(httpResponse.statusCode) else {
            throw HTTPClientError.statusCode(httpResponse.statusCode)
        }
        
        do {
            return try decoder.decode(T.self, from: data)
        } catch {
            throw HTTPClientError.decodingError(error)
        }
    }
}

1.2. Pourquoi async/await ?

Avant l'arrivée de Swift 5.5, les développeurs utilisaient des closures (completion handlers) ou des bibliothèques tierces (Alamofire). Async/await rend le code linéaire, compréhensible et facile à lire. Les erreurs sont gérées via do-catch, et non via des closures imbriquées.

2. POST, PUT, DELETE : travailler avec différentes méthodes HTTP

Un vrai client HTTP doit prendre en charge toutes les méthodes principales. Ajoutons au protocole des méthodes pour envoyer des données et mettre à jour des ressources.

2.1. Extension du protocole pour POST et PUT

extension HTTPClientProtocol {
    func post<T: Decodable, U: Encodable>(
        to urlString: String,
        body: U,
        responseType: T.Type
    ) async throws -> T {
        guard let url = URL(string: urlString) else {
            throw HTTPClientError.invalidURL
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        request.httpBody = try JSONEncoder().encode(body)
        
        return try a
```                

Blogs

Book Recommendations