iPhone 和 Apple Watch 之间的 WatchConnectivity 超时错误

dca*_*mpb 5 ios swift healthkit watchos-2 watchconnectivity

我正在尝试使用 WatchConnectivity 框架将字符串从 Apple Watch 发送到 iPhone。

\n\n

我启动了两个会话,一个是 WCSession,一个是 HKSession,因为我想在读取心率信息时将其从手表传输到 iPhone。(如果有更好的方法请告诉我)。

\n\n

如果有帮助,我已经发布了我收到的日志消息。

\n\n
2017-04-14 20:01:24.660433-0400 MoodTunes WatchKit Extension[180:16033] [WC] -[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:] AD8F92C9-FCAA-45B0-9B4C-0D5C95B72BEE\n errorHandler: YES with WCErrorCodeTransferTimedOut->IDSErrorTypeTimedOut-\n>IDSResponseTimedOut\nWatch send failed with error Error Domain=WCErrorDomain Code=7017 "Transfer \ntimed out." UserInfo={NSUnderlyingError=0x175a7da0 {Error \nDomain=com.apple.identityservices.error Code=23 "Timed out" UserInfo=\n{NSUnderlyingError=0x1752d4f0 {Error Domain=com.apple.ids.idssenderrordomain \nCode=12 "(null)"}, NSLocalizedDescription=Timed out}}, \nNSLocalizedDescription=Transfer timed out.} \n
Run Code Online (Sandbox Code Playgroud)\n\n

iPhone ViewController.swift

