xamarin / xamarin-macios

.NET for iOS, Mac Catalyst, macOS, and tvOS provide open-source bindings of the Apple SDKs for use with .NET managed languages such as C#
Other
2.45k stars 511 forks source link

iOS support for NSUserDefaults.DidChangeNotification not working. #18193

Closed jfversluis closed 1 year ago

jfversluis commented 1 year ago

Issue moved from dotnet/maui#14857


From @s3743761 on Monday, May 1, 2023 8:25:21 AM

Description

iOS api NSUserDefaults.DidChangeNotification doesn't get invoked when UserDefaults get changed. This is required for MDM implementation in iOS using AppConfig

Steps to Reproduce

  1. Create a MAUI App.
  2. In your platform specific iOS code add the following in constructor
          var observer2 = NSNotificationCenter.DefaultCenter.AddObserver(NSUserDefaults.DidChangeNotification,
          n => { Console.WriteLine("Notified"); });

and create a method as in same class

        public const string MangedConfigurationKey = "com.apple.configuration.managed";

        public partial string GetStringValue(string key)
        {
            var dic = NSUserDefaults.StandardUserDefaults.DictionaryForKey(MangedConfigurationKey);
            if (dic != null)
            {
                NSObject value = null;
                if (dic.TryGetValue(new NSString(key), out value) && value != null)
                    Console.WriteLine(value);
                    return value.ToString();
            }

            return null;
        }
  1. Run the code and see nothing getting printed.
  2. Run a simulator and enter key, value using following command xcrun simctl spawn booted defaults write {apple_bundle_id} com.apple.configuration.managed -dict 'key1' 'VALUE1'.
  3. You will see no the notification is not triggered even though there is a change in NSUserDefaults.
  4. Now Create a button and attach the OnClick to GetStringValue("key1");
  5. Now you will see the value on button click but not with Notification Center
  6. try deleting and re-running the command from step 5 again, same resultxcrun simctl spawn booted defaults delete {apple_bundle_id}

Ideally, the Console.WriteLine("Notified"); should be triggered with the command from step 5.

Link to public reproduction project repository

given in description

Version with bug

7.0 (current)

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

iOS 16 and before

Did you find any workaround?

No response

Relevant log output

No response

jfversluis commented 1 year ago

Issue moved from dotnet/maui#14857


From @msftbot[bot] on Monday, May 1, 2023 8:42:47 AM

Hi @s3743761. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

jfversluis commented 1 year ago

Issue moved from dotnet/maui#14857


From @drasticactions on Monday, May 1, 2023 8:43:37 AM

I recommend trying what you're doing in a dotnet iOS app (dotnet new ios) to see if it works there.

jfversluis commented 1 year ago

Issue moved from dotnet/maui#14857


From @s3743761 on Monday, May 1, 2023 8:59:22 AM

@drasticactions no It still doesn't work. This is my code

[Register ("AppDelegate")]
public class AppDelegate : UIApplicationDelegate {
    public override UIWindow? Window {
        get;
        set;
    }

    public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
    {
        // create a new window instance based on the screen size
        Window = new UIWindow (UIScreen.MainScreen.Bounds);

        // create a UIViewController with a single UILabel
        var vc = new UIViewController ();
        vc.View!.AddSubview (new UILabel (Window!.Frame) {
            BackgroundColor = UIColor.SystemBackground,
            TextAlignment = UITextAlignment.Center,
            Text = "Hello, iOS!",
            AutoresizingMask = UIViewAutoresizing.All,
        });
        Window.RootViewController = vc;

        // make the window visible
        Window.MakeKeyAndVisible ();

        NSNotificationCenter.DefaultCenter.AddObserver(
               NSUserDefaults.DidChangeNotification,
               DefaultsChanged,
               null);
        GetStringValue("key1");

        return true;
    }

    private void DefaultsChanged(NSNotification notification)
    {
        // Get the user defaults
        Console.WriteLine("DefaultsChanged");

    }

    public const string MangedConfigurationKey = "com.apple.configuration.managed";

    public string GetStringValue(string key)
    {
        var dic = NSUserDefaults.StandardUserDefaults.DictionaryForKey(MangedConfigurationKey);
        if (dic != null)
        {
            NSObject value = null;
            if (dic.TryGetValue(new NSString(key), out value) && value != null)
                Console.WriteLine(value.ToString());
        }

        return null;
    }

}

GetStringValue works, but not the notification

rolfbjarne commented 1 year ago

According to Apple's documentation:

This notification isn't posted when changes are made outside the current process

So it looks like this is behaving as designed.