1

While working with data sharing between iOS app and Today Extension, I faced the problem that the NSUserDefaultsDidChangeNotification is never sent from either main app or extension when I change UserDefaults. The thing is that I can read and write data to the UserDefaults successfully for the App Group I created. So the data is actually shared by the app and extension. But the notification of the UserDefaults change is never fired (or detected). Can somebody tell me what can be an issue?

The writing of the data in the UserDefaults

NSUserDefaults defaults = new NSUserDefaults("group.com.name1.name2",NSUserDefaultsType.SuiteName);
defaults.SetString("UPDATE " + DateTime.Now.Minute, "data");
defaults.Synchronize();

The notification handler

NSNotificationCenter.DefaultCenter.AddObserver(
                NSValueTransformer.UserDefaultsDidChangeNotification, (notification) => {
    NSUserDefaults defaults = new NSUserDefaults("group.com.name1.name2",NSUserDefaultsType.SuiteName);
    string str = defaults.StringForKey("data");  
});
RadioLog
  • 582
  • 1
  • 4
  • 20
  • 1
    Refre to this question. https://stackoverflow.com/questions/28284989/nsuserdefaultsdidchangenotification-and-today-extensions – Nilesh Jha Aug 31 '17 at 09:26
  • @Nilesh I have tried the CFNotificationCenter instead, but with the same results. The notification does not fire still – RadioLog Aug 31 '17 at 10:20

2 Answers2

2

You can use CFNotificationCenter from your container app to post a cross-process notification to your extension app.

Shared Constants:

const string id = "group.sushihangover";
const string key = "LastUpdateTime";

Container app / Setup an observer on your NSUserDefaults object:

var todayWidgetUserDefaults = new NSUserDefaults(id, NSUserDefaultsType.SuiteName);

NSValueTransformer.Notifications.ObserveUserDefaultsDidChange(todayWidgetUserDefaults,(sender, e) => 
{
     CFNotificationCenter.Darwin.PostNotification(id, todayWidgetUserDefaults, null, true, true);
});

Today Extension App:

var todayWidgetUserDefaults = new NSUserDefaults(id, NSUserDefaultsType.SuiteName);

void ObserverAction(string notificationId, NSDictionary userInfo)
{
    if (notificationId == id)
    {
        Console.WriteLine(todayWidgetUserDefaults.StringForKey(key));
    }
}
var observerToken = CFNotificationCenter.Darwin.AddObserver(id, todayWidgetUserDefaults, ObserverAction, CFNotificationSuspensionBehavior.DeliverImmediately);

Note: App Group entitlements must be setup in both your container app and the Today Extension App.

SushiHangover
  • 73,120
  • 10
  • 106
  • 165
  • I tried your solution, but unfortunately it still doesn't work. Can this be caused by the fact that I am using simulator to test? – RadioLog Aug 31 '17 at 13:34
  • @tomkou Works fine on the simulator. Of course you can only debug a single process and iOS stops your extension when you leave the today screen, so start a debugging session on your Today extension, place a breakpoint within the ObserverAction, then go to your container app, modify a NSUserDefault key/value (ie. via button touch upinside), now go back to phone's Today screen so your extension is running again and you should get the change notification. – SushiHangover Aug 31 '17 at 14:31
  • 1
    @SushiHangover Awesome thanks. That helped me alot. Used this to inform my View from my Notification Service Extension after a new Push-Message arrived. – dragondx Sep 16 '21 at 14:11
-1

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.

RadioLog
  • 582
  • 1
  • 4
  • 20