使用 Combine 的发布者和订阅者发布实时 HealthKit 数据?

Posted

技术标签:

【中文标题】使用 Combine 的发布者和订阅者发布实时 HealthKit 数据?【英文标题】:Using Combine's Publishers and Subscribers to publish real time HealthKit data? 【发布时间】:2020-07-21 18:01:05 【问题描述】:

我一直在 UIKit 和 WatchKit 中使用委托来在对象之间进行通信,例如传递数据。一个 WorkoutManager 视图模型,它在 HKworkout 期间接收来自 HealthKit 的委托回调,将卡路里、心率发送到 InterfaceController。

我现在正在尝试使用 Combine 和 SwiftUI 来传递相同的数据,但我有点迷茫。我使用 WorkoutManager 类作为我在 ContentView 中初始化的环境对象:

class WorkoutManager: NSObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate, ObservableObject  

    @Published var totalEnergyBurned: Double = 0

    //How to subscribe to the changes? 

//Omitted HealthKit code that queries and pushes data into totalEnergyBurned here





struct ContentView: View 
    
    let healthStore = HKHealthStore()
    @StateObject var workoutManager = WorkoutManager()
    
    var sessionTypes = [SessionType.Game, SessionType.Practice, SessionType.Pickup]
    
    var body: some View 
        
        List 
            ForEach(sessionTypes)  sessionType  in
                NavigationLink(destination: LiveWorkoutView(sessionType: sessionType)) 
                    SessionTypeRow(name: sessionType.stringValue)
                
            
        
        .navigationTitle("Let's Go!")
        .onAppear 
            let authorizationStatus = healthStore.authorizationStatus(for: HKSampleType.workoutType())
            switch authorizationStatus 
            case .sharingAuthorized:
                print("sharing authorized")
            case .notDetermined:
                print("not determined")
                HealthKitAuthManager.authorizeHealthKit()
            case .sharingDenied:
                print("sharing denied")
                HealthKitAuthManager.authorizeHealthKit()
            default:
                print("default in healthStore.authorizationStatus in ContentView")
                HealthKitAuthManager.authorizeHealthKit()
            
        
    

我的目标是将更改发布到 ContentView 的所有子项,但我不确定如何订阅更改?

import SwiftUI

struct LiveWorkoutView: View 

@State var sessionType: SessionType
    
    @StateObject var workoutManager = WorkoutManager()
    
    var body: some View 
        VStack 
            Text("\(workoutManager.totalEnergyBurned)")
            Button(action: 
                workoutManager.stopWorkout()
            ) 
                Text("End Workout")
            
        
        .onAppear 
            workoutManager.startWorkout()
            workoutManager.sessionType = sessionType
        
        .navigationTitle(sessionType.stringValue)
    

【问题讨论】:

【参考方案1】:

//如何订阅更改?

你没有。 @StateObject 在视图中注入订阅者,因此只需在视图 body 中的某处(需要时)使用 workoutManager. totalEnergyBurned 属性,一旦此属性更改,视图将自动刷新(例如,您从 HealthKit 回调中为其分配新值。

【讨论】:

好吧,也许我在写轨道上,当我尝试这样做时`Text("($workoutManager.$totalEnergyBurned)")` 编译器说Cannot assign to property: '$totalEnergyBurned' is immutableInstance method 'appendInterpolation' requires that 'Binding<Published<Double>.Publisher>' conform to '_FormatSpecifiable' 我不需要 $,让它与 Text("\(workoutManager.totalEnergyBurned)") 一起工作很容易 ?

以上是关于使用 Combine 的发布者和订阅者发布实时 HealthKit 数据?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI+Combine - 动态订阅发布者的字典

Combine - 测试 CurrentValueSubject 是不是有订阅者

谈谈Combine中一个有趣操作符flatMap的使用与陷阱

谈谈Combine中一个有趣操作符flatMap的使用与陷阱

如何通过另一个可观察对象观察已发布的属性 - Swift Combine

SwiftUI Combine Framework 共享receiveCompletion 代码块