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

Merged
merged 12 commits into from
Jun 6, 2024
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
34 changes: 33 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
x.y.z Release notes (yyyy-MM-dd)
=============================================================
### Enhancements
* None.
* Added support for storing nested collections (List and Map not ManagedSet) in a `AnyRealmValue`.
```swift
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(obj)
}
```
* Added new operators to Swift's Query API for supporting querying nested collections.
```swift
realm.objects(MixedObject.self).where { $0.anyValue[0][0][1] == .double(76.54) }
```

The `.all` operator allows looking up in all keys or indexes, which is the same that using a wildcard as a subscript `["*"]`.
```swift
realm.objects(MixedObject.self).where { $0.anyValue["key"].all == .bool(false) }
```

### Fixed
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-swift/issues/????), since v?.?.?)
Expand Down Expand Up @@ -117,6 +145,10 @@ store. Xcode 15.1 is now the minimum supported version.
progress estimate, which is derived by the server based on historical data and other heuristics.
([#8476](https://github.com/realm/realm-swift/issues/8476))

### Deprecations

* `rlm_valueType` is deprecated in favour of `rlm_anyValueType` which now includes collections (List and Dictionary).

<!-- ### Breaking Changes - ONLY INCLUDE FOR NEW MAJOR version -->

### Compatibility
Expand Down
4 changes: 4 additions & 0 deletions Realm.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@
AC3B33AE29DC6CEE0042F3A0 /* RLMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3B33AB29DC6CEE0042F3A0 /* RLMLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC3B33AF29DC6CEE0042F3A0 /* RLMLogger.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3B33AC29DC6CEE0042F3A0 /* RLMLogger.mm */; };
AC3B33B029DC6CEE0042F3A0 /* RLMLogger_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3B33AD29DC6CEE0042F3A0 /* RLMLogger_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
AC5300722BD03D4A00BF5950 /* MixedCollectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC5300712BD03D4900BF5950 /* MixedCollectionTest.swift */; };
AC7825B92ACD90BE007ABA4B /* Geospatial.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC7825B82ACD90BE007ABA4B /* Geospatial.swift */; };
AC7825BD2ACD90DA007ABA4B /* RLMGeospatial.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC7825BA2ACD90DA007ABA4B /* RLMGeospatial.mm */; };
AC7825BF2ACD90DA007ABA4B /* RLMGeospatial.h in Headers */ = {isa = PBXBuildFile; fileRef = AC7825BC2ACD90DA007ABA4B /* RLMGeospatial.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -900,6 +901,7 @@
AC3B33AB29DC6CEE0042F3A0 /* RLMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMLogger.h; sourceTree = "<group>"; };
AC3B33AC29DC6CEE0042F3A0 /* RLMLogger.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RLMLogger.mm; sourceTree = "<group>"; };
AC3B33AD29DC6CEE0042F3A0 /* RLMLogger_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMLogger_Private.h; sourceTree = "<group>"; };
AC5300712BD03D4900BF5950 /* MixedCollectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixedCollectionTest.swift; sourceTree = "<group>"; };
AC7825B82ACD90BE007ABA4B /* Geospatial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Geospatial.swift; sourceTree = "<group>"; };
AC7825BA2ACD90DA007ABA4B /* RLMGeospatial.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RLMGeospatial.mm; sourceTree = "<group>"; };
AC7825BB2ACD90DA007ABA4B /* RLMGeospatial_Private.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RLMGeospatial_Private.hpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1414,6 +1416,7 @@
5D6610001BE98D880021E04F /* ListTests.swift */,
3F1D8D75265B075000593ABA /* MapTests.swift */,
5D6610011BE98D880021E04F /* MigrationTests.swift */,
AC5300712BD03D4900BF5950 /* MixedCollectionTest.swift */,
3F4E0FF82654765C008B8C0B /* ModernKVOTests.swift */,
3F4E100E2655CA33008B8C0B /* ModernObjectAccessorTests.swift */,
3FA5E94C266064C4008F1345 /* ModernObjectCreationTests.swift */,
Expand Down Expand Up @@ -2612,6 +2615,7 @@
5D6610161BE98D880021E04F /* ListTests.swift in Sources */,
3F1D8D77265B075100593ABA /* MapTests.swift in Sources */,
3F8824FD1E5E335000586B35 /* MigrationTests.swift in Sources */,
AC5300722BD03D4A00BF5950 /* MixedCollectionTest.swift in Sources */,
3F4E0FF92654765C008B8C0B /* ModernKVOTests.swift in Sources */,
3F4E10102655CA33008B8C0B /* ModernObjectAccessorTests.swift in Sources */,
3FA5E94D266064C4008F1345 /* ModernObjectCreationTests.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Realm/RLMAccessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class RLMAccessorContext : public RLMStatelessAccessorContext {
RLMAccessorContext(RLMObjectBase *parentObject, const realm::Property *property = nullptr);
RLMAccessorContext(RLMObjectBase *parentObject, realm::ColKey);
RLMAccessorContext(RLMClassInfo& info);
RLMAccessorContext(RLMClassInfo& parentInfo, RLMClassInfo& info, RLMProperty *property);

// The property currently being accessed; needed for KVO things for boxing
// List and Results
Expand Down
68 changes: 43 additions & 25 deletions Realm/RLMAccessor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
#import "RLMSwiftProperty.h"
#import "RLMUUID_Private.hpp"
#import "RLMUtil.hpp"
#import "RLMValue.h"

#import <realm/object-store/results.hpp>
#import <realm/object-store/object.hpp>
#import <realm/object-store/property.hpp>
#import <realm/object-store/results.hpp>

#import <objc/runtime.h>
#import <objc/message.h>
Expand Down Expand Up @@ -127,13 +129,8 @@ void setValueOrNull(__unsafe_unretained RLMObjectBase *const obj, ColKey col,

RLMTranslateError([&] {
if (value) {
if constexpr (std::is_same_v<T, realm::Mixed>) {
obj->_row.set(col, RLMObjcToMixed(value, obj->_realm, realm::CreatePolicy::SetLink));
}
else {
RLMStatelessAccessorContext ctx;
obj->_row.set(col, ctx.unbox<T>(value));
}
RLMStatelessAccessorContext ctx;
obj->_row.set(col, ctx.unbox<T>(value));
}
else {
obj->_row.set_null(col);
Expand Down Expand Up @@ -272,9 +269,10 @@ void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
setValueOrNull<realm::UUID>(obj, key, value);
}

void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key,
__unsafe_unretained id<RLMValue> const value) {
setValueOrNull<realm::Mixed>(obj, key, value);
void setValue(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const property, __unsafe_unretained id<RLMValue> const value) {
realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
RLMAccessorContext ctx(obj);
o.set_property_value(ctx, getProperty(obj, property), value ?: NSNull.null);
}

RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj,
Expand Down Expand Up @@ -404,6 +402,13 @@ void kvoSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index,
setValue(obj, key, static_cast<StorageType>(value));
}

template<>
void kvoSetValue<id<RLMValue>>(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index, id<RLMValue> value) {
RLMVerifyInWriteTransaction(obj);
auto& prop = getProperty(obj, index);
setValue(obj, obj->_info->propertyForTableColumn(prop.column_key), static_cast<id<RLMValue>>(value));
}

template<typename ArgType, typename StorageType=ArgType>
id makeSetter(__unsafe_unretained RLMProperty *const prop) {
if (prop.isPrimary) {
Expand Down Expand Up @@ -772,13 +777,13 @@ id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj,
}

RLMArray *RLMGetSwiftPropertyArray(__unsafe_unretained RLMObjectBase *const obj, uint16_t key) {
return getCollection(obj, key);
return (RLMArray *)getCollection(obj, key);
}
RLMSet *RLMGetSwiftPropertySet(__unsafe_unretained RLMObjectBase *const obj, uint16_t key) {
return getCollection(obj, key);
}
RLMDictionary *RLMGetSwiftPropertyMap(__unsafe_unretained RLMObjectBase *const obj, uint16_t key) {
return getCollection(obj, key);
return (RLMDictionary *)getCollection(obj, key);
}

void RLMSetSwiftPropertyNil(__unsafe_unretained RLMObjectBase *const obj, uint16_t key) {
Expand Down Expand Up @@ -821,13 +826,8 @@ void RLMSetSwiftPropertyAny(__unsafe_unretained RLMObjectBase *const obj, uint16
, _parentObject(obj)
, _parentObjectInfo(&parent._info)
, _colKey(property.column_key)
{
}

RLMAccessorContext::RLMAccessorContext(RLMClassInfo& info)
: _realm(info.realm), _info(info)
{
}
{
}

RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent,
const realm::Property *prop)
Expand All @@ -850,6 +850,20 @@ void RLMSetSwiftPropertyAny(__unsafe_unretained RLMObjectBase *const obj, uint16
{
}

RLMAccessorContext::RLMAccessorContext(RLMClassInfo& info)
: _realm(info.realm), _info(info)
{
}

RLMAccessorContext::RLMAccessorContext(RLMClassInfo& parentInfo, RLMClassInfo& info,
__unsafe_unretained RLMProperty *const property)
: _realm(info.realm)
, _info(info)
, _parentObjectInfo(&parentInfo)
dianaafanador3 marked this conversation as resolved.
Show resolved Hide resolved
, currentProperty(property)
{
}

id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) {
if (!_defaultValues) {
_defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema);
Expand Down Expand Up @@ -890,15 +904,18 @@ void RLMSetSwiftPropertyAny(__unsafe_unretained RLMObjectBase *const obj, uint16
}

id RLMAccessorContext::box(realm::Mixed v) {
return RLMMixedToObjc(v, _realm, &_info);
auto property = currentProperty ?: _info.propertyForTableColumn(_colKey);
// Property and ParentObject are only passed for List and Dictionary boxing
return RLMMixedToObjc(v, _realm, &_info, property, _parentObject);
}

id RLMAccessorContext::box(realm::List&& l) {
REALM_ASSERT(_parentObjectInfo);
REALM_ASSERT(currentProperty);
auto property = currentProperty ?: _info.propertyForTableColumn(_colKey);
REALM_ASSERT(property);
return [[RLMManagedArray alloc] initWithBackingCollection:std::move(l)
parentInfo:_parentObjectInfo
property:currentProperty];
property:property];
}

id RLMAccessorContext::box(realm::object_store::Set&& s) {
Expand All @@ -911,10 +928,11 @@ void RLMSetSwiftPropertyAny(__unsafe_unretained RLMObjectBase *const obj, uint16

id RLMAccessorContext::box(realm::object_store::Dictionary&& d) {
REALM_ASSERT(_parentObjectInfo);
REALM_ASSERT(currentProperty);
auto property = currentProperty ? currentProperty : _info.propertyForTableColumn(_colKey);
REALM_ASSERT(property);
return [[RLMManagedDictionary alloc] initWithBackingCollection:std::move(d)
parentInfo:_parentObjectInfo
property:currentProperty];
property:property];
}

id RLMAccessorContext::box(realm::Object&& o) {
Expand Down
8 changes: 4 additions & 4 deletions Realm/RLMArray.mm
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ - (instancetype)initWithObjectType:(RLMPropertyType)type optional:(BOOL)optional

- (void)setParent:(RLMObjectBase *)parentObject property:(RLMProperty *)property {
_parentObject = parentObject;
_key = property.name;
_property = property;
_isLegacyProperty = property.isLegacy;
}

Expand Down Expand Up @@ -192,9 +192,9 @@ static void changeArray(__unsafe_unretained RLMArray *const ar,

if (RLMObjectBase *parent = ar->_parentObject) {
NSIndexSet *indexes = is();
[parent willChange:kind valuesAtIndexes:indexes forKey:ar->_key];
[parent willChange:kind valuesAtIndexes:indexes forKey:ar->_property.name];
f();
[parent didChange:kind valuesAtIndexes:indexes forKey:ar->_key];
[parent didChange:kind valuesAtIndexes:indexes forKey:ar->_property.name];
}
else {
f();
Expand Down Expand Up @@ -608,7 +608,7 @@ - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
#pragma mark - Key Path Strings

- (NSString *)propertyKey {
return _key;
return _property.name;
}

@end
Expand Down
5 changes: 2 additions & 3 deletions Realm/RLMArray_Private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ class RLMObservationInfo;
@interface RLMArray () {
@protected
NSString *_objectClassName;
RLMPropertyType _type;
BOOL _optional;
@public
// The name of the property which this RLMArray represents
NSString *_key;
// The property which this RLMArray represents
RLMProperty *_property;
dianaafanador3 marked this conversation as resolved.
Show resolved Hide resolved
__weak RLMObjectBase *_parentObject;
}
@end
Expand Down
21 changes: 16 additions & 5 deletions Realm/RLMCollection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ @implementation RLMFastEnumerator {

RLMRealm *_realm;
RLMClassInfo *_info;
RLMClassInfo *_parentInfo;
RLMProperty *_property;

// A pointer to either _snapshot or a Results from the source collection,
// to avoid having to copy the Results when not in a write transaction
Expand All @@ -58,11 +60,15 @@ @implementation RLMFastEnumerator {

- (instancetype)initWithBackingCollection:(realm::object_store::Collection const&)backingCollection
collection:(id)collection
classInfo:(RLMClassInfo&)info {
classInfo:(RLMClassInfo *)info
parentInfo:(RLMClassInfo *)parentInfo
property:(RLMProperty *)property {
self = [super init];
if (self) {
_info = &info;
_info = info;
_realm = _info->realm;
_parentInfo = parentInfo;
_property = property;

if (_realm.inWriteTransaction) {
_snapshot = backingCollection.as_results().snapshot();
Expand All @@ -79,11 +85,15 @@ - (instancetype)initWithBackingCollection:(realm::object_store::Collection const

- (instancetype)initWithBackingDictionary:(realm::object_store::Dictionary const&)backingDictionary
dictionary:(RLMManagedDictionary *)dictionary
classInfo:(RLMClassInfo&)info {
classInfo:(RLMClassInfo *)info
parentInfo:(RLMClassInfo *)parentInfo
property:(RLMProperty *)property {
self = [super init];
if (self) {
_info = &info;
_info = info;
_realm = _info->realm;
_parentInfo = parentInfo;
_property = property;

if (_realm.inWriteTransaction) {
_snapshot = backingDictionary.get_keys().snapshot();
Expand Down Expand Up @@ -146,7 +156,8 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
NSUInteger batchCount = 0, count = state->extra[1];

@autoreleasepool {
RLMAccessorContext ctx(*_info);
auto ctx = _parentInfo ? RLMAccessorContext(*_parentInfo, *_info, _property) :
RLMAccessorContext(*_info);
for (NSUInteger index = state->state; index < count && batchCount < len; ++index) {
_strongBuffer[batchCount] = _results->get(ctx, index);
batchCount++;
Expand Down
8 changes: 6 additions & 2 deletions Realm/RLMCollection_Private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,15 @@ RLM_DIRECT_MEMBERS
@interface RLMFastEnumerator : NSObject
- (instancetype)initWithBackingCollection:(realm::object_store::Collection const&)backingCollection
collection:(id)collection
classInfo:(RLMClassInfo&)info;
classInfo:(RLMClassInfo *)info
parentInfo:(RLMClassInfo *)parentInfo
property:(RLMProperty *)property;

- (instancetype)initWithBackingDictionary:(realm::object_store::Dictionary const&)backingDictionary
dictionary:(RLMManagedDictionary *)dictionary
classInfo:(RLMClassInfo&)info;
classInfo:(RLMClassInfo *)info
parentInfo:(RLMClassInfo *)parentInfo
property:(RLMProperty *)property;

- (instancetype)initWithResults:(realm::Results&)results
collection:(id)collection
Expand Down
Loading
Loading