Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object notifications #262

Merged
merged 14 commits into from
Feb 23, 2022
8 changes: 8 additions & 0 deletions example/bin/myapp.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions flutter/realm_flutter/example/lib/main.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions generator/lib/src/realm_model_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ class RealmModelInfo {
'',
]);

yield '@override';
yield 'Stream<RealmObjectChanges<$name>> get changes => RealmObject.getChanges<$name>(this);';
yield '';

yield 'static SchemaObject get schema => _schema ??= _initSchema();';
yield 'static SchemaObject? _schema;';
yield 'static SchemaObject _initSchema() {';
Expand Down
4 changes: 4 additions & 0 deletions generator/test/generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class _Foo {
' @override\n'
' set x(int value) => RealmObject.set(this, \'x\', value);\n'
'\n'
' @override\n'
' Stream<RealmObjectChanges<Foo>> get changes =>\n'
' RealmObject.getChanges<Foo>(this);\n'
'\n'
' static SchemaObject get schema => _schema ??= _initSchema();\n'
' static SchemaObject? _schema;\n'
' static SchemaObject _initSchema() {\n'
Expand Down
20 changes: 20 additions & 0 deletions generator/test/good_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class _Person {
' @override\n'
' set age(int value) => RealmObject.set(this, \'age\', value);\n'
'\n'
' @override\n'
' Stream<RealmObjectChanges<Person>> get changes =>\n'
' RealmObject.getChanges<Person>(this);\n'
'\n'
' static SchemaObject get schema => _schema ??= _initSchema();\n'
' static SchemaObject? _schema;\n'
' static SchemaObject _initSchema() {\n'
Expand Down Expand Up @@ -92,6 +96,10 @@ class _Person {
' @override\n'
' set name(String value) => throw RealmUnsupportedSetError();\n'
'\n'
' @override\n'
' Stream<RealmObjectChanges<Person>> get changes =>\n'
' RealmObject.getChanges<Person>(this);\n'
'\n'
' static SchemaObject get schema => _schema ??= _initSchema();\n'
' static SchemaObject? _schema;\n'
' static SchemaObject _initSchema() {\n'
Expand Down Expand Up @@ -142,6 +150,10 @@ class _Person {
' set children(covariant List<Person> value) =>\n'
' throw RealmUnsupportedSetError();\n'
'\n'
' @override\n'
' Stream<RealmObjectChanges<Person>> get changes =>\n'
' RealmObject.getChanges<Person>(this);\n'
'\n'
' static SchemaObject get schema => _schema ??= _initSchema();\n'
' static SchemaObject? _schema;\n'
' static SchemaObject _initSchema() {\n'
Expand Down Expand Up @@ -191,6 +203,10 @@ class _Person {
' @override\n'
' set spouse(covariant Person? value) => RealmObject.set(this, \'spouse\', value);\n'
'\n'
' @override\n'
' Stream<RealmObjectChanges<Person>> get changes =>\n'
' RealmObject.getChanges<Person>(this);\n'
'\n'
' static SchemaObject get schema => _schema ??= _initSchema();\n'
' static SchemaObject? _schema;\n'
' static SchemaObject _initSchema() {\n'
Expand Down Expand Up @@ -241,6 +257,10 @@ class _Person {
' @override\n'
' set name(String value) => RealmObject.set(this, \'name\', value);\n'
'\n'
' @override\n'
' Stream<RealmObjectChanges<Person>> get changes =>\n'
' RealmObject.getChanges<Person>(this);\n'
'\n'
' static SchemaObject get schema => _schema ??= _initSchema();\n'
' static SchemaObject? _schema;\n'
' static SchemaObject _initSchema() {\n'
Expand Down
6 changes: 5 additions & 1 deletion lib/src/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,11 @@ class ListNotificationsController<T extends Object> extends NotificationsControl
}

@override
void onChanges(RealmCollectionChangesHandle changesHandle) {
void onChanges(Handle changesHandle) {
if (changesHandle is! RealmCollectionChangesHandle) {
throw RealmError("Invalid changes handle. RealmCollectionChangesHandle expected");
}

final changes = RealmListChanges._(changesHandle, list);
streamController.add(changes);
}
Expand Down
42 changes: 42 additions & 0 deletions lib/src/native/realm_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7018,6 +7018,45 @@ class RealmLibrary {
realm_dart_on_collection_change_func_t,
ffi.Pointer<realm_scheduler_t>)>();

/// Subscribe for notifications of changes to a realm object.
///
/// @param realm_object The realm object to subscribe to.
/// @param notification_controller A handle to a Dart NotificationController instance that will be passed to the callback.
/// @param on_change The callback to invoke, if the realm list changes.
/// @return A notification token that can be released to unsubscribe.
///
/// This is a dart specific wrapper for realm_object_add_notification_callback.
ffi.Pointer<realm_notification_token_t>
realm_dart_object_add_notification_callback(
ffi.Pointer<realm_object_t> list,
Object notification_controller,
realm_dart_on_object_change_func_t on_change,
ffi.Pointer<realm_scheduler_t> scheduler,
) {
return _realm_dart_object_add_notification_callback(
list,
notification_controller,
on_change,
scheduler,
);
}

late final _realm_dart_object_add_notification_callbackPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<realm_notification_token_t> Function(
ffi.Pointer<realm_object_t>,
ffi.Handle,
realm_dart_on_object_change_func_t,
ffi.Pointer<realm_scheduler_t>)>>(
'realm_dart_object_add_notification_callback');
late final _realm_dart_object_add_notification_callback =
_realm_dart_object_add_notification_callbackPtr.asFunction<
ffi.Pointer<realm_notification_token_t> Function(
ffi.Pointer<realm_object_t>,
Object,
realm_dart_on_object_change_func_t,
ffi.Pointer<realm_scheduler_t>)>();

ffi.Pointer<ffi.Int8> realm_dart_get_files_path() {
return _realm_dart_get_files_path();
}
Expand Down Expand Up @@ -7942,3 +7981,6 @@ typedef realm_dart_on_collection_change_func_t = ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(
ffi.Handle, ffi.Pointer<realm_collection_changes_t>)>>;
typedef realm_dart_on_object_change_func_t = ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(ffi.Handle, ffi.Pointer<realm_object_changes_t>)>>;
52 changes: 49 additions & 3 deletions lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ class _RealmCore {
});
}

//TODO: Use Finalizers, when available, instead of native WeakHandles https://github.com/dart-lang/language/issues/1847
SchemaHandle createSchema(List<SchemaObject> schema) {
return using((Arena arena) {
final classCount = schema.length;
Expand Down Expand Up @@ -548,6 +547,25 @@ class _RealmCore {
}
}

static void object_change_callback(Object object, Pointer<realm_object_changes> data) {
assert(object is NotificationsController, "Notification controller expected");

final controller = object as NotificationsController;

if (data == nullptr) {
//realm_collection_changes data clone is done in native code before this callback is invoked. nullptr data means cloning failed.
controller.onError(RealmError("Invalid notifications data received"));
return;
}

try {
final changesHandle = RealmObjectChangesHandle._(data);
controller.onChanges(changesHandle);
} catch (e) {
controller.onError(RealmError("Error handling collection change notifications. Error: $e"));
}
}

RealmNotificationTokenHandle subscribeResultsNotifications(RealmResultsHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) {
final onChangeCallback = Pointer.fromFunction<Void Function(ffi.Handle, Pointer<realm_collection_changes>)>(collection_change_callback);

Expand All @@ -560,11 +578,35 @@ class _RealmCore {
RealmNotificationTokenHandle subscribeListNotifications(RealmListHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) {
final onChangeCallback = Pointer.fromFunction<Void Function(ffi.Handle, Pointer<realm_collection_changes>)>(collection_change_callback);

final pointer = _realmLib.invokeGetPointer(
() => _realmLib.realm_dart_list_add_notification_callback(handle._pointer, controller, onChangeCallback, schedulerHandle._pointer));
final pointer = _realmLib
.invokeGetPointer(() => _realmLib.realm_dart_list_add_notification_callback(handle._pointer, controller, onChangeCallback, schedulerHandle._pointer));

return RealmNotificationTokenHandle._(pointer);
}

RealmNotificationTokenHandle subscribeObjectNotifications(RealmObjectHandle handle, NotificationsController controller, SchedulerHandle schedulerHandle) {
final onChangeCallback = Pointer.fromFunction<Void Function(ffi.Handle, Pointer<realm_object_changes>)>(object_change_callback);

final pointer = _realmLib
.invokeGetPointer(() => _realmLib.realm_dart_object_add_notification_callback(handle._pointer, controller, onChangeCallback, schedulerHandle._pointer));

return RealmNotificationTokenHandle._(pointer);
}

bool getObjectChangesIsDeleted(RealmObjectChangesHandle handle) {
return _realmLib.realm_object_changes_is_deleted(handle._pointer);
}

List<int> getObjectChangesProperties(RealmObjectChangesHandle handle) {
return using((arena) {
final count = _realmLib.realm_object_changes_get_num_modified_properties(handle._pointer);

final out_modified = arena<realm_property_key_t>(count);
_realmLib.realm_object_changes_get_modified_properties(handle._pointer, out_modified, count);

return out_modified.asTypedList(count).toList();
});
}
}

class LastError {
Expand Down Expand Up @@ -653,6 +695,10 @@ class RealmCollectionChangesHandle extends Handle<realm_collection_changes> {
RealmCollectionChangesHandle._(Pointer<realm_collection_changes> pointer) : super(pointer, 256);
}

class RealmObjectChangesHandle extends Handle<realm_object_changes> {
RealmObjectChangesHandle._(Pointer<realm_object_changes> pointer) : super(pointer, 256);
}

extension _StringEx on String {
Pointer<Int8> toUtf8Ptr(Allocator allocator) {
final units = utf8.encode(this);
Expand Down
18 changes: 15 additions & 3 deletions lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export 'package:realm_common/realm_common.dart'
show Ignored, Indexed, MapTo, PrimaryKey, RealmError, RealmModel, RealmUnsupportedSetError, RealmStateError, RealmCollectionType, RealmPropertyType;
export "configuration.dart" show Configuration, RealmSchema, SchemaObject;
export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges;
export 'realm_object.dart' show RealmException, RealmObject;
export 'realm_object.dart' show RealmException, RealmObject, RealmObjectChanges;
export 'realm_property.dart';
export 'results.dart' show RealmResults, RealmResultsChanges;

Expand Down Expand Up @@ -267,7 +267,7 @@ class Scheduler {
extension RealmInternal on Realm {
RealmHandle get handle => _handle;
Scheduler get scheduler => _scheduler;

RealmObject createObject(Type type, RealmObjectHandle handle) {
RealmMetadata metadata = _getMetadata(type);

Expand All @@ -279,14 +279,26 @@ extension RealmInternal on Realm {
RealmList<T> createList<T extends Object>(RealmListHandle handle) {
return RealmListInternal.create(handle, this);
}

List<String> getPropertyNames(Type type, List<int> propertyKeys) {
RealmMetadata metadata = _getMetadata(type);
final result = <String>[];
for (var key in propertyKeys) {
final name = metadata.getPropertyName(key);
if (name != null) {
result.add(name);
}
}
return result;
}
}

/// @nodoc
abstract class NotificationsController {
RealmNotificationTokenHandle? handle;

RealmNotificationTokenHandle subscribe();
void onChanges(RealmCollectionChangesHandle changesHandle);
void onChanges(Handle changesHandle);
void onError(RealmError error);

void start() {
Expand Down
Loading