0

I had tested this extensively in the past before starting the project I'm working on. I copied and pasted the code into my new project, so it should work. All I want it to do is reload the tableView every minute, so that the clocks displayed on the tableView reload.

func setMinutesForReload(){
    
    //get calender object
    let calender = Calendar.current
    //curent date
    let now = Date()
    //create an array of dates to store minutes
    var minutes = [Date]()
    //timer array to store timers
    var timers = [Timer]()
    
    //set a run loop
    for i in 0...23{
        for j in 0...59{
            minutes.append(calender.date(bySettingHour: i, minute: j, second: 0, of: now)!)
            timers.append(Timer(fireAt: minutes[j], interval: 0, target: self, selector: #selector(minutelyReloadTimer), userInfo: nil, repeats: false))
            RunLoop.main.add(timers[j], forMode: RunLoop.Mode.common)
        }
    }
    
} 

@objc func minutelyReloadTimer(){
    self.cityTableView.reloadData()
}

Oddly enough, when I run the app and set a breakpoint, I see that it calls minutelyReloadTimer() immediately, 59 times in a row (I counted).

enter image description here

Is this an issue with a recent Xcode/Swift update, or am I missing something that I'm not seeing?

jmsapps
  • 388
  • 2
  • 17
  • Don't you have a significant logic error with `minutes.append(calender.date(bySettingHour: 0, minute: i, second: 0, of: now)!)`? I would assume this would only work as expected if you happened to fire it right on the hour. If the goal is just to reload the table every minute, why not just use a single repeating `Timer`? – jnpdx Apr 01 '22 at 22:56
  • I just tried setting the hour parameter to my current hour just to check and its still working incorrectly. It shouldnt be calling multiple times in row. Usually it gets called once when you initialize the observer for the first time – jmsapps Apr 01 '22 at 23:11

1 Answers1

1

I'm certain the unexpected behavior you are witnessing is not due to an issue with Xcode/Swift.

Taking a look at your code, I noticed a couple strange things right off the bat. For one, RunLoop.main.add(timers[j]... will only add timer's 0-59 to the run loop, over and over (24 times to be exact). And for two, calender.date(bySettingHour: i, minute: j, second: 0, of: now)'s default configuration is to find the next time that matches the given data components, since the function's last parameter (i.e. matchingPolicy: Calendar.MatchingPolicy) is assigned a default value of .nextTime. Therefore, the dates that will be returned are for tomorrow, not today.

Now, instead of dissecting your (unnecessarily complicated) method further, I would suggest a much simpler approach: fire a repeating 60 second timer, with the initial fire being on the start of the next minute.

In short, refactoring your code would result in the following.

func setMinutesForReload(){
    
    //get calender object
    let calender = Calendar.current
    //curent date
    let now = Date()
    
    let dateOfNextMinute: Date = calender.nextDate(after: now, matching: DateComponents(second: 0), matchingPolicy: .nextTime)!
    let timer = Timer(fireAt: dateOfNextMinute, interval: 60.0, target: self, selector: #selector(minutelyReloadTimer), userInfo: nil, repeats: true)
    RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
}

@objc func minutelyReloadTimer(){
    print("Reload")
}
Eric
  • 569
  • 4
  • 21
  • Thanks that was very helpful. I'm still confused about one thing however. I had the exact same code written in another project, except it looped just 24 times to set timers for every hour, and it worked (though it doesn't work in this project). I've even been testing that old project every hour and it always reloads on the hour. I feel like I'm taking crazy pills. Can you think of any reason why that could have worked despite that you mention that I am setting timers for the next day? – jmsapps Apr 02 '22 at 12:16
  • I guess now that I'm thinking about it more, it sets timers from tomorrow until the current moment of tomorrow, then it will find dates for today. So technically the dates will be correct. So in theory it would work (for a 24 hour period at least). Not sure if you have any other errors above though. – Eric Apr 02 '22 at 19:09
  • At any rate the code you provided seems to work. One last quick question though, if I wanted to set a timer for every hour (on the hour) I would just set the time interval to 3600? – jmsapps Apr 02 '22 at 23:48
  • Well no, because the initial firing would still be on the next minute, not the next hour. You would have to use `let dateOfNextHour: Date = calender.nextDate(after: now, matching: DateComponents(minute: 0), matchingPolicy: .nextTime)!` as your initial firing date. Then yes, just set your time interval to 3600. – Eric Apr 03 '22 at 00:19
  • Oh okay I see, I tried setting the date components to (hour: 0). I guess hour would be it I wanted to set it for every day, then. – jmsapps Apr 03 '22 at 00:35
  • That is correct. – Eric Apr 04 '22 at 22:18