9

I have played around with the new iOS 14 widgets, and seems to be working almost fine, except the auto-refresh. My goal is to have my widget refreshed every midnight (so, only once per day, and of course occasionally if something happens in my app). In XCode I played around with it setting to refresh every minute and seemed to work quite randomly - so it maybe refreshed once in my tested 10 minutes, but thought, it's OK as I read refreshes are handled by iOS so it might come sooner or later, but will eventually come.

Now, I have published this version of my app and today morning (around 8am, so well after midnight) I checked my widget and didn't update today.

My question, am I doing something wrong? Or is it simply not that stable yet?

My timeline contains only one item with date: now and refresh policy set to .after(nextMidnight). I also tried it with refresh policy .atEnd, but as I have only one item in the timeline which is set to now, wouldn't it try to refresh continuosly? I thought, .after is the better approach.

If it's relevant, it is StaticConfiguration and not IntentConfiguration (to be honest, I still have to dig into what the difference here is...).

The code snippet:

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let now = Date()
        let calendar = Calendar.current
        var dateComponents = DateComponents()
        dateComponents.year = calendar.component(.year, from: now)
        dateComponents.month = calendar.component(.month, from: now)
        dateComponents.day = calendar.component(.day, from: now) + 1
        dateComponents.hour = 0
        dateComponents.minute = 0
        dateComponents.second = 0
        let nextUpdate = calendar.date(from: dateComponents)
        
        completion(Timeline(entries: [SimpleEntry(date: now)], policy: .after(nextUpdate!)))
    }
pawello2222
  • 46,897
  • 22
  • 145
  • 209
zdtorok
  • 594
  • 1
  • 7
  • 21
  • 1
    you can easily get the nextUpdate using the `Calendar.startOfDay` instead of creating a date component like below `Calendar(identifier: .gregorian).startOfDay(for: Date())` – Srinivasan Raman Oct 26 '20 at 11:55
  • For future readers this might be helpful: [How to refresh Widget data?](https://stackoverflow.com/q/63976424/8697793) and [How to change view on midnight in WidgetKit, SwiftUI?](https://stackoverflow.com/q/64209612/8697793) – pawello2222 Jan 14 '21 at 19:58

2 Answers2

4

This should save you a few lines:

let nextUpdate = Calendar.autoupdatingCurrent.date(byAdding: .day, value: 1, to: Calendar.autoupdatingCurrent.startOfDay(for: Date()))!
xornoz
  • 79
  • 6
  • For some reason this causes my widget not to load before the first day passes, even though the current `Date()` is being used for the first timeline entry. Am I missing something? – Jprofficial Nov 20 '20 at 20:02
  • You need to have an initial widget load/reload. I don't know how you want it but I have mine as `if #available(iOS 14.0, *) { WidgetCenter.shared.reloadAllTimelines() print("Widgets relaoded!") }` Whenever the app loads and whenever I need widget data reloaded immediately. – xornoz Nov 20 '20 at 22:20
  • I thought the widget gets initially loaded on the first timeline entries date. How do you guarantee your code gets called when the widget is placed? IE, they place the widget but don't open your app. I am misunderstanding. Appreciate your help. – Jprofficial Nov 21 '20 at 04:08
3

Actually, it seems to be I overlooked it, the widget actually refreshes, it was me, who didn't refresh the data itself. (I'm using UserDefaults to share data between my app and the widget, and even though the widget refreshed properly, as the data in UserDefaults was not updated, the same was shown over and over again.)

zdtorok
  • 594
  • 1
  • 7
  • 21