Estoy tratando de descubrir cómo trabajar con operaciones asíncronas usando Combine y SwiftUI.
Por ejemplo, tengo una HealthKitManager
clase que, entre otras cosas, maneja la solicitud de autorización de tienda de salud ...
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))
}
}
}
}
que se usa de la siguiente manera ...
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
}
}
}
}
Lo que me gustaría hacer es usar Combinar en lugar de un Result
cierre. Estoy adivinando algo como esto ...
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)
}
}
}
}
Pero entonces no está claro cómo vincular los valores para NavigationLink(isActive:)
y alert(isPresented:)
, y obtener el error.
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
}
}
}
Supongo que eso @Published var authorisationResult: Result<Bool, Error>?
no es correcto? ¿Debería estar usando Future / Promise
algo más?
Actualizar
Descubrí que hay otra forma de presentar una alerta ...
.alert(item: self.$error) { error in
Alert(title: Text(error.localizedDescription))
lo que significa que no necesito el Bool para showError
(solo requiere que el Error
objeto sea Identifiable
)
fuente
@Published
le proporciona un editor y tiene integración automática con la actualización de vista SwiftUI a través de@ObservedObject
la propiedad dinámica. Puedes usar cualquier cosa, pero piensa en pros y contras . ¿Es el objetivo hacer que las cosas simples sean complejas?Respuestas:
Me gusta tener
result
como lo hiciste en la segunda varianteentonces el posible enfoque para el uso puede ser el siguiente
donde alguna extensión conveniente
la variante para
error
se puede hacer de manera similar.fuente
Revisé mi respuesta para que se base en la respuesta de @ Asperi :
fuente
hasAuthorizationError
,authorizationError
yisAuthorized
no parece correcto de alguna manera ... especialmente porque los 3 están cubiertos por el tipo de Resultado único. Además, esta clase se puede usar para otras operaciones asíncronas, por lo que agregar 3@Published
vars adicionales para cada operación parece mucho. Esperaba que Combine tuviera una mejor manera de manejar esto.