Tutoriel avancé sur Swift : Programmation concurrente avec Combine
Introduction
Ce tutoriel couvre l’utilisation avancée du framework Combine de Swift pour la programmation réactive et concurrente. Nous explorerons les concepts clés et les mettrons en pratique à travers des exemples concrets.
Table des matières
Bases de Combine
Combine est un framework de programmation réactive introduit par Apple dans iOS 13. Il permet de traiter des valeurs au fil du temps de manière asynchrone.
import Combine
let publisher = Just(5)
let cancellable = publisher.sink { value in
print("Received value: \(value)")
}
Publishers et Subscribers
Les publishers émettent des valeurs, tandis que les subscribers les reçoivent.
let subject = PassthroughSubject<Int, Never>()
let subscription = subject
.sink { value in
print("Received: \(value)")
}
subject.send(1)
subject.send(2)
subject.send(completion: .finished)
Opérateurs
Les opérateurs permettent de transformer, filtrer et combiner des flux de données.
let numbers = (1...10).publisher
numbers
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
.collect()
.sink { squares in
print("Carrés des nombres pairs : \(squares)")
}
Gestion des erreurs
Combine offre des mécanismes puissants pour gérer les erreurs de manière élégante.
enum MyError: Error {
case customError
}
let failingPublisher = Fail<Int, MyError>(error: .customError)
failingPublisher
.catch { error -> AnyPublisher<Int, Never> in
print("Erreur interceptée : \(error)")
return Just(0).eraseToAnyPublisher()
}
.sink { value in
print("Valeur reçue : \(value)")
}
Schedulers
Les schedulers permettent de contrôler sur quel thread les opérations sont exécutées.
let queue = DispatchQueue(label: "com.example.myqueue")
Just(5)
.receive(on: queue)
.map { value -> Int in
print("Current thread: \(Thread.current)")
return value * 2
}
.receive(on: DispatchQueue.main)
.sink { value in
print("Résultat final sur le thread principal: \(value)")
}
Projet pratique
Implémentons un gestionnaire de téléchargements concurrents utilisant Combine.
class DownloadManager {
private var cancellables = Set<AnyCancellable>()
func download(urls: [URL]) -> AnyPublisher<[Data], Error> {
urls.publisher
.flatMap(maxPublishers: .max(3)) { url -> AnyPublisher<Data, Error> in
URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.mapError { $0 as Error }
.eraseToAnyPublisher()
}
.collect()
.eraseToAnyPublisher()
}
func startDownloads(urls: [URL]) {
download(urls: urls)
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("Tous les téléchargements sont terminés")
case .failure(let error):
print("Erreur de téléchargement : \(error)")
}
},
receiveValue: { dataArray in
print("Nombre de fichiers téléchargés : \(dataArray.count)")
}
)
.store(in: &cancellables)
}
}
// Utilisation
let manager = DownloadManager()
let urls = [
URL(string: "https://example.com/file1.pdf")!,
URL(string: "https://example.com/file2.pdf")!,
URL(string: "https://example.com/file3.pdf")!
]
manager.startDownloads(urls: urls)
Ce tutoriel couvre les aspects avancés de la programmation concurrente avec Combine en Swift. Il aborde les concepts fondamentaux, la manipulation des flux de données, la gestion des erreurs, et l’utilisation des schedulers. Le projet pratique démontre comment ces concepts peuvent être appliqués pour créer un gestionnaire de téléchargements concurrents robuste.