我正在尝试通过使用GPS找出用户的位置并跟踪用户的心率。就位置功能(在单独的类中构建)而言,我正在使用CLLocationManager,在心率方面,我正在使用HealthKit框架。我已经在infop.list中设置了所有必要的属性。但是,我不明白为什么对于位置功能根本不显示授权提示,而就心率功能而言,该提示在很大的延迟后才出现。如果用户未授予授权权限,则无法查询任何值。在定位功能方面,我尝试在viewController中实现本地化功能,以及首先触发提示并将其重新用于手表。 (但是,这不应该是这样)到目前为止,我已经尝试了许多方法,但是无法解决。我希望你能帮助我。
我认为应该重点关注的类是LocationOutsideManager和HeartRateManager,它们都在InterfaceController中使用。
import WatchKit
import Foundation
import CoreLocation
protocol LocationOutsideDelegate {
func processNewLocation(newLocation:CLLocation)
func processLocationFailure(error:NSError)
}
class LocationOutsideManager: NSObject {
let locationManager = CLLocationManager()
var delegate: LocationOutsideDelegate
let authorizationStatus = CLLocationManager.authorizationStatus()
// setting up the delegate
init(delegate:LocationOutsideDelegate){
self.delegate = delegate
super.init()
// location service is "on" on users device
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
// fast, but lower accuracy
locationManager.desiredAccuracy = kCLLocationAccuracyBest
checkLocationAuthorization(status: authorizationStatus)
}
}
func checkLocationAuthorization(status: CLAuthorizationStatus){
switch status {
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted, .denied:
print("Location data is not available")
case .authorizedAlways, .authorizedWhenInUse:
locationManager.startUpdatingLocation()
default:
break
}
}
}
extension LocationOutsideManager: CLLocationManagerDelegate {
// whenever authorization changes we want to check for state
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization(status: status)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// just interested in last and recent location in loc array
guard let location = locations.last else{ return }
// pass location data in delegate
delegate.processNewLocation(newLocation: location)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
guard let locationError = error as? CLError else {
print("other error: ", error.localizedDescription)
return
}
switch locationError {
case CLError.locationUnknown:
delegate.processLocationFailure(error: locationError as NSError)
print("location unknown")
case CLError.denied:
delegate.processLocationFailure(error: locationError as NSError)
print("denied")
default:
delegate.processLocationFailure(error: locationError as NSError)
print("other Core Location error")
}
delegate.processLocationFailure(error: locationError as NSError)
}
}
import Foundation
import HealthKit
protocol HeartRateManagerDelegate {
func handleNewHeartRate(newHeartRate:Double)
}
class HeartRateManager: NSObject{
let heartRateManager = HKHealthStore()
var delegate: HeartRateManagerDelegate
var session: HKWorkoutSession?
var currentQuery: HKQuery?
init(delegate: HeartRateManagerDelegate){
self.delegate = delegate
super.init()
}
func startWorkout(){
// If a workout has already been started, do nothing.
if (session != nil) {
return
}
// Configure the workout session.
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .running
workoutConfiguration.locationType = .outdoor
do {
session = try HKWorkoutSession(configuration: workoutConfiguration)
session?.delegate = self
} catch {
fatalError("Unable to create workout session")
}
heartRateManager.start(self.session!)
}
func stopWorkout(){
if(session == nil){
return
}
//heartRateManager.end(session!)
session = nil
}
// query for heartrate values
func heartRateQuery(_ startDate: Date) -> HKQuery? {
let datePredicate = HKQuery.predicateForSamples(withStart: startDate, end: nil, options: .strictEndDate)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate])
let heartRateQuery = HKAnchoredObjectQuery(type: HeartRate.hrType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
//Do nothing
}
heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
guard let samples = samples as? [HKQuantitySample] else {return}
DispatchQueue.main.async {
guard let sample = samples.first else { return }
// after extraction of bpm value conversion to double
let value = sample.quantity.doubleValue(for: HKUnit(from: "count/min"))
print("Here is hv\(value)")
self.delegate.handleNewHeartRate(newHeartRate: value)
}
}
return heartRateQuery
}
}
extension HeartRateManager: HKWorkoutSessionDelegate{
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
switch toState {
case .running:
//print(date)
if let query = heartRateQuery(date){
self.currentQuery = query
heartRateManager.execute(query)
}
//Execute Query
case .ended:
//Stop Query
heartRateManager.stop(self.currentQuery!)
session = nil
default:
print("Unexpected state: \(toState)")
}
}
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
fatalError(error.localizedDescription)
}
func workoutSession(_ workoutSession: HKWorkoutSession, didGenerate event: HKWorkoutEvent) {
print("\(event) generated!")
}
}
import WatchKit
import Foundation
import HealthKit
import AVFoundation
import CoreMotion
// generate a short unique id
struct ShortCodeGenerator {
private static let base62chars = [Character]("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
private static let maxBase : UInt32 = 62
static func getCode(withBase base: UInt32 = maxBase, length: Int) -> String {
var code = ""
for _ in 0..<length {
let random = Int(arc4random_uniform(min(base, maxBase)))
code.append(base62chars[random])
}
return code
}
}
// Date will be constructed in database --> server side
class InterfaceController: WKInterfaceController {
var saveUrl: URL?
// instance of locationOutside exist already at runtime
var locationManager: LocationOutsideManager!
let healthService: HealthDataService = HealthDataService()
var heartRateManager: HeartRateManager!
// Outlets for testing
@IBOutlet weak var button: WKInterfaceButton!
@IBOutlet weak var furtherSigLabels: WKInterfaceLabel!
var settings = [String : Any]()
// distinguish start recording heartbeat
var isRecording = false
//For workout session
var currentQuery: HKQuery?
var filename: String?
var motionManager: MovementManager!
var movement: String = ""
var manualLat: Double = 0.0
var manualLong: Double = 0.0
override func awake(withContext context: Any?) {
super.awake(withContext: context)
healthService.authorizeHealthKitAccess { (success, error) in
if success {
print("HealthKit authorization received.")
} else {
print("HealthKit authorization denied!")
if error != nil {
print("\(String(describing: error))")
}
}
}
heartRateManager = HeartRateManager(delegate: self)
// initialize locationManager
locationManager = LocationOutsideManager(delegate: self)
motionManager = MovementManager(delegate: self)
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
motionManager.startUpdatingMotions()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
motionManager.stopUpdatingMotions()
}
func sendToServer(params : Dictionary<String, String>){
guard let url = URL(string:"http://13.125.244.168:80") else
{ print("URL could not be created")
return
}
let requestBody = try? JSONSerialization.data(withJSONObject: params, options: [])
var urlRequest = URLRequest(url: url)
//urlRequest.timeoutInterval = 240
urlRequest.httpMethod = "POST"
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
urlRequest.httpBody = requestBody
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
print("error:", error)
return
}
do {
guard let data = data else { return }
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject] else { return }
print("json:", json)
} catch {
print("error:", error)
}
}
task.resume()
}
@IBAction func manualBtnPressed() {
// manual reporting functionality
// generating 6 character long unique id
let uniqueId = ShortCodeGenerator.getCode(length: 6)
let txtMsg = "I am student \(uniqueId). I need help!"
print(txtMsg)
if manualLat != 0.0 && manualLong != 0.0 {
var latStr = String(format:"%.2f",manualLat)
var longStr = String(format:"%.2f",manualLong)
let request = NSMutableURLRequest(url: NSURL(string: "http://147.46.242.219/addmanual.php")! as URL)
request.httpMethod = "POST"
let postString = "a=\(manualLat)&b=\(manualLong)&c=\(txtMsg)"
print(postString)
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
//print("error=\(error)")
return
}
//print("response = \(response)")
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
//print("responseString = \(responseString)")
}
task.resume()
}
}
// when button clicked label is shown
@IBAction func btnPressed() {
if(!isRecording){
let stopTitle = NSMutableAttributedString(string: "Stop Recording")
stopTitle.setAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], range: NSMakeRange(0, stopTitle.length))
button.setAttributedTitle(stopTitle)
isRecording = true
heartRateManager.startWorkout() //Start workout session/healthkit streaming
} else {
let exitTitle = NSMutableAttributedString(string: "Start Recording")
exitTitle.setAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], range: NSMakeRange(0, exitTitle.length))
button.setAttributedTitle(exitTitle)
isRecording = false
heartRateManager.stopWorkout()
//healthStore.end(session!)
}
}
}
extension InterfaceController: LocationOutsideDelegate {
func processNewLocation(newLocation: CLLocation) {
let latitude = newLocation.coordinate.latitude
let longitude = newLocation.coordinate.longitude
print("Latitude \(latitude)")
print("Longitude \(longitude)")
let stringFrLat = "\(latitude)"
let stringFrLong = "\(longitude)"
var locationData = ["latitude": stringFrLat, "longitude":stringFrLong] as Dictionary<String,String>
sendToServer(params: locationData)
}
func processLocationFailure(error: NSError) {
print(error)
}
}
extension InterfaceController: HeartRateManagerDelegate {
func handleNewHeartRate(newHeartRate: Double) {
print("New Heartrate \(newHeartRate)")
}
}
extension InterfaceController: MovementDelegate {
func evalMovForSending(toSend: Bool, gravStr: String, accelStr: String, rotationStr: String, attStr: String){
let tmp = "\(gravStr), \(accelStr), \(rotationStr), \(attStr), "
if toSend{
print("Student has fallen down!")
movement = "\(tmp) _1"
print(movement)
}else{
movement = "\(tmp) _2"
print(movement)
}
}
}
这里是xcode项目:https://filebin.net/0ybr2co7qutulfxx。我希望你能帮助我。非常感谢你。
我也遇到同样的问题,您找到任何解决办法了吗?