3

I have a widget in my app do like basic weather widget.

I want to refresh the content of the widget without let the user notice that.

And according to Apple documentation go to Updating Content they said:

To help your widget look up to date, the system occasionally captures snapshots of your widget’s view. When the widget becomes visible again, the most recent snapshot is displayed until the system replaces it with a live version of the view. To update a widget’s state before a snapshot is taken, be sure to conform to the NCWidgetProviding protocol.

So I use widgetPerformUpdate to update my widget content

but what happens is the app displays the "initial design" from the MainInterface.storboard until getting the data and update the screen, and each time I swipe left right I got the "initial design" until I got the data!

my code is :

func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {

    getWeatherInfo()

    completionHandler(NCUpdateResult.newData)
}

anyone know what the problem and how to fix it?

Thanks.

Rawan
  • 1,589
  • 4
  • 23
  • 47
  • Show what you have in `widgetPerformUpdateWithCompletionHandler` method – Ladislav Oct 26 '17 at 10:53
  • @Ladislav yes, this is : func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) { getWeatherInfo() completionHandler(NCUpdateResult.newData) } – Rawan Oct 26 '17 at 11:20
  • @Ladislav and inside getWearherInfo() when the request success I update the view – Rawan Oct 26 '17 at 11:23
  • Store the completion Handler and call it when you get back the weather from `getWeatherInfo()` – Ladislav Oct 26 '17 at 11:24
  • @Ladislav thank you so mush, but what did you meant by : "Store the completion Handler " I didn't get you very well :$ – Rawan Oct 26 '17 at 11:27
  • See my answer below how I would do it – Ladislav Oct 26 '17 at 11:45

2 Answers2

5

You should wait and first get the weatherInfo and then initiate the completionHandler in widgetPerformUpdate, I would add a completion block to the getWeatherInfo() method and do it like so:

Add a completionHandler to getWeatherInfo method

func getWeatherInfo(completion: (_ result: Bool) -> Void)

Then when you are done fetching the weather inside getWeatherInfo method call the completion block, here you can specify if new weather data is avalable or not, so you can do

completion(true) if weather was successfully received and then you just rewrite the widgetPerformUpdate like so:

func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
    getWeatherInfo { didUpdate in
        completionHandler(.newData)
        //you can check didUpdate and if it is false return .noData
    }
}
Ladislav
  • 7,223
  • 5
  • 27
  • 31
  • 1
    thank you, but still the widget not capturing the latest result, it back to the initial storyboard design :'( – Rawan Oct 26 '17 at 11:52
  • When you first load the app it will always load from storyboard, then sometimes Today widget would be updated while you are not yet looking at the widgets, but a lot of times it will call perform update when you actually go to today view – Ladislav Oct 26 '17 at 12:50
  • For a widget, It's load from the storyboard each time the notification center area is opened, not only when the app load the first time – Lory Huz Oct 26 '17 at 13:00
  • And when you get the first update, and later on go back notification center you see the old data and it gets replaced or does it go back to storyboard empty data? – Ladislav Oct 26 '17 at 13:03
  • Here is the cycle => Come back -> Old data -> view is recreated -> come back to storyboard empty data -> widget perform update -> API return data – Lory Huz Oct 26 '17 at 13:13
0

The problem is not widgetPerformUpdate method.

Widgets are calling viewDidLoad more often than you think, so it will return back to storyboard design as it recreating the view.

You can hide all UI elements (in viewDidLoad) and show them with fade animations when you have new data like Apple widgets.

Or storing previous fetched values in UserDefaults or local database, to fill UI elements in viewDidLoad, then it won't flick when the view will be recreated.

Lory Huz
  • 1,557
  • 2
  • 15
  • 23
  • thanks for answering, but in this case what is the benefit from widgetPerformUpdate method? in Apple's doc, they said it keeps the latest results as snapshot until it get the data so no need to store any value in defaults right? – Rawan Oct 26 '17 at 12:11
  • widgetPerformUpdate is needed to say if you should refresh the view or not when the view is already created, and you should perform data/model logic here. But each time you quit notification center and come back, viewDidLoad is called, see this: https://stackoverflow.com/a/31306820/1887988 – Lory Huz Oct 26 '17 at 12:57
  • There is another step is useful to take and that's to run viewWillDisappear to clear out any old content you might have loaded and don't want to "flash" before the new content is loaded. – Dan Korkelia Jun 22 '18 at 10:43