\n\n
import HealthKit\nimport WatchKit\nimport Foundation\nimport CoreLocation\nimport WatchConnectivity\nimport UIKit\n\nclass ViewController: UIViewController, CLLocationManagerDelegate, \nWCSessionDelegate { \n\n@IBOutlet weak var timerLabel: UILabel!\n@IBOutlet weak var milesLabel: UILabel!\n@IBOutlet weak var hrLabel: UILabel!\nlet session = WCSession.default()\nstatic let sharedManager = ViewController()\nlet healthManager:HealthKitManager = HealthKitManager()\nlet healthStore = HKHealthStore()\nlet heartRateUnit = HKUnit(from: "count/min")\n\n//var anchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))\nvar currenQuery : HKQuery?    // the current query can be nil\nvar heartRate: HKQuantitySample?\nvar zeroTime = TimeInterval()\nvar counter =  0\n\n\n\noverride func viewWillAppear(_ animated: Bool) {\n    if (WCSession.isSupported()) {\n        session.delegate = self\n        session.activate()\n    }\n    getHealthKitPermission()\n}\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    if (WCSession.isSupported()) {\n        session.delegate = self\n        session.activate()\n    }\n    getHealthKitPermission()\n}\noverride func didReceiveMemoryWarning() {\n    super.didReceiveMemoryWarning()\n}\n// Function to get access to the user\'s Healthkit\nfunc getHealthKitPermission() {\n    // checks if the health data is avaiable\n    if  HKHealthStore.isHealthDataAvailable() == true {hrLabel.text = "Good"} else {\n        hrLabel.text = "not available"\n        return\n    }\n\n    guard let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else {\n      displayNotAllowed()\n        return\n    }\n    let dataTypes = Set(arrayLiteral: quantityType)\n    healthStore.requestAuthorization(toShare: dataTypes, read: dataTypes) { (success, error) -> Void in\n        if success == false {\n            self.displayNotAllowed()\n        }\n    }\n}\nfunc displayNotAllowed() {\n    hrLabel.text = "not allowed"\n}\n\n// function crates a heartrate query for the healthstore. it returns an optional HKQuery\nfunc createHeartRateStreamingQuery(_ workoutStartDate: Date) -> HKQuery? {\n\n\n    guard let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return nil }\n    let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictEndDate )\n\n    //let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])\n    let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate])\n\n\n    let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in\n        //guard let newAnchor = newAnchor else {return}\n        //self.anchor = newAnchor\n        self.updateHeartRate(sampleObjects)\n    }\n\n    heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in\n        //self.anchor = newAnchor!\n        self.updateHeartRate(samples)\n    }\n    return heartRateQuery\n}\n// updates the display on the UI, heartRateLabel and calls animateHeart for the heart enlargment\nfunc updateHeartRate(_ samples: [HKSample]?) {\n    guard let heartRateSamples = samples as? [HKQuantitySample] else {return}\n    var value = 0.0\n    DispatchQueue.main.async {\n        guard let sample = heartRateSamples.first else{return}\n        value = sample.quantity.doubleValue(for: self.heartRateUnit)\n        self.hrLabel.text = "Hello"\n        //self.hrLabel.text = String(UInt16(value))\n\n    }\n}\n\n        // retrieve source from sample\n@available(iOS 9.3, *)\nfunc session(_: WCSession, activationDidCompleteWith: WCSessionActivationState, error: Error?) {\n\n}\n\nfunc session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) {\n    DispatchQueue.main.async() {\n        guard let m = message["m"] as? String else { return }\n      //  self.label.text = m\n    }\n}\nfunc sessionDidBecomeInactive(_ sesson: WCSession) -> Void {\n\n}\nfunc sessionDidDeactivate (_ session: WCSession) -> Void {\n\n}\n@IBAction func shareData(_ sender: Any) {\n    if let query = createHeartRateStreamingQuery(Date()) {\n        self.currenQuery = query\n        healthStore.execute(query)\n\n\n}\n\n}\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是我的苹果手表应用程序的界面控制器

\n\n

InterfaceController.swift

\n\n
import WatchKit\nimport Foundation\nimport UIKit\nimport CoreLocation\nimport HealthKit\nimport WatchConnectivity\n\nclass InterfaceController: WKInterfaceController, HKWorkoutSessionDelegate, \nWCSessionDelegate {\n@IBOutlet private weak var heartRateLabel: WKInterfaceLabel!\n@IBOutlet private weak var heart: WKInterfaceImage!\n@IBOutlet private weak var startStopButton : WKInterfaceButton!\n@IBOutlet private weak var label : WKInterfaceLabel!\n\n@IBOutlet var dataLabel: WKInterfaceLabel!\n@IBOutlet private weak var deviceLabel: WKInterfaceLabel!\nlet healthStore = HKHealthStore() // Creates an instance of the healthkit store\n\n//State of the app - is the workout activated\nvar workoutActive = false\n\n// define the activity type and location\nvar hkSession : HKWorkoutSession?  // ? means the session can be nil\nvar session = WCSession.default()\nlet heartRateUnit = HKUnit(from: "count/min")\n\n//var anchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))\nvar currenQuery : HKQuery?    // the current query can be nil\n\noverride func awake(withContext context: Any?) {\n    super.awake(withContext: context)\n    if WCSession.isSupported() {\n        session.delegate = self\n        session.activate()\n    }\n}\n\noverride func willActivate() {\n    super.willActivate()\n\n    // checks if the health data is avaiable\n    guard HKHealthStore.isHealthDataAvailable() == true else {\n        heartRateLabel.setText("not available")\n        return\n    }\n\n    guard let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else {\n        displayNotAllowed()\n        return\n    }\n    let dataTypes = Set(arrayLiteral: quantityType)\n    healthStore.requestAuthorization(toShare: dataTypes, read: dataTypes) { (success, error) -> Void in\n        if success == false {\n            self.displayNotAllowed()\n        }\n    }\n}\n\nfunc session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {\n    DispatchQueue.main.async() {\n        guard let m = message["m"] as? String else { return }\n        self.dataLabel.setText(m)\n    }\n\n}\n@available(watchOSApplicationExtension 2.2, *)\nfunc session(_: WCSession, activationDidCompleteWith: WCSessionActivationState, error: Error?) {\n\n}\n\nfunc displayNotAllowed() {\n    heartRateLabel.setText("not allowed")\n}\n// Function used to set the state of the workout, if it\'s still "running" then call workoutDidStart else call workoutDidEnd\nfunc workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {\n    switch toState {\n    case .running:\n        workoutDidStart(date)\n    case .ended:\n        workoutDidEnd(date)\n    default:\n        print("Unexpected state \\(toState)")\n    }\n}\n\nfunc workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {\n    // Do nothing for now\n    print("Workout error")\n}\n\n// function used to query the healthkitstore when the workout is active\nfunc workoutDidStart(_ date : Date) {\n    if let query = createHeartRateStreamingQuery(date) {\n        self.currenQuery = query\n        healthStore.execute(query)\n    } else {\n        heartRateLabel.setText("cannot start")\n    }\n}\n\nfunc workoutDidEnd(_ date : Date) {\n    healthStore.stop(self.currenQuery!)\n    heartRateLabel.setText("---")\n    hkSession = nil\n}\n\n// MARK: - Actions\n@IBAction func startBtnTapped() {\n    if (self.workoutActive) {\n        //finish the current workout\n        self.workoutActive = false\n        self.startStopButton.setTitle("Start")\n        if let workout = self.hkSession {\n            healthStore.end(workout)\n        }\n    } else {\n        //start a new workout\n        self.workoutActive = true\n        self.startStopButton.setTitle("Stop")\n        startWorkout()\n    }\n\n}\n\nfunc startWorkout() {\n\n    // If we have already started the workout, then do nothing.\n    if (hkSession != nil) {\n        return\n    }\n\n    // Configure the workout session.\n    let workoutConfiguration = HKWorkoutConfiguration()\n    workoutConfiguration.activityType = .crossTraining\n    workoutConfiguration.locationType = .indoor\n\n    do {\n        hkSession = try HKWorkoutSession(configuration: workoutConfiguration)\n        hkSession?.delegate = self\n    } catch {\n        fatalError("Unable to create the workout session!")\n    }\n\n    healthStore.start(self.hkSession!)\n}\n\n// function crates a heartrate query for the healthstore. it returns an optional HKQuery\nfunc createHeartRateStreamingQuery(_ workoutStartDate: Date) -> HKQuery? {\n\n\n    guard let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return nil }\n    let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictEndDate )\n\n    //let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])\n    let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate])\n\n\n    let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in\n        //guard let newAnchor = newAnchor else {return}\n        //self.anchor = newAnchor\n        self.updateHeartRate(sampleObjects)\n    }\n\n    heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in\n        //self.anchor = newAnchor!\n        self.updateHeartRate(samples)\n    }\n    return heartRateQuery\n}\n// updates the display on the UI, heartRateLabel and calls animateHeart for the heart enlargment\nfunc updateHeartRate(_ samples: [HKSample]?) {\n    guard let heartRateSamples = samples as? [HKQuantitySample] else {return}\n    var value = 0.0\n    DispatchQueue.main.async {\n        guard let sample = heartRateSamples.first else{return}\n        value = sample.quantity.doubleValue(for: self.heartRateUnit)\n        self.heartRateLabel.setText(String(UInt16(value)))\n\n        // retrieve source from sample\n        let name = sample.sourceRevision.source.name\n        self.updateDeviceName(name)\n        self.animateHeart()\n    }\n    if (self.session.isReachable) {\n        self.sendMessage(value: value)\n    }\n\n}\n\n// function to create and send the message the iphone\nfunc sendMessage(value: Double) {\n    let strValue = String(UInt16(value))\n    let message = [ "m": strValue ]\n    self.session.sendMessage(message, replyHandler:nil, errorHandler: { (error) -> Void in\n        print("\xef\xa3\xbfWatch send failed with error \\(error)")\n    })\n}\nfunc updateDeviceName(_ deviceName: String) {\n    deviceLabel.setText(deviceName)\n}\n\n\nfunc animateHeart() {\n    self.animate(withDuration: 0.5) {\n        self.heart.setWidth(60)\n        self.heart.setHeight(90)\n    }\n\n    let when = DispatchTime.now() + Double(Int64(0.5 * double_t(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)\n\n    DispatchQueue.global(qos: .default).async {\n        DispatchQueue.main.asyncAfter(deadline: when) {\n            self.animate(withDuration: 0.5, animations: {\n                self.heart.setWidth(50)\n                self.heart.setHeight(80)\n            })\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

小智 1

WCErrorCodeTransferTimedOut->IDSErrorTypeTimedOut->IDSResponseTimedOut

我通过重新启动 iPhone 和手表解决了这个错误。