Estou tentando descobrir como trabalhar com operações assíncronas usando Combine e SwiftUI.
Por exemplo, eu tenho uma HealthKitManager
classe que, entre outras coisas, lida com a solicitação de autorização de loja de saúde ...
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 é usado da seguinte maneira…
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
}
}
}
}
O que eu gostaria de fazer é usar Combinar em vez de um Result
fechamento. Eu estou supondo algo assim ...
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)
}
}
}
}
Mas não está claro como vincular-se aos valores de NavigationLink(isActive:)
e alert(isPresented:)
e obter o erro.
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
}
}
}
Eu estou supondo que isso @Published var authorisationResult: Result<Bool, Error>?
não está correto? Devo estar usando Future / Promise
, algo mais?
Atualizar
Descobri que há outra maneira de apresentar um alerta ...
.alert(item: self.$error) { error in
Alert(title: Text(error.localizedDescription))
o que significa que não preciso do Bool showError
(apenas requer que o Error
objeto seja Identifiable
)
fonte
@Published
fornece um editor e tem integração automática com a atualização da visualização SwiftUI por meio de@ObservedObject
propriedade dinâmica. Você pode usar qualquer coisa, mas pense em prós e contras . O objetivo é tornar as coisas simples complexas?Respostas:
Eu gosto de ter
result
como você fez na segunda varianteportanto, a possível abordagem para uso pode ser a seguinte
onde alguma extensão conveniente
a variante para
error
pode ser feita de maneira semelhante.fonte
Revisei minha resposta para basear- me na resposta da @ Asperi :
fonte
hasAuthorizationError
,authorizationError
eisAuthorized
não parece certo de alguma forma ... especialmente porque todos os três são cobertos pelo único tipo de resultado. Além disso, essa classe pode ser usada para outras operações assíncronas, portanto, adicionar 3@Published
vars extras para cada operação parece muito. Eu esperava que Combine tivesse uma maneira melhor de lidar com isso.