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

RCOCOA-2271: Collections in Mixed #8546

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

dianaafanador3
Copy link
Collaborator

@dianaafanador3 dianaafanador3 commented Apr 12, 2024

  • Added support for storing nested collections (List and Map not ManagedSet) in a AnyRealmValue.

    class MixedObject: Object {
      @Persisted var anyValue: AnyRealmValue
    }
    
    // You can build a AnyRealmValue from a Swift's Dictionary.
    let dictionary: Dictionary<String, AnyRealmValue> = ["key1": .string("hello"), "key2": .bool(false)]
    
    // You can build a AnyRealmValue from a Swift's Map
    // and nest a collection within another collection.
    let list: Array<AnyRealmValue> = [.int(12), .double(14.17), AnyRealmValue.fromDictionary(dictionary)]
    
    let realm = realmWithTestPath()
    try realm.write {
      let obj = MixedObject()
      obj.anyValue = AnyRealmValue.fromArray(list)
      realm.add(o)
    }
  • Added new operators to Swift's Query API for supporting querying nested collections.

    realm.objects(MixedObject.self).where { $0.anyValue[0][0][1] == .double(76.54) }

    The .all operator allows looking up in all keys or indexes.

    realm.objects(MixedObject.self).where { $0.anyValue["key"].all == .bool(false) }

@cla-bot cla-bot bot added the cla: yes label Apr 12, 2024
@dianaafanador3 dianaafanador3 marked this pull request as draft April 12, 2024 16:16
@dianaafanador3 dianaafanador3 force-pushed the dp/collections_in_mixed branch 8 times, most recently from b60f46f to 3386d7a Compare April 22, 2024 07:36
@dianaafanador3 dianaafanador3 marked this pull request as ready for review April 22, 2024 11:15
@dianaafanador3
Copy link
Collaborator Author

This is waiting for this PR to be merged to core. Also, there is one failing test which is working locally, taking a look at it, but putting this on review on the meantime.

@dianaafanador3 dianaafanador3 changed the title Collections in Mixed RCOCOA-2271: Collections in Mixed Apr 22, 2024
@dianaafanador3 dianaafanador3 force-pushed the dp/collections_in_mixed branch 9 times, most recently from ed91d6b to 45a17bd Compare April 23, 2024 14:44
Copy link
Member

@tgoyne tgoyne left a comment

Choose a reason for hiding this comment

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

A first pass over things.

Realm/RLMDictionary.h Outdated Show resolved Hide resolved
Realm/RLMAccessor.mm Outdated Show resolved Hide resolved
Realm/RLMAccessor.mm Outdated Show resolved Hide resolved
Realm/RLMArray_Private.hpp Show resolved Hide resolved
Realm/RLMConstants.h Outdated Show resolved Hide resolved
Realm/RLMAccessor.mm Outdated Show resolved Hide resolved
RealmSwift/AnyRealmValue.swift Outdated Show resolved Hide resolved
RealmSwift/List.swift Outdated Show resolved Hide resolved
@@ -779,6 +779,14 @@ public final class Map<Key: _MapKey, Value: RealmCollectionValue>: RLMSwiftColle
}
}

// MARK: - Hashable

extension Map: Hashable {
Copy link
Member

Choose a reason for hiding this comment

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

Similarly, this is not at all a valid Hashable implementation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Using ObjectIdentifier given that we already conform to Equatable

Copy link
Member

Choose a reason for hiding this comment

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

ObjectIdentifier is not a valid definition either. Two managed dictionary objects which correspond to the same field are equal to each other but have different ObjectIdentifiers. All of our accessor types are fundamentally incompatible with Hashable due to that their identity changes when you promote an unmanaged object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tgoyne we do need to conform to Hashable to be able to include Map or List as a an associated value in AnyRealmValue if we want to go that approach.
I can think of a few approaches for this:

