Updating a WidgetKit widget from a Siri intents extension

Viewed 346

I have an interesting quandary I've been grappling with for the past few days: I have an app that also has a widget extension and a Siri intents extension. There is a button in the app that updates a shared data file. Siri intents extension also updates that data file and the widget reflects those changes (to avoid conflicts, I'm using the NSFileCoordinator API).

When I switch to the app, tap the button, and return to the springboard, the widget contents are updated. When I invoke the intent through a shortcut, the app is updated. However, when I invoke the intent, the widget is not updated.

An interesting wrinkle is that widget updates if the app is run in the debug mode from Xcode, which leads me to suspect that this is some sort of a timing issue.

The app, the widget and the intent extension use shared code to read from and to write to the shared data file.

Here is some code:

A. Button in the app:

// …

Button("Update") {
  storage.write(updatedData) { (error) in
    guard error == nil else { return }
    WidgetCenter.shared.reloadTimelines(ofKind: "MyWidget")
  }
}

// …

B. Code in the intents extension

// …

storage.write(updatedData) { (error) in
  guard error == nil else {
    completion(MyIntentResponse(code: .failure, userActivity: nil))
    return
  }
            
  WidgetCenter.shared.reloadTimelines(ofKind: "MyWidget")
  completion(MyIntentResponse(code: .success, userActivity: nil))
}

// …

As you can see, I handle the situation when writing the data fails in the intents extesion. If that happens, Siri informs me about it – this is how I know that writing succeeds.

C. Widget entry/timeline code

// …

func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (MyEntry) -> ()) {
  storage.read { (data, error) in
    completion(makeEntryFromData(data))
  }
}
        
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {    
  storage.read { (data, error) in
    let timeline = Timeline(entries: [makeEntryFromData(data)], policy: .never)
    completion(timeline)
  }
}

// …

Here, although the error state seems not to be handled, in actuality, "data" becomes nil when there is an error, and nil values are handled in the view part of the widget – that way I know that data corruption is not the issue either.

Please guide me, as I'm sorely perplexed!

Cheers,
–Baglan

UPDATE: widget seem to update eventually most of the time. My best guess is, there is some some of a mechanism that throttles updates or schedules them for some time convenient for the OS and that results in unpredictable widget update delays.

0 Answers
Related