0

I am writing a piece of code to check my CoreMotion activities in the past 90s (which repeats constantly every second), and if it is all non-stationary activities within that 90s timeframe, I would like to alert the user that leave is detected. However, the logic as seen below detects "leave" even though I am stationary for 90s immediately after starting the timer.

logic for dispatchsourcetimer referenced from this answer https://stackoverflow.com/a/53929386/12181863

@Binding var timerFromParent: DispatchSourceTimer?
@State private var fromTime = ""
@State private var toTime = ""

    //-----------------------------------------------------------------------
    //start a background time for x seconds later to check if stationary or there are other activities
    func startTimer(timePeriod: Double) {
        let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".timer")
        timerFromParent = DispatchSource.makeTimerSource(queue: queue)
        timerFromParent!.schedule(deadline: .now() + timePeriod, repeating: .seconds(1))
        timerFromParent!.setEventHandler { //[weak self] in
            //[weak self] only needed if you reference self in this closure and you want to prevent strong reference cycle
            // do whatever stuff you want on the background queue here here
            let returned_result = checkTimePeriod(timeInterval: timePeriod)
            
            if (returned_result == false) {
                DispatchQueue.main.async {
                    // update your model objects and/or UI here
                    leaveDetected = true
                    stopPress()
                    makeRequest(fromTime: fromTime, toTime: toTime, detected: true)
                    stopTimer()    //stopTimer must come after makeRequest is done (timer stops then the whole task disappears unable to complete makeRequest because becomes nil)
                }
            }
        }
        timerFromParent!.resume()
    }

    
    func stopTimer() {
        timerFromParent?.cancel()
        timerFromParent = nil
    }
    
    func checkTimePeriod(timeInterval: Double)->Bool{
        let time = Date()
        let time_period_earlier = time - TimeInterval(timeInterval)
        var detected = false
        
        motionActivityManager.queryActivityStarting(from: time_period_earlier, to: time, to: .main) { motionActivities, error in
            
            if let error = error {
                print("error: \(error.localizedDescription)")
                return
            }
            
            motionActivities?.forEach { activity in
                if activity.confidence == .medium || activity.confidence == .high {
                    if activity.stationary {
                        detected = true
                    }else{
                        //donothing
                    }
                }
            }
            
        }
        
//        if (detected == true) {
//            return true
//        }else {
//            return false
//        }
        return detected
    }
    //-----------------------------------------------------------------------
YHStan
  • 227
  • 3
  • 12
  • I think I narrowed it down to checkTimePeriod function. It constantly returns detected as false, even though stationary activity is detected. ```detected = true``` statement does not seem to help – YHStan Mar 18 '22 at 05:02
  • I double checked documentation and it said that queryActivityStarting() is async, thus before I can even change value of detected, the function has already returned false. Is there anyway to bypass this issue? – YHStan Mar 18 '22 at 05:35

1 Answers1

0

I moved the logic to for detecting leave from startTimer() to checkTimePeriod(). I think now I wont need to wait for the boolean variable from checkTimePeriod because everything is done inside the checkTimePeriod function

func startTimer(timePeriod: Double) {
        let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".timer")
        timerFromParent = DispatchSource.makeTimerSource(queue: queue)
        timerFromParent!.schedule(deadline: .now() + timePeriod, repeating: .seconds(1))
        timerFromParent!.setEventHandler { //[weak self] in
            //[weak self] only needed if you reference self in this closure and you want to prevent strong reference cycle
            // do whatever stuff you want on the background queue here here
            checkTimePeriod(timeInterval: timePeriod)
        }
        timerFromParent!.resume()
    }

func checkTimePeriod(timeInterval: Double){
        let time = Date()
        let time_period_earlier = time - TimeInterval(timeInterval)
        var detected = false
        
        motionActivityManager.queryActivityStarting(from: time_period_earlier, to: time, to: .main) { motionActivities, error in
            
            if let error = error {
                print("error: \(error.localizedDescription)")
                return
            }
            
            motionActivities?.forEach { activity in
                if activity.confidence == .medium || activity.confidence == .high {
                    if activity.stationary {
                        detected = true
                        return
                    }
                }
            }
            
            if (detected == false){
                DispatchQueue.main.async {
                    // update your model objects and/or UI here
                    leaveDetected = true
                    stopPress()
                    makeRequest(fromTime: fromTime, toTime: toTime, detected: true)
                    stopTimer()
                    //stopTimer must come after makeRequest is done (timer stops then the whole task disappears unable to complete makeRequest because becomes nil)
                }
            }
        }
    }
YHStan
  • 227
  • 3
  • 12