Ich versuche herauszufinden, wie man mit asynchronen Operationen mit Combine und SwiftUI arbeitet.
Zum Beispiel habe ich eine HealthKitManager
Klasse, die unter anderem die Beantragung einer Genehmigung für ein Reformhaus behandelt…
final class HealthKitManager {
enum Error: Swift.Error {
case notAvailable
case authorisationError(Swift.Error)
}
let healthStore = HKHealthStore()
func getHealthKitData(for objects: Set<HKObjectType>, completion: @escaping (Result<Bool, Error>) -> Void) {
guard HKHealthStore.isHealthDataAvailable() else {
completion(.failure(.notAvailable))
return
}
self.healthStore.requestAuthorization(toShare: nil, read: objects) { completed, error in
DispatchQueue.main.async {
if let error = error {
completion(.failure(.authorisationError(error)))
}
completion(.success(completed))
}
}
}
}
welches wie folgt verwendet wird…
struct ContentView: View {
let healthKitManager = HealthKitManager()
@State var showNextView = false
@State var showError = false
@State var hkError: Error?
let objectTypes = Set([HKObjectType.quantityType(forIdentifier: .bloodGlucose)!])
var body: some View {
NavigationView {
NavigationLink(destination: NextView(), isActive: $showNextView) {
Button("Show Next View") {
self.getHealthKitData()
}
}.navigationBarTitle("Content View")
}.alert(isPresented: $showError) {
Alert(title: Text("Error"), message: Text(hkError?.localizedDescription ?? ""), dismissButton: .cancel())
}
}
func getHealthKitData() {
self.healthKitManager.getHealthKitData(for: self.objectTypes) { result in
switch result {
case let .success(complete):
self.showNextView = complete
case let .failure(error):
self.hkError = error
self.showError = true
}
}
}
}
Was ich tun möchte, ist Kombinieren anstelle eines Result
Verschlusses. Ich vermute so etwas…
final class HealthKitManager: ObservableObject {
enum Error: Swift.Error {
case notAvailable
case authorisationError(Swift.Error)
}
@Published var authorisationResult: Result<Bool, Error>?
let healthStore = HKHealthStore()
func getHealthKitData(for objects: Set<HKObjectType>) {
guard HKHealthStore.isHealthDataAvailable() else {
self.authorisationResult = .failure(.notAvailable)
return
}
self.healthStore.requestAuthorization(toShare: nil, read: objects) { completed, error in
DispatchQueue.main.async {
if let error = error {
self.authorisationResult = .failure(.authorisationError(error))
return
}
self.authorisationResult = .success(completed)
}
}
}
}
Aber dann ist es unklar, wie man sich an die Werte für NavigationLink(isActive:)
und bindet alert(isPresented:)
und den Fehler erhält.
struct ContentView: View {
@ObservedObject var healthKitManager = HealthKitManager()
let objectTypes = Set([HKObjectType.quantityType(forIdentifier: .bloodGlucose)!])
var body: some View {
NavigationView {
NavigationLink(destination: NextView(), isActive: ????) { // How do I get this
Button("Show Next View") {
self.healthKitManager.getHealthKitData(for: self.objectTypes)
}
}.navigationBarTitle("Content View")
}.alert(isPresented: ????) { // or this
Alert(title: Text("Error"), message: Text(????.localizedDescription ?? ""), dismissButton: .cancel()) // or this
}
}
}
Ich vermute, das @Published var authorisationResult: Result<Bool, Error>?
ist nicht richtig? Soll ich Future / Promise
etwas anderes verwenden?
Aktualisieren
Ich habe festgestellt, dass es eine andere Möglichkeit gibt, eine Warnung anzuzeigen…
.alert(item: self.$error) { error in
Alert(title: Text(error.localizedDescription))
was bedeutet, dass ich den Bool nicht brauche showError
(es muss nur das Error
Objekt sein Identifiable
)
quelle
@Published
Bietet Ihnen einen Publisher und verfügt über eine automatische Integration in die Aktualisierung der SwiftUI-Ansicht über@ObservedObject
dynamische Eigenschaften. Sie können alles verwenden, aber über Vor- und Nachteile nachdenken . Ist es das Ziel, einfache Dinge komplex zu machen?Antworten:
Ich mag es,
result
wie du es in der zweiten Variante getan hastDaher kann der mögliche Ansatz für die Verwendung wie folgt sein
wo eine bequeme Erweiterung
Die Variante für
error
kann auf ähnliche Weise erfolgen.quelle
Meine Antwort wurde überarbeitet, um auf der Antwort von @ Asperi zu basieren :
quelle
hasAuthorizationError
habenauthorizationError
undisAuthorized
irgendwie nicht richtig erscheinen ... zumal alle 3 vom einzelnen Ergebnistyp abgedeckt werden. Diese Klasse kann auch für andere asynchrone Operationen verwendet werden, so dass das Hinzufügen von 3 zusätzlichen@Published
Variablen für jede Operation viel zu sein scheint. Ich hatte gehofft, dass Combine eine bessere Möglichkeit haben würde, damit umzugehen.