【发布时间】:2020-06-10 02:00:34
【问题描述】:
我正在尝试弄清楚如何使用 Combine 和 SwiftUI 处理异步操作。
例如,我有一个 HealthKitManager 类,除其他外,它处理请求健康商店授权……
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))
}
}
}
}
使用如下……
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
}
}
}
}
我想做的是使用 Combine 而不是 Result 闭包。我猜是这样的……
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)
}
}
}
}
但是不清楚如何绑定到NavigationLink(isActive:) 和alert(isPresented:) 的值,并得到错误。
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
}
}
}
我猜@Published var authorisationResult: Result<Bool, Error>? 不正确?
我应该使用Future / Promise 吗?
更新
我发现还有另一种显示警报的方式……
.alert(item: self.$error) { error in
Alert(title: Text(error.localizedDescription))
这意味着我不需要 showError 的 Bool(它只需要 Error 对象为 Identifiable)
【问题讨论】:
-
@Published为您提供发布者,并通过@ObservedObject动态属性与 SwiftUI 视图刷新自动集成。您可以使用任何东西,但请考虑利弊。目标是让简单的事情变得复杂吗?
标签: asynchronous swiftui combine