1

I am trying to get haptic feedback when an app is in the background. Essentially when a certain value exceeds a threshold there should be haptic feedback. From what I can see I have everything setup correctly; the function that I am calling when the threshold is surpassed I know is being called because I can see it in the log, but no sound or vibration is happening. So I am not sure what it is I am doing wrong. It works fine when the app is shown on the screen, but not in background.

This is using an older Apple Sample Code (I am aware of the depreciated items). My end goal is to have a baseball pitching analysis app, and the haptics is to reinforce learned muscle memory, kind of like clicker training with dogs.

Here is the MotionManager that pulls in CoreMotion.

import Foundation
import CoreMotion
import WatchKit
import os.log

protocol MotionManagerDelegate: class {
    func didUpdateMotion(_ manager: MotionManager, gravityStr: String, rotationRateStr: String, userAccelStr: String, attitudeStr: String, rollInt: Double)
}

extension Date {
    var millisecondsSince1970:Int64 {
        return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
    }
}

class MotionManager {
// MARK: Properties

let motionManager = CMMotionManager()
let queue = OperationQueue()
let wristLocationIsLeft = WKInterfaceDevice.current().wristLocation == .left

// MARK: Application Specific Constants

// The app is using 50hz data and the buffer is going to hold 1s worth of data.
let sampleInterval = 1.0 / 50
let rateAlongGravityBuffer = RunningBuffer(size: 50)

weak var delegate: MotionManagerDelegate?

var gravityStr = ""
var rotationRateStr = ""
var userAccelStr = ""
var attitudeStr = ""
var rollInt = 0.0

var recentDetection = false

// MARK: Initialization

init() {
    // Serial queue for sample handling and calculations.
    queue.maxConcurrentOperationCount = 1
    queue.name = "MotionManagerQueue"
}

// MARK: Motion Manager

func startUpdates() {
    if !motionManager.isDeviceMotionAvailable {
        print("Device Motion is not available.")
        return
    }

    os_log("Start Updates");

    motionManager.deviceMotionUpdateInterval = sampleInterval
    motionManager.startDeviceMotionUpdates(to: queue) { (deviceMotion: CMDeviceMotion?, error: Error?) in
        if error != nil {
            print("Encountered error: \(error!)")
        }

        if deviceMotion != nil {
            self.processDeviceMotion(deviceMotion!)
        }
    }
}

func stopUpdates() {
    if motionManager.isDeviceMotionAvailable {
        motionManager.stopDeviceMotionUpdates()
    }
}

// MARK: Motion Processing

func processDeviceMotion(_ deviceMotion: CMDeviceMotion) {
    gravityStr = String(format: "X: %.1f Y: %.1f Z: %.1f" ,
                        deviceMotion.gravity.x,
                        deviceMotion.gravity.y,
                        deviceMotion.gravity.z)
    userAccelStr = String(format: "X: %.1f Y: %.1f Z: %.1f" ,
                       deviceMotion.userAcceleration.x,
                       deviceMotion.userAcceleration.y,
                       deviceMotion.userAcceleration.z)
    rotationRateStr = String(format: "X: %.1f Y: %.1f Z: %.1f" ,
                          deviceMotion.rotationRate.x,
                          deviceMotion.rotationRate.y,
                          deviceMotion.rotationRate.z)
    attitudeStr = String(format: "r: %.1f p: %.1f y: %.1f" ,
                             deviceMotion.attitude.roll,
                             deviceMotion.attitude.pitch,
                             deviceMotion.attitude.yaw)

    rollInt = deviceMotion.attitude.roll

    let timestamp = Date().millisecondsSince1970

    os_log("Motion: %@, %@, %@, %@, %@, %@, %@, %@, %@, %@, %@, %@, %@",
           String(timestamp),
           String(deviceMotion.gravity.x),
           String(deviceMotion.gravity.y),
           String(deviceMotion.gravity.z),
           String(deviceMotion.userAcceleration.x),
           String(deviceMotion.userAcceleration.y),
           String(deviceMotion.userAcceleration.z),
           String(deviceMotion.rotationRate.x),
           String(deviceMotion.rotationRate.y),
           String(deviceMotion.rotationRate.z),
           String(deviceMotion.attitude.roll),
           String(deviceMotion.attitude.pitch),
           String(deviceMotion.attitude.yaw))

    updateMetricsDelegate();
}

// MARK: Data and Delegate Management

func updateMetricsDelegate() {
    delegate?.didUpdateMotion(self,gravityStr:gravityStr, rotationRateStr: rotationRateStr, userAccelStr: userAccelStr, attitudeStr: attitudeStr, rollInt: rollInt)
}
}

Here is my WorkOutMangager where the workout session is controlled. The function I am calling is at the very end. It is pulling in values from the MotionManagerDelagate.

import Foundation
import HealthKit
import WatchKit
import os.log

protocol WorkoutManagerDelegate: class {
    func didUpdateMotion(_ manager: WorkoutManager, gravityStr: String, rotationRateStr: String, userAccelStr: String, attitudeStr: String)
}

class WorkoutManager: MotionManagerDelegate {
    // MARK: Properties
    let motionManager = MotionManager()
    let healthStore = HKHealthStore()

var rollInt = 0.0

weak var delegate: WorkoutManagerDelegate?
var session: HKWorkoutSession?

// MARK: Initialization

init() {
    motionManager.delegate = self
}

// MARK: WorkoutManager

func startWorkout() {
    // If we have already started the workout, then do nothing.
    if (session != nil) {
        return
    }

    // Configure the workout session.
    let workoutConfiguration = HKWorkoutConfiguration()
    workoutConfiguration.activityType = .tennis
    workoutConfiguration.locationType = .outdoor

    do {
        session = try HKWorkoutSession(configuration: workoutConfiguration)
    } catch {
        fatalError("Unable to create the workout session!")
    }

    // Start the workout session andKAKAHA.watchWatch.watchkitapp device motion updates.
    healthStore.start(session!)
    motionManager.startUpdates()

    if(rollInt < -0.9){
        WKInterfaceDevice.current().play(.failure)
        os_log("ALERT!!!")
    }
}

func stopWorkout() {
    // If we have already stopped the workout, then do nothing.
    if (session == nil) {
        return
    }

    // Stop the device motion updates and workout session.
    motionManager.stopUpdates()
    healthStore.end(session!)

    // Clear the workout session.
    session = nil
}

// MARK: MotionManagerDelegate

func didUpdateMotion(_ manager: MotionManager, gravityStr: String, rotationRateStr: String, userAccelStr: String, attitudeStr: String, rollInt: Double) {

    delegate?.didUpdateMotion(self, gravityStr: gravityStr, rotationRateStr: rotationRateStr, userAccelStr: userAccelStr, attitudeStr: attitudeStr)

    self.rollInt = rollInt
    rollAlert()
}

@objc fileprivate func rollAlert(){
    if (rollInt < -0.9) {
        WKInterfaceDevice.current().play(.failure)
        os_log("TOO CLOSE TO FACE!!!")
    }
   }
}
Geppelt
  • 363
  • 2
  • 14

1 Answers1

1

I feel stupid. I did not have 'Audio' selected as a Background Mode.

Geppelt
  • 363
  • 2
  • 14