1

I'm building an app where I'm getting sensor values. Each time a sensor value changes, the function handleDeviceMotionUpdate is called, which is multiple times per second.

handleDeviceMotionUpdate calls the following function of a different class:

func doStuff(){
     delay(1){
           print("some thing")
     }
}

The delay function looks like this, which I found somewhere here on Stackoverflow:

func delay(_ delay:Double, closure:@escaping ()->()){
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

I expected that "some thing" is only printed after one second has passed, but I believe the problem is that doStuff() is called multiple times per second. Is there some way I can execute code after a certain delay in a function that is called multiple times per second? And why is my example not working?

I thought about creating a boolean variable in the first class that is set to true after 1 second and then calling the function of the different class, but I thought that might clutter my code because I've already done that somewhere else.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 1
    You can just save current `Date` and next time method is called, check that it was incremented for a full second. Will require more work but will be technically correct. Another option is the `Timer` object, which fires every second. – NSDmitry Aug 29 '18 at 18:22
  • I think DispatchTime is in nanoseconds so are you only delaying 1 nanosecond? – seeinvisible Aug 29 '18 at 18:30
  • @seeinvisible it's actually in seconds https://stackoverflow.com/a/37801602/2907715 – ielyamani Aug 29 '18 at 19:32
  • To the OP: What do you mean by "why is my example not working". What does your code print, if at all? – ielyamani Aug 29 '18 at 19:35
  • @Carpsen90 The first time **doStuff()** is called the delay is executed and "some thing" is printed with the correct delay, after that the print statement is executed without a delay at all, that is whenever a sensor value changed. – ThatGuyWhoDoesntGetIt Aug 29 '18 at 22:12
  • @NSDmitry Thanks for your answer, I'll try the **Date** approach next – ThatGuyWhoDoesntGetIt Aug 29 '18 at 22:14
  • @ThatGuyWhoDoesntGetIt You mean you want `doStuff()` to fire every 1 second and not whenever `handleDeviceMotionUpdate` calls it? – ielyamani Aug 29 '18 at 22:22
  • @Carpsen90 Yes, either that or delay some code in the doStuff() function. – ThatGuyWhoDoesntGetIt Aug 29 '18 at 22:41

1 Answers1

1

To do so declare a global timer:

var timer: Timer = Timer(timeInterval: -1, target: self, selector: #selector(ViewControllerName.doStuff), userInfo: nil, repeats: false)

Where ViewControllerName is the name of the class that has doStuff() as a method.

Make sure to invalidate the timer initially (like in viewDidLoad()) :

timer.invalidate()

And here is how your doStuff() might look like:

func doStuff() {
    //As long as the timer is started/vlaid, nothing will be executed
    if !timer.isValid {
        //print something or do whatever you'd like
        print("some thing ", Date()) 

        //and finally restart the timer
        timer = Timer.scheduledTimer(
            timeInterval: 1, //This is the time interval you don't want to do nothing
            target: self, 
            selector: #selector(ViewController.doStuff),
            userInfo: nil,
            repeats: false)
    }
}
ielyamani
  • 17,807
  • 10
  • 55
  • 90