iPhone和Apple Watch

时间:2017-04-15 00:11:59

标签: ios swift watch-os-2 health-kit watchconnectivity

我正在尝试使用WatchConnectivity框架将Apple手表中的字符串发送到我的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 else{return}
        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 else{return}
        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 个答案:

答案 0 :(得分:0)

WCErrorCodeTransferTimedOut->IDSErrorTypeTimedOut->IDSResponseTimedOut

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