Nsuserdefaultsdidchangenotification and Today Extensions

NSUserDefaultsDidChangeNotification and Today Extensions

From doc here:

Cocoa includes two types of notification centers:
The NSNotificationCenter class manages notifications within a single
process. The NSDistributedNotificationCenter class manages
notifications across multiple processes on a single computer.

Apparently the containing app and today extension are different processes, since when you debug today extension you want to attach containing app process, but NSNotificationCenter only work within a single process.

In order to communicate between containing app and extensions, you can use
Darwin Notify Center CFNotificationCenterthat works like NSDistributedNotificationCenter, which is only available for osx.

The idea is use a file inside the group folder that they share. in containing app, you write the data you want to send into the file, then post a CFNotification, which will be received by today extension.

In today extension, use CFNotificationCenterAddObserver to observer the CFNotification, upon receiving it, callback will be called, in which a NSNotification has to be posted due to callback is a C style function and "userInfo" cannot be passed in the CFNotification, after receiving this NSNotification object, it starts to read data from the file, which is used to update the today extension view in Notification center.

You can use this github code to implement force loading the today extension view. It works for me.

Here is a great post on this. http://www.atomicbird.com/blog/sharing-with-app-extensions

Another option is to use setHasContent function. When you schedule a local identifier, set has content to false to hide the view, in handleActionWithIdentifier set it to true to show the view. This way, when you stay in notification center, you will not see the view for a moment, but when you see it, it will be the updated data.

let widgetController = NCWidgetController.widgetController()
widgetController.setHasContent(false, forWidgetWithBundleIdentifier: "YourTodayWidgetBundleIdentifier")

But I think the whole problem is a rare case, which doesn't need to be fixed since you can get the updated data reloading the notification center or switch to notification tab and switch back to today tab.

NSUserDefaultsDidChangeNotification not sent

So, SushiHangover provided perfectly working piece of code.
However, the problem was in the Info.plist file. In order to be able to exchange the notification the Background Mode should be enable for the app with Remote Notification feature. It sounds like a very obvious thing to do, but none of the tutorials for exchanging data between Extension and app with User Defaults that I read mentioned this. Perhaps, it is an obvious thing to do. But I am writing this just in case somebody might have missed this thing too.

NSUserDefaultsDidChangeNotification: What's the name Of the Key, that Changed?

As others stated, there is no way to get the info about the changed key from the NSUserDefaultsDidChange Notification. But there is no need to duplicate any content and check for yourself, because there is Key Value Observing (KVO) which also works with the NSUserDefaults, if you need to specifically be notified of a certain property:

First, register for KVO instead of using the NotificationCenter:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults addObserver:self
forKeyPath:@"nameOfThingIAmInterestedIn"
options:NSKeyValueObservingOptionNew
context:NULL];

don't forget to remove the observation (e.g. in viewDidUnload or dealloc)

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObserver:self forKeyPath:@"nameOfThingIAmInterestedIn"];

and finally implement this method to receive KVO notifications

-(void)observeValueForKeyPath:(NSString *)keyPath 
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(@"KVO: %@ changed property %@ to value %@", object, keyPath, change);
}

Save and Load Data on Today Extensions (iOS 8)

You need to use app group identifier instead of com.*
For instance:

NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:@"group.company.appgroup"];

Don't forget to synchronise when you store data

[shared synchronize];

How to send and receive data in Today extensions

Apart from NSUserDefaults, you can use the NSNotificationCenter to send or receive data anywhere.

You need to set the observer where you can receive the data like below:

override func viewDidLoad() {
super.viewDidLoad()

NSNotificationCenter.defaultCenter().addObserver(self, selector: "dataReceived:", name: "SpecialKey", object: nil)
}

Funciton to catch data:

func dataReceived(notification: NSNotification) {
//deal with notification.userInfo
println(notification.userInfo)

println("Received Data")
}

And you need to define NSNotificationCenter from where you need to send the data:

NSNotificationCenter.defaultCenter().postNotificationName("SpecialKey", object: nil, userInfo: ["MyData": "Demo"])

References:

The complete guide to NSNotificationCenter

Hope it helps!

http://moreindirection.blogspot.in/2014/08/nsnotificationcenter-swift-and-blocks.html

Refreshing a Today extension view

You can use shared NSUserDefaults and observer it.

In your app:

After you update database, execute the below:

NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.yourcompany.sharedDefaults"];
[userDefaults setObject:[NSDate date] forKey:@"updatedDate"];
[userDefaults synchronize];


In your Today Widget:

add a observer in viewDidLoad:

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(userDefaultsDidChange:)
name:NSUserDefaultsDidChangeNotification
object:nil];

then

- (void)userDefaultsDidChange:(NSNotification *)notification {
// check updatedDate and update widget UI
}

refer: http://www.glimsoft.com/06/28/ios-8-today-extension-tutorial/



Related Topics



Leave a reply



Submit