Skip to content

Commit

Permalink
[RGen] Implement the generation of the Notifications helper class. (#…
Browse files Browse the repository at this point in the history
…22265)

Implement the notifications helper class for those properties marked as
notifications. Code supports the same configuration options as bgen and
the test ensures that all configurations are tested:

1. No special config
2. Special args.
3. Special notification center.
4. Special args and notification center.

---------

Co-authored-by: GitHub Actions Autoformatter <[email protected]>
  • Loading branch information
mandel-macaque and GitHub Actions Autoformatter authored Mar 3, 2025
1 parent 74636f5 commit 49763bb
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 1 deletion.
36 changes: 35 additions & 1 deletion src/rgen/Microsoft.Macios.Generator/Emitters/ClassEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,41 @@ void EmitProperties (in BindingContext context, TabbedWriter<StringWriter> class

void EmitNotifications (in ImmutableArray<Property> properties, TabbedWriter<StringWriter> classBlock)
{
// to be implemented, do not throw or tests will fail.
if (properties.Length == 0)
return;

// default values
const string defaultNotificationCenter = "NSNotificationCenter.DefaultCenter";
const string defaultEventArgument = "Foundation.NSNotificationEventArgs";

// add a space just to make it nicer to read
classBlock.WriteLine ();

// create a nested static class with the notification helpers
using (var notificationClass = classBlock.CreateBlock ("public static partial class Notifications", true)) {
notificationClass.WriteLine ();
// generate two methods per notification
foreach (var notification in properties) {
var count = 12; // == "Notification".Length;
var name = $"Observe{notification.Name [..^count]}";
var notificationCenter = notification.ExportFieldData?.FieldData.NotificationCenter ?? defaultNotificationCenter;
var eventType = notification.ExportFieldData?.FieldData.Type ?? defaultEventArgument;
// use the raw writer which makes it easier to read in this case
notificationClass.WriteRaw (
@$"public static NSObject {name} (EventHandler<{eventType}> handler)
{{
return {notificationCenter}.AddObserver ({notification.Name}, notification => handler (null, new {eventType} (notification)));
}}
public static NSObject {name} (NSObject objectToObserve, EventHandler<{eventType}> handler)
{{
return {notificationCenter}.AddObserver ({notification.Name}, notification => handler (null, new {eventType} (notification)), objectToObserve);
}}
"
);
}
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public class TestDataGenerator : BaseTestDataGenerator, IEnumerable<object []> {
(ApplePlatform.MacOSX, "AppKitPropertyTests", "AppKitPropertyTests.cs", "ExpectedAppKitPropertyTests.cs", null),
(ApplePlatform.MacOSX, "ThreadSafeAppKitPropertyTests", "ThreadSafeAppKitPropertyTests.cs", "ExpectedThreadSafeAppKitPropertyTests.cs", null),

(ApplePlatform.iOS, "NSUserDefaults", "NSUserDefaults.cs", "ExpectedNSUserDefaults.cs", null),
(ApplePlatform.TVOS, "NSUserDefaults", "NSUserDefaults.cs", "ExpectedNSUserDefaults.cs", null),
(ApplePlatform.MacCatalyst, "NSUserDefaults", "NSUserDefaults.cs", "ExpectedNSUserDefaults.cs", null),
(ApplePlatform.MacOSX, "NSUserDefaults", "NSUserDefaults.cs", "ExpectedNSUserDefaults.cs", null),
};

public IEnumerator<object []> GetEnumerator ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,20 @@ public static partial int FormatRGBA16Int
return Dlfcn.GetInt32 (Libraries.TestNamespace.Handle, "FormatRGBA16Int");
}
}

public static partial class Notifications
{

public static NSObject ObserveDidProcessEditing (EventHandler<Foundation.NSNotificationEventArgs> handler)
{
return NSNotificationCenter.DefaultCenter.AddObserver (DidProcessEditingNotification, notification => handler (null, new Foundation.NSNotificationEventArgs (notification)));
}

public static NSObject ObserveDidProcessEditing (NSObject objectToObserve, EventHandler<Foundation.NSNotificationEventArgs> handler)
{
return NSNotificationCenter.DefaultCenter.AddObserver (DidProcessEditingNotification, notification => handler (null, new Foundation.NSNotificationEventArgs (notification)), objectToObserve);
}

}
// TODO: add binding code here
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// <auto-generated />

#nullable enable

using Foundation;
using ObjCBindings;
using ObjCRuntime;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;

namespace Foundation;

[Register ("NSUserDefaults", true)]
public partial class NSUserDefaults
{
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
static readonly NativeHandle class_ptr = Class.GetHandle ("NSUserDefaults");

public override NativeHandle ClassHandle => class_ptr;

[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[DesignatedInitializer]
[Export ("init")]
public NSUserDefaults () : base (NSObjectFlag.Empty)
{
if (IsDirectBinding)
InitializeHandle (global::ObjCRuntime.Messaging.IntPtr_objc_msgSend (this.Handle, global::ObjCRuntime.Selector.GetHandle ("init")), "init");
else
InitializeHandle (global::ObjCRuntime.Messaging.IntPtr_objc_msgSendSuper (this.SuperHandle, global::ObjCRuntime.Selector.GetHandle ("init")), "init");
}

[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected NSUserDefaults (NSObjectFlag t) : base (t) {}

[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected internal NSUserDefaults (NativeHandle handle) : base (handle) {}

static Foundation.NSString? _CompletedInitialSyncNotification;

[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[Advice ("Use 'NSUserDefaults.Notifications.CompletedInitialSyncNotification' helper method instead.")]
public static partial Foundation.NSString CompletedInitialSyncNotification
{
get
{
if (_CompletedInitialSyncNotification is null)
_CompletedInitialSyncNotification = Dlfcn.GetStringConstant (Libraries.Foundation.Handle, "NSUbiquitousUserDefaultsCompletedInitialSyncNotification")!;
return _CompletedInitialSyncNotification;
}
}

static Foundation.NSString? _DidChangeAccountsNotification;

[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[Advice ("Use 'NSUserDefaults.Notifications.DidChangeAccountsNotification' helper method instead.")]
public static partial Foundation.NSString DidChangeAccountsNotification
{
get
{
if (_DidChangeAccountsNotification is null)
_DidChangeAccountsNotification = Dlfcn.GetStringConstant (Libraries.Foundation.Handle, "NSUbiquitousUserDefaultsDidChangeAccountsNotification")!;
return _DidChangeAccountsNotification;
}
}

static Foundation.NSString? _NoCloudAccountNotification;

[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[Advice ("Use 'NSUserDefaults.Notifications.NoCloudAccountNotification' helper method instead.")]
public static partial Foundation.NSString NoCloudAccountNotification
{
get
{
if (_NoCloudAccountNotification is null)
_NoCloudAccountNotification = Dlfcn.GetStringConstant (Libraries.Foundation.Handle, "NSUbiquitousUserDefaultsNoCloudAccountNotification")!;
return _NoCloudAccountNotification;
}
}

static Foundation.NSString? _SizeLimitExceededNotification;

[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[Advice ("Use 'NSUserDefaults.Notifications.SizeLimitExceededNotification' helper method instead.")]
public static partial Foundation.NSString SizeLimitExceededNotification
{
get
{
if (_SizeLimitExceededNotification is null)
_SizeLimitExceededNotification = Dlfcn.GetStringConstant (Libraries.Foundation.Handle, "NSUserDefaultsSizeLimitExceededNotification")!;
return _SizeLimitExceededNotification;
}
}

public static partial class Notifications
{

public static NSObject ObserveCompletedInitialSync (EventHandler<Foundation.MyNotificationArgs> handler)
{
return SharedWorkspace.NotificationCenter.AddObserver (CompletedInitialSyncNotification, notification => handler (null, new Foundation.MyNotificationArgs (notification)));
}

public static NSObject ObserveCompletedInitialSync (NSObject objectToObserve, EventHandler<Foundation.MyNotificationArgs> handler)
{
return SharedWorkspace.NotificationCenter.AddObserver (CompletedInitialSyncNotification, notification => handler (null, new Foundation.MyNotificationArgs (notification)), objectToObserve);
}

public static NSObject ObserveDidChangeAccounts (EventHandler<Foundation.MyNotificationArgs> handler)
{
return NSNotificationCenter.DefaultCenter.AddObserver (DidChangeAccountsNotification, notification => handler (null, new Foundation.MyNotificationArgs (notification)));
}

public static NSObject ObserveDidChangeAccounts (NSObject objectToObserve, EventHandler<Foundation.MyNotificationArgs> handler)
{
return NSNotificationCenter.DefaultCenter.AddObserver (DidChangeAccountsNotification, notification => handler (null, new Foundation.MyNotificationArgs (notification)), objectToObserve);
}

public static NSObject ObserveNoCloudAccount (EventHandler<Foundation.NSNotificationEventArgs> handler)
{
return SharedWorkspace.NotificationCenter.AddObserver (NoCloudAccountNotification, notification => handler (null, new Foundation.NSNotificationEventArgs (notification)));
}

public static NSObject ObserveNoCloudAccount (NSObject objectToObserve, EventHandler<Foundation.NSNotificationEventArgs> handler)
{
return SharedWorkspace.NotificationCenter.AddObserver (NoCloudAccountNotification, notification => handler (null, new Foundation.NSNotificationEventArgs (notification)), objectToObserve);
}

public static NSObject ObserveSizeLimitExceeded (EventHandler<Foundation.NSNotificationEventArgs> handler)
{
return NSNotificationCenter.DefaultCenter.AddObserver (SizeLimitExceededNotification, notification => handler (null, new Foundation.NSNotificationEventArgs (notification)));
}

public static NSObject ObserveSizeLimitExceeded (NSObject objectToObserve, EventHandler<Foundation.NSNotificationEventArgs> handler)
{
return NSNotificationCenter.DefaultCenter.AddObserver (SizeLimitExceededNotification, notification => handler (null, new Foundation.NSNotificationEventArgs (notification)), objectToObserve);
}

}
// TODO: add binding code here
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Runtime.Versioning;
using Foundation;
using ObjCBindings;

namespace Foundation

public partial class MyNotificationArgs {

}

[BindingType<Class>]
public partial class NSUserDefaults : NSObject {

// default values

[Field<Property> ("NSUserDefaultsSizeLimitExceededNotification", Flags = Property.Notification)]
public static partial NSString SizeLimitExceededNotification { get; }

// special notification center
[Field<Property> ("NSUbiquitousUserDefaultsNoCloudAccountNotification", Flags = Property.Notification, NotificationCenter = "SharedWorkspace.NotificationCenter")]
public static partial NSString NoCloudAccountNotification { get; }

// special args
[Field<Property> ("NSUbiquitousUserDefaultsDidChangeAccountsNotification", Flags = Property.Notification, Type = typeof (MyNotificationArgs))]
public static partial NSString DidChangeAccountsNotification { get; }

// full customization
[Field<Property> ("NSUbiquitousUserDefaultsCompletedInitialSyncNotification", Flags = Property.Notification, Type = typeof (MyNotificationArgs), NotificationCenter = "SharedWorkspace.NotificationCenter")]
public static partial NSString CompletedInitialSyncNotification { get; }
}

8 comments on commit 49763bb

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.