Warum AsyncButton gebraucht wird
Standard-SwiftUI-Button-Aktionen sind synchron. Wenn du eine asynchrone Operation durchführen musst – eine Netzwerkanfrage, einen Datenbankschreibvorgang, einen StoreKit-Kauf – musst du manuell einen Task verwalten, den Ladezustand tracken, den Button deaktivieren und Fehler behandeln. Dieses Boilerplate wiederholt sich bei jedem asynchronen Button in deiner App.
Ich habe eine AsyncButton-Komponente gebaut, die all das in eine einzige wiederverwendbare View verpackt.
Eine vereinfachte Implementierung
Hier ist die Kernidee:
struct AsyncButton<Label: View>: View {
let action: () async throws -> Void
let label: () -> Label
@State private var isRunning = false
@State private var result: Result<Void, Error>?
var body: some View {
Button {
isRunning = true
Task {
do {
try await action()
result = .success(())
} catch {
result = .failure(error)
}
isRunning = false
}
} label: {
HStack(spacing: 8) {
label()
if isRunning {
ProgressView()
}
}
}
.disabled(isRunning)
}
}Der Button erstellt intern einen Task, sodass Aufrufer await direkt in der Action-Closure verwenden können. Während der Task läuft, erscheint ein ProgressView neben dem Label und der Button wird deaktiviert, um doppelte Absendungen zu verhindern. Der result-State kann Erfolgs- oder Fehlerindikatoren steuern – ein Häkchen, einen Farbblitz oder eine Schüttelanimation.
Verwendung
Die Verwendung fühlt sich natürlich an:
AsyncButton {
try await viewModel.submitOrder()
} label: {
Text("Place Order")
}Kein manuelles State-Management, keine Task-Erstellung an der Aufrufstelle. Die vollständige Implementierung in HandySwiftUI fügt konfigurierbare Erfolgs-/Fehleranimationen, anpassbare Fortschrittsanzeigen und Unterstützung für Button-Styles hinzu. Aber das Kernmuster oben deckt den häufigsten Fall ab und lässt sich unkompliziert an deine eigenen Projekte anpassen.
