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

Results of primitives #893

Merged
merged 8 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"dart.lineLength": 160,
"cSpell.words": [
"apikeys",
"BEGINSWITH",
"bson",
"deallocated",
"deleter",
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Enhancements
* Added `MutableSubscriptionSet.removeByType` for removing subscriptions by their realm object type. (Issue [#317](https://github.com/realm/realm-dart/issues/317))
* Support results of primitives, ie. `RealmResult<int>`. (Issue [#162](https://github.com/realm/realm-dart/issues/162))
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
* Support notifications on all managed realm lists, including list of primitives, ie. `RealmList<int>.changes` is supported. ([#893](https://github.com/realm/realm-dart/pull/893))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Support notifications on all managed realm lists, including list of primitives, ie. `RealmList<int>.changes` is supported. ([#893](https://github.com/realm/realm-dart/pull/893))
* Support notifications on all managed `RealmList`s, including list of primitive types, for example: `RealmList<int>.changes` is supported. ([#893](https://github.com/realm/realm-dart/pull/893))


### Fixed
* Fixed a wrong mapping for `AuthProviderType` returned by `User.provider` for google, facebook and apple credentials.
Expand Down
42 changes: 27 additions & 15 deletions lib/src/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,17 @@ abstract class RealmList<T extends Object?> with RealmEntity implements List<T>,
/// and it's parent object hasn't been deleted.
bool get isValid;

/// Converts this [List] to a [RealmResults].
RealmResults<T> asResults();

factory RealmList._(RealmListHandle handle, Realm realm, RealmObjectMetadata? metadata) => ManagedRealmList._(handle, realm, metadata);
factory RealmList(Iterable<T> items) => UnmanagedRealmList(items);

/// Creates a frozen snapshot of this `RealmList`.
RealmList<T> freeze();

/// Allows listening for changes when the contents of this collection changes.
Stream<RealmListChanges<T>> get changes;
}

class ManagedRealmList<T extends Object?> with RealmEntity, ListMixin<T> implements RealmList<T> {
Expand Down Expand Up @@ -165,6 +171,18 @@ class ManagedRealmList<T extends Object?> with RealmEntity, ListMixin<T> impleme
final frozenRealm = realm.freeze();
return frozenRealm.resolveList(this)!;
}

@override
RealmResults<T> asResults() => RealmResultsInternal.create<T>(realmCore.resultsFromList(this), realm, metadata);

@override
Stream<RealmListChanges<T>> get changes {
if (isFrozen) {
throw RealmStateError('List is frozen and cannot emit changes');
}
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
final controller = ListNotificationsController<T>(asManaged());
return controller.createStream();
}
}

class UnmanagedRealmList<T extends Object?> extends collection.DelegatingList<T> with RealmEntity implements RealmList<T> {
Expand All @@ -181,6 +199,12 @@ class UnmanagedRealmList<T extends Object?> extends collection.DelegatingList<T>

@override
RealmList<T> freeze() => throw RealmStateError("Unmanaged lists can't be frozen");

@override
RealmResults<T> asResults() => throw RealmStateError("Unmanaged lists can't be converted to results");

@override
Stream<RealmListChanges<T>> get changes => throw RealmStateError("Unmanaged lists don't support changes");
}

// The query operations on lists, as well as the ability to subscribe for notifications,
Expand All @@ -194,21 +218,9 @@ extension RealmListOfObject<T extends RealmObjectBase> on RealmList<T> {
/// The Realm Dart and Realm Flutter SDKs supports querying based on a language inspired by [NSPredicate](https://academy.realm.io/posts/nspredicate-cheatsheet/)
/// and [Predicate Programming Guide.](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html#//apple_ref/doc/uid/TP40001789)
RealmResults<T> query(String query, [List<Object> arguments = const []]) {
final managedList = asManaged();
final handle = realmCore.queryList(managedList, query, arguments);
final handle = realmCore.queryList(asManaged(), query, arguments);
return RealmResultsInternal.create<T>(handle, realm, _metadata);
}

/// Allows listening for changes when the contents of this collection changes.
Stream<RealmListChanges<T>> get changes {
if (isFrozen) {
throw RealmStateError('List is frozen and cannot emit changes');
}

final managedList = asManaged();
final controller = ListNotificationsController<T>(managedList);
return controller.createStream();
}
}

/// @nodoc
Expand Down Expand Up @@ -275,15 +287,15 @@ extension RealmListInternal<T extends Object?> on RealmList<T> {
}

/// Describes the changes in a Realm results collection since the last time the notification callback was invoked.
class RealmListChanges<T extends Object> extends RealmCollectionChanges {
class RealmListChanges<T extends Object?> extends RealmCollectionChanges {
/// The collection being monitored for changes.
final RealmList<T> list;

RealmListChanges._(super.handle, this.list);
}

/// @nodoc
class ListNotificationsController<T extends Object> extends NotificationsController {
class ListNotificationsController<T extends Object?> extends NotificationsController {
final ManagedRealmList<T> list;
late final StreamController<RealmListChanges<T>> streamController;

Expand Down
15 changes: 14 additions & 1 deletion lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,20 @@ class _RealmCore {
});
}

RealmObjectHandle getObjectAt(RealmResults results, int index) {
RealmResultsHandle resultsFromList(RealmList list) {
final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_list_to_results(list.handle._pointer));
return RealmResultsHandle._(pointer, list.realm.handle);
}

Object? resultsGetElementAt(RealmResults results, int index) {
return using((Arena arena) {
final realm_value = arena<realm_value_t>();
_realmLib.invokeGetBool(() => _realmLib.realm_results_get(results.handle._pointer, index, realm_value));
return realm_value.toDartValue(results.realm);
});
}

RealmObjectHandle resultsGetObjectAt(RealmResults results, int index) {
Comment on lines +883 to +891
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are those different?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one is returning a RealmObject while the other is returning a realm_value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that. Since realm_value can hold objects, why not use the same method for both?

Copy link
Contributor Author

@nielsenko nielsenko Oct 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still need to convert the handle (toDartValue don't/can't), and the later is less efficient.

final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_get_object(results.handle._pointer, index));
return RealmObjectHandle._(pointer, results.realm.handle);
}
Expand Down
8 changes: 5 additions & 3 deletions lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges;
export 'realm_object.dart'
show RealmEntity, RealmException, UserCallbackException, RealmObject, RealmObjectBase, EmbeddedObject, RealmObjectChanges, DynamicRealmObject;
export 'realm_property.dart';
export 'results.dart' show RealmResults, RealmResultsChanges;
export 'results.dart' show RealmResults, RealmResultsChanges, RealmResultsOfObject;
export 'session.dart' show Session, SessionState, ConnectionState, ProgressDirection, ProgressMode, SyncProgress, ConnectionStateChange;
export 'subscription.dart' show Subscription, SubscriptionSet, SubscriptionSetState, MutableSubscriptionSet;
export 'user.dart' show User, UserState, UserIdentity, ApiKeyClient, ApiKey, FunctionsClient;
export 'session.dart' show Session, SessionState, ConnectionState, ProgressDirection, ProgressMode, SyncProgress, ConnectionStateChange;
export 'migration.dart' show Migration;
export 'package:cancellation_token/cancellation_token.dart' show CancellationToken, CancelledException;

Expand Down Expand Up @@ -628,12 +628,14 @@ extension RealmInternal on Realm {
return createList<T>(handle, list.metadata);
}

RealmResults<T> resolveResults<T extends RealmObjectBase>(RealmResults<T> results) {
RealmResults<T> resolveResults<T extends Object?>(RealmResults<T> results) {
final handle = realmCore.resolveResults(results, this);
return RealmResultsInternal.create<T>(handle, this, results.metadata);
}

static MigrationRealm getMigrationRealm(Realm realm) => MigrationRealm._(realm);

bool get isInMigration => _isInMigration;
}

/// @nodoc
Expand Down
66 changes: 38 additions & 28 deletions lib/src/results.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

import 'dart:async';
import 'dart:collection' as collection;
import 'dart:ffi';
Expand All @@ -29,7 +28,7 @@ import 'realm_object.dart';
/// added to or deleted from the Realm that match the underlying query.
///
/// {@category Realm}
class RealmResults<T extends RealmObjectBase> extends collection.IterableBase<T> with RealmEntity implements Finalizable {
class RealmResults<T extends Object?> extends collection.IterableBase<T> with RealmEntity implements Finalizable {
final RealmObjectMetadata? _metadata;
final RealmResultsHandle _handle;

Expand All @@ -41,17 +40,13 @@ class RealmResults<T extends RealmObjectBase> extends collection.IterableBase<T>

/// Returns the element of type `T` at the specified [index].
T operator [](int index) {
final handle = realmCore.getObjectAt(this, index);
return realm.createObject(T, handle, _metadata!) as T;
}

/// Returns a new [RealmResults] filtered according to the provided query.
///
/// The Realm Dart and Realm Flutter SDKs supports querying based on a language inspired by [NSPredicate](https://academy.realm.io/posts/nspredicate-cheatsheet/)
/// and [Predicate Programming Guide.](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html#//apple_ref/doc/uid/TP40001789)
RealmResults<T> query(String query, [List<Object> args = const []]) {
final handle = realmCore.queryResults(this, query, args);
return RealmResultsInternal.create<T>(handle, realm, _metadata);
if (this is RealmResults<RealmObjectBase>) {
final handle = realmCore.resultsGetObjectAt(this, index);
final accessor = RealmCoreAccessor(metadata, realm.isInMigration);
return RealmObjectInternal.create(T, realm, handle, accessor) as T;
} else {
return realmCore.resultsGetElementAt(this, index) as T;
}
}

/// `true` if the `Results` collection is empty.
Expand All @@ -73,6 +68,16 @@ class RealmResults<T extends RealmObjectBase> extends collection.IterableBase<T>
@override
int get length => realmCore.getResultsCount(this);

/// Creates a frozen snapshot of this query.
RealmResults<T> freeze() {
if (isFrozen) {
return this;
}

final frozenRealm = realm.freeze();
return frozenRealm.resolveResults(this);
}

/// Allows listening for changes when the contents of this collection changes.
Stream<RealmResultsChanges<T>> get changes {
if (isFrozen) {
Expand All @@ -82,15 +87,17 @@ class RealmResults<T extends RealmObjectBase> extends collection.IterableBase<T>
final controller = ResultsNotificationsController<T>(this);
return controller.createStream();
}
}

/// Creates a frozen snapshot of this query.
RealmResults<T> freeze() {
if (isFrozen) {
return this;
}

final frozenRealm = realm.freeze();
return frozenRealm.resolveResults(this);
// The query operations on results only work for results of objects (core restriction),
// so we add it as an extension methods to allow the compiler to prevent misuse.
extension RealmResultsOfObject<T extends RealmObjectBase> on RealmResults<T> {
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
/// Returns a new [RealmResults] filtered according to the provided query.
///
/// The Realm Dart and Realm Flutter SDKs supports querying based on a language inspired by [NSPredicate](https://www.mongodb.com/docs/realm/realm-query-language/)
RealmResults<T> query(String query, [List<Object> args = const []]) {
final handle = realmCore.queryResults(this, query, args);
return RealmResultsInternal.create<T>(handle, realm, _metadata);
}
}

Expand All @@ -110,23 +117,26 @@ extension RealmResultsInternal on RealmResults {
return _handle;
}

RealmObjectMetadata? get metadata => _metadata;
RealmObjectMetadata get metadata => _metadata!;

static RealmResults<T> create<T extends RealmObjectBase>(RealmResultsHandle handle, Realm realm, RealmObjectMetadata? metadata) {
return RealmResults<T>._(handle, realm, metadata);
}
static RealmResults<T> create<T extends Object?>(
RealmResultsHandle handle,
Realm realm,
RealmObjectMetadata? metadata,
) =>
RealmResults<T>._(handle, realm, metadata);
}

/// Describes the changes in a Realm results collection since the last time the notification callback was invoked.
class RealmResultsChanges<T extends RealmObjectBase> extends RealmCollectionChanges {
class RealmResultsChanges<T extends Object?> extends RealmCollectionChanges {
/// The results collection being monitored for changes.
final RealmResults<T> results;

RealmResultsChanges._(super.handle, this.results);
}

/// @nodoc
class ResultsNotificationsController<T extends RealmObjectBase> extends NotificationsController {
class ResultsNotificationsController<T extends Object?> extends NotificationsController {
final RealmResults<T> results;
late final StreamController<RealmResultsChanges<T>> streamController;

Expand Down Expand Up @@ -158,7 +168,7 @@ class ResultsNotificationsController<T extends RealmObjectBase> extends Notifica
}
}

class _RealmResultsIterator<T extends RealmObjectBase> implements Iterator<T> {
class _RealmResultsIterator<T extends Object?> implements Iterator<T> {
final RealmResults<T> _results;
int _index;
T? _current;
Expand Down
Loading