iPhone 和 Apple Watch 之间的 WatchConnectivity 超时错误
Posted
技术标签:
【中文标题】iPhone 和 Apple Watch 之间的 WatchConnectivity 超时错误【英文标题】:WatchConnectivity timeout error between iPhone and Apple Watch 【发布时间】:2017-09-11 06:14:53 【问题描述】:我正在尝试使用 WatchConnectivity 框架将字符串从我的 Apple Watch 发送到我的 iPhone。
我已经开始了两个会话,一个 WCSession 和一个 HKSession,因为我想在读取时将心率信息从手表传输到 iPhone。 (如果有更好的方法请告诉我)。
如果有帮助,我已经发布了我收到的日志消息。
2017-04-14 20:01:24.660433-0400 MoodTunes WatchKit Extension[180:16033] [WC] -[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:] AD8F92C9-FCAA-45B0-9B4C-0D5C95B72BEE
errorHandler: YES with WCErrorCodeTransferTimedOut->IDSErrorTypeTimedOut-
>IDSResponseTimedOut
Watch send failed with error Error Domain=WCErrorDomain Code=7017 "Transfer
timed out." UserInfo=NSUnderlyingError=0x175a7da0 Error
Domain=com.apple.identityservices.error Code=23 "Timed out" UserInfo=
NSUnderlyingError=0x1752d4f0 Error Domain=com.apple.ids.idssenderrordomain
Code=12 "(null)", NSLocalizedDescription=Timed out,
NSLocalizedDescription=Transfer timed out.
iPhone ViewController.swift
import HealthKit
import WatchKit
import Foundation
import CoreLocation
import WatchConnectivity
import UIKit
class ViewController: UIViewController, CLLocationManagerDelegate,
WCSessionDelegate
@IBOutlet weak var timerLabel: UILabel!
@IBOutlet weak var milesLabel: UILabel!
@IBOutlet weak var hrLabel: UILabel!
let session = WCSession.default()
static let sharedManager = ViewController()
let healthManager:HealthKitManager = HealthKitManager()
let healthStore = HKHealthStore()
let heartRateUnit = HKUnit(from: "count/min")
//var anchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
var currenQuery : HKQuery? // the current query can be nil
var heartRate: HKQuantitySample?
var zeroTime = TimeInterval()
var counter = 0
override func viewWillAppear(_ animated: Bool)
if (WCSession.isSupported())
session.delegate = self
session.activate()
getHealthKitPermission()
override func viewDidLoad()
super.viewDidLoad()
if (WCSession.isSupported())
session.delegate = self
session.activate()
getHealthKitPermission()
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Function to get access to the user's Healthkit
func getHealthKitPermission()
// checks if the health data is avaiable
if HKHealthStore.isHealthDataAvailable() == true hrLabel.text = "Good" else
hrLabel.text = "not available"
return
guard let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else
displayNotAllowed()
return
let dataTypes = Set(arrayLiteral: quantityType)
healthStore.requestAuthorization(toShare: dataTypes, read: dataTypes) (success, error) -> Void in
if success == false
self.displayNotAllowed()
func displayNotAllowed()
hrLabel.text = "not allowed"
// function crates a heartrate query for the healthstore. it returns an optional HKQuery
func createHeartRateStreamingQuery(_ workoutStartDate: Date) -> HKQuery?
guard let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else return nil
let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictEndDate )
//let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate])
let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
//guard let newAnchor = newAnchor else return
//self.anchor = newAnchor
self.updateHeartRate(sampleObjects)
heartRateQuery.updateHandler = (query, samples, deleteObjects, newAnchor, error) -> Void in
//self.anchor = newAnchor!
self.updateHeartRate(samples)
return heartRateQuery
// updates the display on the UI, heartRateLabel and calls animateHeart for the heart enlargment
func updateHeartRate(_ samples: [HKSample]?)
guard let heartRateSamples = samples as? [HKQuantitySample] else return
var value = 0.0
DispatchQueue.main.async
guard let sample = heartRateSamples.first elsereturn
value = sample.quantity.doubleValue(for: self.heartRateUnit)
self.hrLabel.text = "Hello"
//self.hrLabel.text = String(UInt16(value))
// retrieve source from sample
@available(ios 9.3, *)
func session(_: WCSession, activationDidCompleteWith: WCSessionActivationState, error: Error?)
func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void)
DispatchQueue.main.async()
guard let m = message["m"] as? String else return
// self.label.text = m
func sessionDidBecomeInactive(_ sesson: WCSession) -> Void
func sessionDidDeactivate (_ session: WCSession) -> Void
@IBAction func shareData(_ sender: Any)
if let query = createHeartRateStreamingQuery(Date())
self.currenQuery = query
healthStore.execute(query)
这是我的 Apple Watch 应用的 interfaceController
InterfaceController.swift
import WatchKit
import Foundation
import UIKit
import CoreLocation
import HealthKit
import WatchConnectivity
class InterfaceController: WKInterfaceController, HKWorkoutSessionDelegate,
WCSessionDelegate
@IBOutlet private weak var heartRateLabel: WKInterfaceLabel!
@IBOutlet private weak var heart: WKInterfaceImage!
@IBOutlet private weak var startStopButton : WKInterfaceButton!
@IBOutlet private weak var label : WKInterfaceLabel!
@IBOutlet var dataLabel: WKInterfaceLabel!
@IBOutlet private weak var deviceLabel: WKInterfaceLabel!
let healthStore = HKHealthStore() // Creates an instance of the healthkit store
//State of the app - is the workout activated
var workoutActive = false
// define the activity type and location
var hkSession : HKWorkoutSession? // ? means the session can be nil
var session = WCSession.default()
let heartRateUnit = HKUnit(from: "count/min")
//var anchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
var currenQuery : HKQuery? // the current query can be nil
override func awake(withContext context: Any?)
super.awake(withContext: context)
if WCSession.isSupported()
session.delegate = self
session.activate()
override func willActivate()
super.willActivate()
// checks if the health data is avaiable
guard HKHealthStore.isHealthDataAvailable() == true else
heartRateLabel.setText("not available")
return
guard let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else
displayNotAllowed()
return
let dataTypes = Set(arrayLiteral: quantityType)
healthStore.requestAuthorization(toShare: dataTypes, read: dataTypes) (success, error) -> Void in
if success == false
self.displayNotAllowed()
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void)
DispatchQueue.main.async()
guard let m = message["m"] as? String else return
self.dataLabel.setText(m)
@available(watchOSApplicationExtension 2.2, *)
func session(_: WCSession, activationDidCompleteWith: WCSessionActivationState, error: Error?)
func displayNotAllowed()
heartRateLabel.setText("not allowed")
// Function used to set the state of the workout, if it's still "running" then call workoutDidStart else call workoutDidEnd
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date)
switch toState
case .running:
workoutDidStart(date)
case .ended:
workoutDidEnd(date)
default:
print("Unexpected state \(toState)")
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error)
// Do nothing for now
print("Workout error")
// function used to query the healthkitstore when the workout is active
func workoutDidStart(_ date : Date)
if let query = createHeartRateStreamingQuery(date)
self.currenQuery = query
healthStore.execute(query)
else
heartRateLabel.setText("cannot start")
func workoutDidEnd(_ date : Date)
healthStore.stop(self.currenQuery!)
heartRateLabel.setText("---")
hkSession = nil
// MARK: - Actions
@IBAction func startBtnTapped()
if (self.workoutActive)
//finish the current workout
self.workoutActive = false
self.startStopButton.setTitle("Start")
if let workout = self.hkSession
healthStore.end(workout)
else
//start a new workout
self.workoutActive = true
self.startStopButton.setTitle("Stop")
startWorkout()
func startWorkout()
// If we have already started the workout, then do nothing.
if (hkSession != nil)
return
// Configure the workout session.
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .crossTraining
workoutConfiguration.locationType = .indoor
do
hkSession = try HKWorkoutSession(configuration: workoutConfiguration)
hkSession?.delegate = self
catch
fatalError("Unable to create the workout session!")
healthStore.start(self.hkSession!)
// function crates a heartrate query for the healthstore. it returns an optional HKQuery
func createHeartRateStreamingQuery(_ workoutStartDate: Date) -> HKQuery?
guard let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else return nil
let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictEndDate )
//let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()])
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate])
let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
//guard let newAnchor = newAnchor else return
//self.anchor = newAnchor
self.updateHeartRate(sampleObjects)
heartRateQuery.updateHandler = (query, samples, deleteObjects, newAnchor, error) -> Void in
//self.anchor = newAnchor!
self.updateHeartRate(samples)
return heartRateQuery
// updates the display on the UI, heartRateLabel and calls animateHeart for the heart enlargment
func updateHeartRate(_ samples: [HKSample]?)
guard let heartRateSamples = samples as? [HKQuantitySample] else return
var value = 0.0
DispatchQueue.main.async
guard let sample = heartRateSamples.first elsereturn
value = sample.quantity.doubleValue(for: self.heartRateUnit)
self.heartRateLabel.setText(String(UInt16(value)))
// retrieve source from sample
let name = sample.sourceRevision.source.name
self.updateDeviceName(name)
self.animateHeart()
if (self.session.isReachable)
self.sendMessage(value: value)
// function to create and send the message the iphone
func sendMessage(value: Double)
let strValue = String(UInt16(value))
let message = [ "m": strValue ]
self.session.sendMessage(message, replyHandler:nil, errorHandler: (error) -> Void in
print("Watch send failed with error \(error)")
)
func updateDeviceName(_ deviceName: String)
deviceLabel.setText(deviceName)
func animateHeart()
self.animate(withDuration: 0.5)
self.heart.setWidth(60)
self.heart.setHeight(90)
let when = DispatchTime.now() + Double(Int64(0.5 * double_t(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.global(qos: .default).async
DispatchQueue.main.asyncAfter(deadline: when)
self.animate(withDuration: 0.5, animations:
self.heart.setWidth(50)
self.heart.setHeight(80)
)
【问题讨论】:
【参考方案1】:WCErrorCodeTransferTimedOut->IDSErrorTypeTimedOut->IDSResponseTimedOut
我通过重启 iphone 和 watch 解决了这个错误。
【讨论】:
以上是关于iPhone 和 Apple Watch 之间的 WatchConnectivity 超时错误的主要内容,如果未能解决你的问题,请参考以下文章
iPhone 和 Apple Watch 之间的 WatchConnectivity 超时错误
Watch OS 2:是不是可以获得配对的 iPhone 和 Apple Watch 之间连接的蓝牙信号强度?
有没有办法确定 Apple Watch 和 iPhone 之间的粗略距离?
如何在 iPhone 应用程序和 Apple Watch 之间共享数据?