I am developing an iPhone app with a Today Extension. The app has a Model module that loads from/saves toNSUserDefaults
. Since I want this information to be available to both the main app and the extension, I use an app group:
let storage = NSUserDefaults(suiteName: "group.etc.etc.etc...")
Both the app and the extension can access the information without any problem.
The main app occasionally might create a local notification to present to the user. That notification has two actions associated with it (UIUserNotificationAction
). One of those actions triggers some code run on the background on the main app. That code changes the NSUserDefaults
information and triggers a synchronization. My code is something like this:
func application(application: UIApplication, handleActionWithIdentifier id: String?, forLocalNotification not: UILocalNotification, completionHandler: () -> ()) {
// Interact with model here
// New information gets saved to NSUserDefaults
userDefaultsStorage.synchronize()
completionHandler()
}
Now, on the Today Ext. I naturally observe any changes made to the information on NSUserDefaults
so that I can reload the interface on the widget:
override func viewDidLoad() {
super.viewDidLoad()
// ...
NSNotificationCenter.defaultCenter().addObserverForName(NSUserDefaultsDidChangeNotification, object: nil, queue: NSOperationQueue.mainQueue()) { _ in
self.reload()
}
}
Now, here's my issue:
The main app schedules a UILocalNotification. I open the today view and look at my today widget.
When the notification fires, a banner appears on the top of the screen.
I slide down on that banner to reveal the two actions and I select the one that I mentioned earlier (the today widget is still live and on screen).
I know for a fact that the action runs correctly in the background, and that the changes are being made to the information on NSUserDefaults
.
However, even though the today widget has been active and on screen all this time, no reload action is triggered. After further investigation, I can confirm that the NSUserDefaultsDidChangeNotification
is not being fired (I placed a breakpoint and it did not trigger, and did some other checks as well).
I know the changes are being made by the notification action because if I force a reload of the widget (by closing and opening the today view) the widget updates correctly.
I have seen various tutorials online where the first thing they say is to listen to this notification and update the widget so that "the widget is in sync with NSUserDefaults
". But the thing is that AFAICT this notification is absolutely useless! How come??
Note 1: When I change the information on NSUserDefaults from within the today widget the notification fires correctly.
Note 2: Debugging a today widget is absolutely horrible, btw. It is always necessary to tell Xcode to "Attach to process by name..." before it can react to breakpoints and crashes. And iOS is constantly creating a new process for the widget so I have to constantly tell Xcode to attach again.