  • Use ObjectIdentifer and sacrifice equality for managed and unmanaged object
  • Have associated values as Swift's native types Array/Dictionary, this will clearly will go bad with nested collections which will need to be instantiated completely to convert it (Discarded)
  • Store some reference to the collection as the associated value and then and have some static function that returns the collection (Not need to conform to hashable, but worse developer experience).

I'm open to discuss different solutions

Copy link
Member

Choose a reason for hiding this comment

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

We can override the hash function on AnyRealmValue and return a constant value for the Map and List cases.

RealmSwift/Tests/ObjectCreationTests.swift Outdated Show resolved Hide resolved
Realm/RLMAccessor.mm Outdated Show resolved Hide resolved
Realm/RLMAccessor.mm Show resolved Hide resolved
@@ -890,15 +914,17 @@ void RLMSetSwiftPropertyAny(__unsafe_unretained RLMObjectBase *const obj, uint16
}

id RLMAccessorContext::box(realm::Mixed v) {
return RLMMixedToObjc(v, _realm, &_info);
auto property = (currentProperty) ? currentProperty : _info.propertyForTableColumn(_colKey);
Copy link
Contributor

Choose a reason for hiding this comment

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

why would currentProperty be nil here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For any other mixed wrapped types besides list and dictionary the accessor is constructed without the current property because it is not needed to box the native value

Realm/RLMCollection.mm Outdated Show resolved Hide resolved
Realm/RLMCollection.mm Outdated Show resolved Hide resolved
Realm/RLMValue.h Outdated Show resolved Hide resolved
Realm/Tests/KVOTests.mm Outdated Show resolved Hide resolved
Realm/Tests/NotificationTests.m Show resolved Hide resolved
Realm/Tests/ObjectTests.m Outdated Show resolved Hide resolved
RealmSwift/AnyRealmValue.swift Outdated Show resolved Hide resolved
Copy link
Member

@elle-j elle-j left a comment

Choose a reason for hiding this comment

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

Nice to see the Swift implementation of this as well! 🙂

CHANGELOG.md Outdated Show resolved Hide resolved
Package.swift Outdated Show resolved Hide resolved
Realm/RLMUtil.mm Show resolved Hide resolved
Realm/RLMValue.h Outdated Show resolved Hide resolved
@tgoyne
Copy link
Member

tgoyne commented Apr 29, 2024

There are a bunch of new warnings here that need to be fixed.

Realm/ObjectServerTests/RealmServer.swift Outdated Show resolved Hide resolved
Realm/RLMAccessor.mm Outdated Show resolved Hide resolved
Realm/RLMAccessor.mm Outdated Show resolved Hide resolved
Realm/RLMManagedArray.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
RealmSwift/Object.swift Outdated Show resolved Hide resolved
@dianaafanador3 dianaafanador3 force-pushed the dp/collections_in_mixed branch 4 times, most recently from cede8e2 to 26d3366 Compare May 7, 2024 15:05
RealmSwift/AnyRealmValue.swift Show resolved Hide resolved
Realm/RLMManagedArray.mm Outdated Show resolved Hide resolved
Realm/RLMCollection.mm Outdated Show resolved Hide resolved
Realm/RLMManagedDictionary.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
RealmSwift/Object.swift Outdated Show resolved Hide resolved
RealmSwift/ObjectiveCSupport+AnyRealmValue.swift Outdated Show resolved Hide resolved
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (RLMPropertyType)rlm_valueType {
REALM_UNREACHABLE();
Copy link
Member

Choose a reason for hiding this comment

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

This is not unreachable. rlm_valueType is part of the public API.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tgoyne This is a very particular case because we are deprecating the declaration but also changing the type to be used. You are right because this is part of the public, the user can ask for rlm_valueType , but because we don't want to add any new types to property type we are in some sort of limbo here.
Solutions
We can return RLMPropertyTypeAny like we do for Null, and this is actually true because this indeed a colection of mixed?

Copy link
Member

Choose a reason for hiding this comment

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

Returning RLMPropertyTypeAny seems like the best option here.

REALM_UNREACHABLE() marks a place that should be logically impossible to hit and if we get there it always indicates a bug in our code.

RealmSwift/Impl/ObjcBridgeable.swift Outdated Show resolved Hide resolved
}
/// :nodoc:
public subscript(key: String) -> Query<AnyRealmValue> {
.init(appendKeyPath("['\(key)']", options: [.isPath]))
Copy link
Member

Choose a reason for hiding this comment

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

This needs to handle key containing quotes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tgoyne
Do you mean because of this ?

Note
The expressions you write inside parentheses within an interpolated string can’t contain an unescaped backslash (\), a carriage return, or a line feed. However, they can contain other string literals.

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/

Copy link
Member

Choose a reason for hiding this comment

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

If you do $0.anyCol["'"] == 0 to query on the dictionary key "'" this will produce invalid syntax.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

$0.anyCol["'"] is not allowed for AnyRealmValue subscripts or I'm missing something

Copy link
Member

Choose a reason for hiding this comment

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

I don't know what you mean. any["'"] == %@ (or any['\''] == %@) is a perfectly valid query, which this currently fails to generate a valid string for.

Copy link
Member

@tgoyne tgoyne left a comment

Choose a reason for hiding this comment

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

There are still several new warnings. I'm not sure why the implicit casts from id to RLMArray are producing warnings here but not on master, but the other warnings seem straightforward.

CHANGELOG.md Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
Realm/RLMQueryUtil.mm Outdated Show resolved Hide resolved
}
/// :nodoc:
public subscript(key: String) -> Query<AnyRealmValue> {
.init(appendKeyPath("['\(key)']", options: [.isPath]))
Copy link
Member

Choose a reason for hiding this comment

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

If you do $0.anyCol["'"] == 0 to query on the dictionary key "'" this will produce invalid syntax.

RealmSwift/Query.swift Outdated Show resolved Hide resolved
RealmSwift/RealmCollection.swift Outdated Show resolved Hide resolved
RealmSwift/ObjectiveCSupport+AnyRealmValue.swift Outdated Show resolved Hide resolved
@dianaafanador3 dianaafanador3 force-pushed the dp/collections_in_mixed branch 3 times, most recently from 3683365 to 98a39c3 Compare May 22, 2024 23:36
@dianaafanador3
Copy link
Collaborator Author

@tgoyne solved PR comments and removed the warnings

Realm/RLMSwiftValueStorage.mm Show resolved Hide resolved
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (RLMPropertyType)rlm_valueType {
REALM_UNREACHABLE();
Copy link
Member

Choose a reason for hiding this comment

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

Returning RLMPropertyTypeAny seems like the best option here.

REALM_UNREACHABLE() marks a place that should be logically impossible to hit and if we get there it always indicates a bug in our code.

Realm/Tests/NotificationTests.m Show resolved Hide resolved
Realm/Tests/QueryTests.m Show resolved Hide resolved
RealmSwift/AnyRealmValue.swift Show resolved Hide resolved
RealmSwift/Object.swift Show resolved Hide resolved
}
/// :nodoc:
public subscript(key: String) -> Query<AnyRealmValue> {
.init(appendKeyPath("['\(key)']", options: [.isPath]))
Copy link
Member

Choose a reason for hiding this comment

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

I don't know what you mean. any["'"] == %@ (or any['\''] == %@) is a perfectly valid query, which this currently fails to generate a valid string for.

RealmSwift/RealmCollection.swift Show resolved Hide resolved
RealmSwift/Tests/MixedCollectionTest.swift Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants