Asynchronous 使用Combine和SwiftUI的异步操作

Asynchronous 使用Combine和SwiftUI的异步操作,asynchronous,swiftui,combine,Asynchronous,Swiftui,Combine,我试图弄清楚如何使用Combine和SwiftUI处理异步操作 例如,我有一个HealthKitManager类,该类处理健康商店授权请求 final class HealthKitManager { enum Error: Swift.Error { case notAvailable case authorisationError(Swift.Error) } let healthStore = HKHealthStore()

我试图弄清楚如何使用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))
            }
        }
    }
}
我想做的是使用Combine,而不是
结果
闭包。我猜是这样的

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)
            }
        }
    }
}
我猜
@发布的var授权结果:结果?
不正确? 我应该使用
Future/Promise
,其他什么吗


更新

我发现还有另一种方式来表示警报

.alert(item: self.$error) { error in
        Alert(title: Text(error.localizedDescription))
这意味着我不需要
showError
的Bool(它只需要
Error
对象是
可识别的

修改了我的答案,使之基于:
扩展结果{
func getFailure()->失败{
切换自身{
案例.失败(let-er):
回程器
违约:
归零
}
}
func绑定(
success successClosure:(@escaping(success)->B),
failureClosure:@escaping(failure)->B)->Binding{
返回绑定(
获取:{
切换自身{
成功案例(let value):
返回successClosure(值)
案例.失败(让失败):
返回故障关闭(故障)
}
},设置:{uin})
}
func implicitBinding(failure-failureClosure:@escaping(failure)->Success)->Binding{
返回绑定(成功:{$0},失败:failureClosure)
}
}
类HealthKitManager:ObservableObject{
枚举错误:Swift.Error{
案例授权错误(Swift.错误)
案例不可用
}
@已发布的var授权结果=结果。失败(.notAvailable)
让healthStore=HKHealthStore()
func getHealthKitData(对象:集合){
guard HKHealthStore.isHealthDataAvailable()其他{
自我授权结果=.failure(.notAvailable)
返回
}
self.healthStore.requestAuthorization(toShare:nil,read:objects){completed,中出错
DispatchQueue.main.async{
如果let error=error{
自我授权结果=.failure(.authorizationError(错误))
返回
}
自我授权结果=.success(已完成)
}
}
}
}
结构ContentView:View{
@ObservedObject变量healthKitManager=healthKitManager()
让objectTypes=Set([HKObjectType.quantityType(forIdentifier:.bloodGlucose)!]))
var body:一些观点{
导航视图{
导航链接(目标:NextView(),
isActive:healthKitManager.AuthorizationResult.implicitBinding(失败:{inFalse})){
按钮(“显示下一个视图”){
self.healthKitManager.getHealthKitData(用于:self.objectTypes)
}
}.navigationBarTitle(“内容视图”)
}.alert(显示为:healthKitManager.authorizationresult.binding(成功:{infalse},失败:{intrue})){
let message=healthKitManager.authorizationresult.getFailure()?.localizedDescription??“
返回警报(标题:Text(“错误”)、消息:Text(消息)、dismissButton:.cancel())//或此
}
}
}

我喜欢有
结果
,就像您在第二个变体中所做的那样

@Published var authorisationResult: Result<Bool, Error>?
@已发布的var授权结果:结果?
因此,可能的使用方法如下

NavigationLink(destination: NextView(), isActive: 
         Binding<Bool>.ifSuccess(self.healthKitManager.authorisationResult)) {
    Button("Show Next View") {
        self.healthKitManager.getHealthKitData(for: self.objectTypes)
    }
}.navigationBarTitle("Content View")
NavigationLink(目的地:NextView(),isActive:
绑定.ifSuccess(self.healthKitManager.authorizationresult)){
按钮(“显示下一个视图”){
self.healthKitManager.getHealthKitData(用于:self.objectTypes)
}
}.navigationBarTitle(“内容视图”)
哪里有方便的分机

extension Binding {
    static func ifSuccess<E>(_ result: Result<Bool, E>?) -> Binding<Bool> where E: Error {
        Binding<Bool>(
            get: {
                guard let result = result else { return false }
                switch result {
                 case .success(true):
                    return true
                 default:
                    return false
            }
        }, set: { _ in })
    }
}
扩展绑定{
静态函数ifSuccess(uresult:result?->绑定,其中E:Error{
装订(
获取:{
guard let result=result else{return false}
切换结果{
成功案例(正确):
返回真值
违约:
返回错误
}
},设置:{uin})
}
}

错误的变量
也可以用类似的方法完成。

谢谢。这肯定会起作用,但将
hasaauthorizationerror
authorizationError
isAuthorized
分别设置为不同的值似乎有点不对劲……尤其是当这三个值都包含在单一结果类型中时。这个类也可以用于其他异步操作,因此为每个操作添加3个额外的
@Published
变量似乎很多。我希望Combine能有更好的方法来处理这个问题。谢谢你的回答-很遗憾,这需要这么多额外的代码来完成。@AshleyMills,如果苹果为所有事情都提供API,我们会怎么做?我们不是程序员吗?=^)
@Published
为您提供发布者,并通过
@ObservedObject
动态属性与SwiftUI视图刷新自动集成。你可以使用任何东西,但要考虑利弊。把简单的事情复杂化是目标吗?
@Published var authorisationResult: Result<Bool, Error>?
NavigationLink(destination: NextView(), isActive: 
         Binding<Bool>.ifSuccess(self.healthKitManager.authorisationResult)) {
    Button("Show Next View") {
        self.healthKitManager.getHealthKitData(for: self.objectTypes)
    }
}.navigationBarTitle("Content View")
extension Binding {
    static func ifSuccess<E>(_ result: Result<Bool, E>?) -> Binding<Bool> where E: Error {
        Binding<Bool>(
            get: {
                guard let result = result else { return false }
                switch result {
                 case .success(true):
                    return true
                 default:
                    return false
            }
        }, set: { _ in })
    }
}