我正在尝试在Apple Watch上启动HKWorkoutSession,并将心率数据即时发送到iPhone以进行近乎实时的显示。为实现这一目标,我正在使用WatchConnectivity的sendMessage
方法。我不需要来自手机/手表的回复,所以我使用的是不使用回复处理程序的sendMessage版本。
心率数据没有进入手机,我很难理解为什么。我没有得到任何有用的错误消息。我怎么知道出了什么问题?我已经将手机应用程序和手表应用程序连接到调试器,但由于消息永远不会到达手机,我在查明正在发生的事情时遇到了很多麻烦,因为很明显我的电话代码中的断点不是到达。之前,我使用的是sendMessage
的replyHandler版本,它有一个用于错误处理的闭包。使用它,我能够看到我的错误(有时)是由于收到返回消息的超时。但是,手机上的didReceiveMessage
代表从未到过,所以我从未发送过它的回复信息,我并不感到惊讶。我假设手机应用和手表应用之间的连接有问题,但我检查了isReachable
布尔值,以及isPaired
和isWatchAppInstalled
。
所以我想我想知道你是否有关于Watch Connectivity调试技术的任何提示。而且,如果你想看看我的代码,也就是这样。如果你看到任何明显的错误,请告诉我。 (对不起,它有点草率 - 只是试着让事情立即发挥作用。)
的ViewController:
class ViewController: UIViewController, WCSessionDelegate {
@IBOutlet weak var bpmLabel: UILabel!
let healthStore = HKHealthStore()
var bpmArray = [Double]()
var wcSession: WCSession?
// WC Session Delegate methods
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("WC Session activation failed with error: \(error.localizedDescription)")
return
}
}
func sessionDidBecomeInactive(_ session: WCSession) {
print("WC has become inactive")
}
func sessionDidDeactivate(_ session: WCSession) {
print("WC was deactivated")
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
guard let bpm = message["bpm"] as? Double else { return }
DispatchQueue.main.async {
self.bpmArray.append(bpm)
self.bpmLabel.text = String(bpm)
}
override func viewDidLoad() {
super.viewDidLoad()
if WCSession.isSupported() {
wcSession = WCSession.default
wcSession?.delegate = self
wcSession?.activate()
}
if !(wcSession?.isPaired)! || !(wcSession?.isWatchAppInstalled)! {
print("PAIRING PROBLEM")
}
}
InterfaceController:
var wcSession: WCSession?
@IBOutlet var bpm: WKInterfaceLabel!
let healthStore = HKHealthStore()
var workoutSession: HKWorkoutSession?
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("WC Session activation failed with error: \(error.localizedDescription)")
return
}
}
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
print("here")
}
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
print("Workout session failure")
}
override func awake(withContext context: Any?) {
super.awake(withContext: context)
workoutSession?.delegate = self
guard let wcSess = self.wcSession else { return }
wcSess.activate()
}
// modified from https://github.com/coolioxlr/watchOS-2-heartrate/blob/master/VimoHeartRate%20WatchKit%20App%20Extension/InterfaceController.swift
func createHeartRateStreamingQuery() -> HKQuery? {
if !HKHealthStore.isHealthDataAvailable() {
print("health data not available")
}
guard let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return nil }
let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: nil, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
self.updateHeartRate(samples: sampleObjects)
}
heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
self.updateHeartRate(samples: samples)
}
return heartRateQuery
}
// modified from https://github.com/coolioxlr/watchOS-2-heartrate/blob/master/VimoHeartRate%20WatchKit%20App%20Extension/InterfaceController.swift
func updateHeartRate(samples: [HKSample]?) {
guard let heartRateSamples = samples as? [HKQuantitySample] else { return }
guard let sample = heartRateSamples.first else { return }
let value = sample.quantity.doubleValue(for: HKUnit(from: "count/min"))
DispatchQueue.main.async() {
self.bpm.setText(String(UInt16(value)))
}
let dataToSendToPhone = ["bpm":String(value)]
if (wcSession?.isReachable)! {
self.wcSession?.sendMessage(dataToSendToPhone, replyHandler: nil)
}
else {
print("WC Session not reachable")
}
}
override func willActivate() {
super.willActivate()
if WCSession.isSupported() {
wcSession = WCSession.default
wcSession?.delegate = self
wcSession?.activate()
}
}
override func didDeactivate() {
super.didDeactivate()
}
@IBAction func recordIsTapped() {
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .other
workoutConfiguration.locationType = .unknown
// Inspired by https://developer.apple.com/library/content/samplecode/SpeedySloth/Introduction/Intro.html
do {
try self.workoutSession = HKWorkoutSession(configuration: workoutConfiguration)
healthStore.start(self.workoutSession!)
if HKHealthStore.isHealthDataAvailable() {
if let query = createHeartRateStreamingQuery() {
self.healthStore.execute(query)
}
}
else {
print("Healthkit unavailable")
}
}
catch {
fatalError(error.localizedDescription)
}
}