SwiftUI vs UIKit
Comparaison approfondie entre SwiftUI, le framework déclaratif moderne d'Apple, et UIKit, éprouvé de longue date. Quel framework choisir en 2025 ?
Paradigme de programmation réactive — flux, opérateurs, souscripteurs
Concurrence native Swift 5.5 — lisible, sûre, moderne
| Catégorie | Combine | Async/Await |
|---|---|---|
| Performance | 8/10 | 9/10 |
| Facilité d'apprentissage | 4/10 | 9/10 |
| Écosystème | 8/10 | 9/10 |
| Communauté | 7/10 | 9/10 |
| Marché de l'emploi | 7/10 | 9/10 |
| Pérennité | 6/10 | 10/10 |
// Combine - Validation de formulaire avec recherche en direct
import Combine
import Foundation
class SearchViewModel: ObservableObject {
@Published var searchText = ""
@Published var results: [String] = []
@Published var isLoading = false
@Published var errorMessage: String?
private var cancellables = Set<AnyCancellable>()
private let searchService: SearchService
init(searchService: SearchService) {
self.searchService = searchService
setupSearch()
}
private func setupSearch() {
$searchText
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
.removeDuplicates()
.filter { $0.count >= 2 }
.handleEvents(receiveOutput: { [weak self] _ in
self?.isLoading = true
self?.errorMessage = nil
})
.flatMap { [weak self] query -> AnyPublisher<[String], Never> in
guard let self else { return Just([]).eraseToAnyPublisher() }
return self.searchService.search(query: query)
.catch { [weak self] error -> Just<[String]> in
self?.errorMessage = error.localizedDescription
return Just([])
}
.eraseToAnyPublisher()
}
.receive(on: DispatchQueue.main)
.sink { [weak self] results in
self?.isLoading = false
self?.results = results
}
.store(in: &cancellables)
}
}// Async/Await - Appels API en parallèle et gestion des erreurs
import Foundation
// Cache thread-safe avec Actor
actor NetworkCache {
private var cache: [URL: Data] = [:]
func get(_ url: URL) -> Data? { cache[url] }
func set(_ url: URL, data: Data) { cache[url] = data }
}
struct UserDashboard {
let user: User
let posts: [Post]
let notifications: [Notification]
}
@MainActor
class DashboardViewModel: ObservableObject {
@Published var dashboard: UserDashboard?
@Published var isLoading = false
@Published var error: Error?
private let cache = NetworkCache()
func loadDashboard(userId: String) async {
isLoading = true
error = nil
do {
// Chargement en parallèle avec async let
async let user = fetchUser(id: userId)
async let posts = fetchPosts(userId: userId)
async let notifications = fetchNotifications(userId: userId)
// Tout a démarré en même temps, on attend tout
dashboard = UserDashboard(
user: try await user,
posts: try await posts,
notifications: try await notifications
)
} catch {
self.error = error
}
isLoading = false
}
private func fetchUser(id: String) async throws -> User {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
private func fetchPosts(userId: String) async throws -> [Post] {
let url = URL(string: "https://api.example.com/users/\(userId)/posts")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([Post].self, from: data)
}
private func fetchNotifications(userId: String) async throws -> [Notification] {
let url = URL(string: "https://api.example.com/users/\(userId)/notifications")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([Notification].self, from: data)
}
}En 2025, privilégiez Async/Await pour les nouveaux projets — plus lisible, moins sujet aux erreurs, et c'est la direction activement développée par Apple. Continuez à utiliser Combine pour les flux réactifs complexes (recherche en direct, pipelines de validation de formulaires). Les deux fonctionnent parfaitement ensemble.
Obtenir une consultation gratuiteUne migration progressive est recommandée. Les nouvelles fonctionnalités peuvent être écrites avec async/await ; le code Combine existant continue de fonctionner. AsyncPublisher permet de convertir les flux Combine en séquences asynchrones.