Skip to content

Commit

Permalink
RCOCOA-2271: Collections in Mixed (#8546)
Browse files Browse the repository at this point in the history
  • Loading branch information
dianaafanador3 authored Jun 6, 2024
1 parent 21ac01d commit 023f713
Show file tree
Hide file tree
Showing 42 changed files with 3,459 additions and 503 deletions.
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)
, 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;
__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

0 comments on commit 023f713

Please sign in to comment.