SwiftUI vs UIKit
Der umfassende Vergleich zwischen Apples modernem deklarativem Framework SwiftUI und dem bewährten UIKit. Welches Framework sollten Sie 2025 wählen?
Reaktives Programmierparadigma — Streams, Operatoren, Subscriber
Native Swift-5.5-Nebenläufigkeit — lesbar, sicher, modern
| Kategorie | Combine | Async/Await |
|---|---|---|
| Performance | 8/10 | 9/10 |
| Erlernbarkeit | 4/10 | 9/10 |
| Ökosystem | 8/10 | 9/10 |
| Community | 7/10 | 9/10 |
| Arbeitsmarkt | 7/10 | 9/10 |
| Zukunftssicherheit | 6/10 | 10/10 |
// Combine - Formularvalidierung mit Live-Suche
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 - Parallele API-Aufrufe und Fehlerbehandlung
import Foundation
// Threadsicherer Cache mit 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 {
// Paralleles Laden mit async let
async let user = fetchUser(id: userId)
async let posts = fetchPosts(userId: userId)
async let notifications = fetchNotifications(userId: userId)
// Alle gleichzeitig gestartet, jetzt warten wir auf alle Ergebnisse
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)
}
}Für neue Projekte 2025 sollten Sie Async/Await bevorzugen — besser lesbar, weniger fehleranfällig und die Richtung, die Apple aktiv weiterentwickelt. Combine sollten Sie weiterhin für komplexe reaktive Abläufe (Live-Suche, Formularvalidierungs-Pipelines) einsetzen. Beide zusammen funktionieren hervorragend.
Kostenlose Beratung erhaltenEin schrittweiser Übergang wird empfohlen. Neue Features können mit async/await geschrieben werden; bestehender Combine-Code funktioniert weiter. Mit AsyncPublisher lassen sich Combine-Streams in AsyncSequences umwandeln.