From 023f713095c1d17e2efe14d8d1b69630ece61f99 Mon Sep 17 00:00:00 2001 From: Diana Perez Afanador Date: Thu, 6 Jun 2024 11:58:43 +0200 Subject: [PATCH] RCOCOA-2271: Collections in Mixed (#8546) --- CHANGELOG.md | 34 +- Realm.xcodeproj/project.pbxproj | 4 + Realm/RLMAccessor.hpp | 1 + Realm/RLMAccessor.mm | 68 +- Realm/RLMArray.mm | 8 +- Realm/RLMArray_Private.hpp | 5 +- Realm/RLMCollection.mm | 21 +- Realm/RLMCollection_Private.hpp | 8 +- Realm/RLMConstants.h | 53 +- Realm/RLMDictionary.mm | 8 +- Realm/RLMDictionary_Private.hpp | 5 +- Realm/RLMManagedArray.mm | 68 +- Realm/RLMManagedDictionary.mm | 80 +- Realm/RLMManagedSet.mm | 12 +- Realm/RLMQueryUtil.mm | 77 +- Realm/RLMSet.h | 2 +- Realm/RLMSet.mm | 8 +- Realm/RLMSet_Private.hpp | 2 +- Realm/RLMSwiftValueStorage.mm | 2 +- Realm/RLMUtil.hpp | 12 +- Realm/RLMUtil.mm | 46 +- Realm/RLMValue.h | 26 +- Realm/RLMValue.mm | 141 +++ Realm/Tests/KVOTests.mm | 58 + Realm/Tests/NotificationTests.m | 394 ++++++- Realm/Tests/PrimitiveRLMValuePropertyTests.m | 428 ++++---- .../PrimitiveRLMValuePropertyTests.tpl.m | 48 +- Realm/Tests/QueryTests.m | 16 +- Realm/Tests/RLMValueTests.m | 533 +++++++++- Realm/Tests/SectionedResultsTests.m | 20 +- Realm/Tests/Swift/SwiftDynamicTests.swift | 24 + RealmSwift/AnyRealmValue.swift | 77 ++ .../ObjectiveCSupport+AnyRealmValue.swift | 43 +- RealmSwift/Query.swift | 54 + RealmSwift/RealmCollection.swift | 7 +- RealmSwift/Tests/AnyRealmValueTests.swift | 8 + RealmSwift/Tests/KVOTests.swift | 19 + RealmSwift/Tests/MixedCollectionTest.swift | 989 ++++++++++++++++++ RealmSwift/Tests/ModernKVOTests.swift | 15 + RealmSwift/Tests/ObjectCreationTests.swift | 10 +- RealmSwift/Tests/QueryTests.swift | 374 ++++++- RealmSwift/Tests/QueryTests.swift.gyb | 154 +++ 42 files changed, 3459 insertions(+), 503 deletions(-) create mode 100644 RealmSwift/Tests/MixedCollectionTest.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da64d06d2..9b5c48fc9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 = ["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 = [.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 * ([#????](https://github.com/realm/realm-swift/issues/????), since v?.?.?) @@ -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). + ### Compatibility diff --git a/Realm.xcodeproj/project.pbxproj b/Realm.xcodeproj/project.pbxproj index 5d9f090e83..82b6715fd5 100644 --- a/Realm.xcodeproj/project.pbxproj +++ b/Realm.xcodeproj/project.pbxproj @@ -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, ); }; }; @@ -900,6 +901,7 @@ AC3B33AB29DC6CEE0042F3A0 /* RLMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMLogger.h; sourceTree = ""; }; AC3B33AC29DC6CEE0042F3A0 /* RLMLogger.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RLMLogger.mm; sourceTree = ""; }; AC3B33AD29DC6CEE0042F3A0 /* RLMLogger_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMLogger_Private.h; sourceTree = ""; }; + AC5300712BD03D4900BF5950 /* MixedCollectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixedCollectionTest.swift; sourceTree = ""; }; AC7825B82ACD90BE007ABA4B /* Geospatial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Geospatial.swift; sourceTree = ""; }; AC7825BA2ACD90DA007ABA4B /* RLMGeospatial.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RLMGeospatial.mm; sourceTree = ""; }; AC7825BB2ACD90DA007ABA4B /* RLMGeospatial_Private.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RLMGeospatial_Private.hpp; sourceTree = ""; }; @@ -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 */, @@ -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 */, diff --git a/Realm/RLMAccessor.hpp b/Realm/RLMAccessor.hpp index 30d42492eb..939ef15eed 100644 --- a/Realm/RLMAccessor.hpp +++ b/Realm/RLMAccessor.hpp @@ -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 diff --git a/Realm/RLMAccessor.mm b/Realm/RLMAccessor.mm index db7598738e..b52e7babd4 100644 --- a/Realm/RLMAccessor.mm +++ b/Realm/RLMAccessor.mm @@ -33,9 +33,11 @@ #import "RLMSwiftProperty.h" #import "RLMUUID_Private.hpp" #import "RLMUtil.hpp" +#import "RLMValue.h" -#import +#import #import +#import #import #import @@ -127,13 +129,8 @@ void setValueOrNull(__unsafe_unretained RLMObjectBase *const obj, ColKey col, RLMTranslateError([&] { if (value) { - if constexpr (std::is_same_v) { - obj->_row.set(col, RLMObjcToMixed(value, obj->_realm, realm::CreatePolicy::SetLink)); - } - else { - RLMStatelessAccessorContext ctx; - obj->_row.set(col, ctx.unbox(value)); - } + RLMStatelessAccessorContext ctx; + obj->_row.set(col, ctx.unbox(value)); } else { obj->_row.set_null(col); @@ -272,9 +269,10 @@ void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key, setValueOrNull(obj, key, value); } -void setValue(__unsafe_unretained RLMObjectBase *const obj, ColKey key, - __unsafe_unretained id const value) { - setValueOrNull(obj, key, value); +void setValue(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const property, __unsafe_unretained id 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, @@ -404,6 +402,13 @@ void kvoSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index, setValue(obj, key, static_cast(value)); } +template<> +void kvoSetValue>(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index, id value) { + RLMVerifyInWriteTransaction(obj); + auto& prop = getProperty(obj, index); + setValue(obj, obj->_info->propertyForTableColumn(prop.column_key), static_cast>(value)); +} + template id makeSetter(__unsafe_unretained RLMProperty *const prop) { if (prop.isPrimary) { @@ -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) { @@ -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) @@ -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); @@ -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) { @@ -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) { diff --git a/Realm/RLMArray.mm b/Realm/RLMArray.mm index ae34c64a71..b5e8f02348 100644 --- a/Realm/RLMArray.mm +++ b/Realm/RLMArray.mm @@ -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; } @@ -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(); @@ -608,7 +608,7 @@ - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { #pragma mark - Key Path Strings - (NSString *)propertyKey { - return _key; + return _property.name; } @end diff --git a/Realm/RLMArray_Private.hpp b/Realm/RLMArray_Private.hpp index 332b83cd7b..06e83dfe2b 100644 --- a/Realm/RLMArray_Private.hpp +++ b/Realm/RLMArray_Private.hpp @@ -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 diff --git a/Realm/RLMCollection.mm b/Realm/RLMCollection.mm index 6826e302db..416a82f808 100644 --- a/Realm/RLMCollection.mm +++ b/Realm/RLMCollection.mm @@ -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 @@ -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(); @@ -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(); @@ -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++; diff --git a/Realm/RLMCollection_Private.hpp b/Realm/RLMCollection_Private.hpp index 565dfa84ae..c144d8352a 100644 --- a/Realm/RLMCollection_Private.hpp +++ b/Realm/RLMCollection_Private.hpp @@ -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 diff --git a/Realm/RLMConstants.h b/Realm/RLMConstants.h index 0bc8db972c..b0b783313d 100644 --- a/Realm/RLMConstants.h +++ b/Realm/RLMConstants.h @@ -58,7 +58,7 @@ RLM_HEADER_AUDIT_BEGIN(nullability, sendability) /** `RLMPropertyType` is an enumeration describing all property types supported in Realm models. - For more information, see [Realm Models](https://www.mongodb.com/docs/realm/sdk/swift/fundamentals/object-models-and-schemas/). + For more information, see [Realm Models](https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/model-data/object-models/). */ typedef RLM_CLOSED_ENUM(int32_t, RLMPropertyType) { @@ -84,16 +84,59 @@ typedef RLM_CLOSED_ENUM(int32_t, RLMPropertyType) { RLMPropertyTypeAny = 9, /** Dates: `NSDate` */ RLMPropertyTypeDate = 4, + RLMPropertyTypeObjectId = 10, + RLMPropertyTypeDecimal128 = 11, #pragma mark - Linked object types - /** Realm model objects. See [Realm Models](https://www.mongodb.com/docs/realm/sdk/swift/fundamentals/object-models-and-schemas/) for more information. */ + /** Realm model objects. See [Realm Models](https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/model-data/object-models/) for more information. */ RLMPropertyTypeObject = 7, - /** Realm linking objects. See [Realm Models](https://www.mongodb.com/docs/realm/sdk/swift/fundamentals/relationships/#inverse-relationship) for more information. */ + /** Realm linking objects. See [Realm Models](https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/model-data/relationships/#define-an-inverse-relationship-property) for more information. */ RLMPropertyTypeLinkingObjects = 8, +}; - RLMPropertyTypeObjectId = 10, - RLMPropertyTypeDecimal128 = 11 +/** + `RLMAnyValueType` is an enumeration describing all property types supported by RLMValue (AnyRealmValue). + + For more information, see [Realm Models](https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/model-data/supported-types/#std-label-ios-anyrealmvalue-data-type). + */ +typedef RLM_CLOSED_ENUM(int32_t, RLMAnyValueType) { +#pragma mark - Primitive types + /** Integers: `NSInteger`, `int`, `long`, `Int` (Swift) */ + RLMAnyValueTypeInt = 0, + /** Booleans: `BOOL`, `bool`, `Bool` (Swift) */ + RLMAnyValueTypeBool = 1, + /** Floating-point numbers: `float`, `Float` (Swift) */ + RLMAnyValueTypeFloat = 5, + /** Double-precision floating-point numbers: `double`, `Double` (Swift) */ + RLMAnyValueTypeDouble = 6, + /** NSUUID, UUID */ + RLMAnyValueTypeUUID = 12, + +#pragma mark - Object types + + /** Strings: `NSString`, `String` (Swift) */ + RLMAnyValueTypeString = 2, + /** Binary data: `NSData` */ + RLMAnyValueTypeData = 3, + /** Any type: `id`, `AnyRealmValue` (Swift) */ + RLMAnyValueTypeAny = 9, + /** Dates: `NSDate` */ + RLMAnyValueTypeDate = 4, + RLMAnyValueTypeObjectId = 10, + RLMAnyValueTypeDecimal128 = 11, + +#pragma mark - Linked object types + + /** Realm model objects. See [Realm Models](https://www.mongodb.com/docs/realm/sdk/swift/fundamentals/object-models-and-schemas/) for more information. */ + RLMAnyValueTypeObject = 7, + /** Realm linking objects. See [Realm Models](https://www.mongodb.com/docs/realm/sdk/swift/fundamentals/relationships/#inverse-relationship) for more information. */ + RLMAnyValueTypeLinkingObjects = 8, + + /** Dictionary: `RLMDictionary`, `Map` (Swift) */ + RLMAnyValueTypeDictionary = 512, + /** Set: `RLMArray`, `List` (Swift) */ + RLMAnyValueTypeList = 128, }; #pragma mark - Notification Constants diff --git a/Realm/RLMDictionary.mm b/Realm/RLMDictionary.mm index 650a011e68..2d7dd96ca5 100644 --- a/Realm/RLMDictionary.mm +++ b/Realm/RLMDictionary.mm @@ -67,7 +67,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; } @@ -133,9 +133,9 @@ static void changeDictionary(__unsafe_unretained RLMDictionary *const dictionary dictionary->_backingCollection = [NSMutableDictionary new]; } if (RLMObjectBase *parent = dictionary->_parentObject) { - [parent willChangeValueForKey:dictionary->_key]; + [parent willChangeValueForKey:dictionary->_property.name]; f(); - [parent didChangeValueForKey:dictionary->_key]; + [parent didChangeValueForKey:dictionary->_property.name]; } else { f(); @@ -425,7 +425,7 @@ - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath #pragma mark - Key Path Strings - (NSString *)propertyKey { - return _key; + return _property.name; } #pragma mark - Methods unsupported on unmanaged RLMDictionary instances diff --git a/Realm/RLMDictionary_Private.hpp b/Realm/RLMDictionary_Private.hpp index f294dbacdb..fcd1be8b85 100644 --- a/Realm/RLMDictionary_Private.hpp +++ b/Realm/RLMDictionary_Private.hpp @@ -35,11 +35,10 @@ class RLMObservationInfo; @interface RLMDictionary () { @protected NSString *_objectClassName; - RLMPropertyType _type; BOOL _optional; @public - // The name of the property which this RLMDictionary represents - NSString *_key; + // The property which this RLMDictionary represents + RLMProperty *_property; __weak RLMObjectBase *_parentObject; } @end diff --git a/Realm/RLMManagedArray.mm b/Realm/RLMManagedArray.mm index 44aee52a6f..93191becde 100644 --- a/Realm/RLMManagedArray.mm +++ b/Realm/RLMManagedArray.mm @@ -33,6 +33,7 @@ #import "RLMThreadSafeReference_Private.hpp" #import "RLMUtil.hpp" +#import #import #import #import @@ -76,24 +77,15 @@ - (RLMManagedArray *)initWithBackingCollection:(realm::List)list REALM_ASSERT(list.get_realm() == _realm->_realm); _backingList = std::move(list); _ownerInfo = parentInfo; + _property = property; if (property.type == RLMPropertyTypeObject) _objectInfo = &parentInfo->linkTargetType(property.index); else _objectInfo = _ownerInfo; - _key = property.name; } return self; } -- (RLMManagedArray *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject - property:(__unsafe_unretained RLMProperty *const)property { - __unsafe_unretained RLMRealm *const realm = parentObject->_realm; - auto col = parentObject->_info->tableColumn(property); - return [self initWithBackingCollection:realm::List(realm->_realm, parentObject->_row, col) - parentInfo:parentObject->_info - property:property]; -} - - (RLMManagedArray *)initWithParent:(realm::Obj)parent property:(__unsafe_unretained RLMProperty *const)property parentInfo:(RLMClassInfo&)info { @@ -103,6 +95,13 @@ - (RLMManagedArray *)initWithParent:(realm::Obj)parent property:property]; } +- (RLMManagedArray *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject + property:(__unsafe_unretained RLMProperty *const)property { + return [self initWithParent:parentObject->_row + property:property + parentInfo:*parentObject->_info]; +} + void RLMValidateArrayObservationKey(__unsafe_unretained NSString *const keyPath, __unsafe_unretained RLMArray *const array) { if (![keyPath isEqualToString:RLMInvalidatedKey]) { @@ -141,7 +140,7 @@ static void changeArray(__unsafe_unretained RLMManagedArray *const ar, ar->_backingList.get_parent_object_key(), *ar->_ownerInfo); if (obsInfo) { - tracker.willChange(obsInfo, ar->_key, kind, is()); + tracker.willChange(obsInfo, ar->_property.name, kind, is()); } translateErrors(f); @@ -198,8 +197,8 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state } - (id)objectAtIndex:(NSUInteger)index { - return translateErrors([&] { - RLMAccessorContext context(*_objectInfo); + return translateErrors([&]() -> id { + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); return _backingList.get(context, index); }); } @@ -211,7 +210,7 @@ static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) { } changeArray(ar, NSKeyValueChangeInsertion, index, ^{ - RLMAccessorContext context(*ar->_objectInfo); + RLMAccessorContext context(*ar->_ownerInfo, *ar->_objectInfo, ar->_property); ar->_backingList.insert(context, index, object); }); } @@ -227,7 +226,7 @@ - (void)insertObject:(id)object atIndex:(NSUInteger)index { - (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)indexes { changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ NSUInteger index = [indexes firstIndex]; - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); for (id obj in objects) { RLMArrayValidateMatchingObjectType(self, obj); _backingList.insert(context, index, obj); @@ -252,7 +251,7 @@ - (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { - (void)addObjectsFromArray:(NSArray *)array { changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(self.count, array.count), ^{ - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); for (id obj in array) { RLMArrayValidateMatchingObjectType(self, obj); _backingList.add(context, obj); @@ -276,7 +275,7 @@ - (void)replaceAllObjectsWithObjects:(NSArray *)objects { return; } changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(0, objects.count), ^{ - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); _backingList.assign(context, objects); }); } @@ -284,7 +283,7 @@ - (void)replaceAllObjectsWithObjects:(NSArray *)objects { - (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)object { RLMArrayValidateMatchingObjectType(self, object); changeArray(self, NSKeyValueChangeReplacement, index, ^{ - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); if (index >= _backingList.size()) { @throw RLMException(@"Index %llu is out of bounds (must be less than %llu).", (unsigned long long)index, (unsigned long long)_backingList.size()); @@ -314,7 +313,7 @@ - (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)i - (NSUInteger)indexOfObject:(id)object { RLMArrayValidateMatchingObjectType(self, object); return translateErrors([&] { - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); return RLMConvertNotFound(_backingList.find(context, object)); }); } @@ -350,7 +349,7 @@ - (id)valueForKey:(NSString *)key { - (void)setValue:(id)value forKey:(NSString *)key { if ([key isEqualToString:@"self"]) { RLMArrayValidateMatchingObjectType(self, value); - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); translateErrors([&] { for (size_t i = 0, count = _backingList.size(); i < count; ++i) { _backingList.set(context, i, value); @@ -358,7 +357,7 @@ - (void)setValue:(id)value forKey:(NSString *)key { }); return; } - else if (_type == RLMPropertyTypeObject) { + else if (_property->_type == RLMPropertyTypeObject) { RLMArrayValidateMatchingObjectType(self, value); translateErrors([&] { _backingList.verify_in_transaction(); }); RLMCollectionSetValueForKey(self, key, value); @@ -369,31 +368,32 @@ - (void)setValue:(id)value forKey:(NSString *)key { } - (id)minOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingList, _objectInfo, _type, RLMCollectionTypeArray); + auto column = columnForProperty(property, _backingList, _objectInfo, _property->_type, RLMCollectionTypeArray); auto value = translateErrors([&] { return _backingList.min(column); }); return value ? RLMMixedToObjc(*value) : nil; } - (id)maxOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingList, _objectInfo, _type, RLMCollectionTypeArray); + auto column = columnForProperty(property, _backingList, _objectInfo, _property->_type, RLMCollectionTypeArray); auto value = translateErrors([&] { return _backingList.max(column); }); return value ? RLMMixedToObjc(*value) : nil; } - (id)sumOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingList, _objectInfo, _type, RLMCollectionTypeArray); + auto column = columnForProperty(property, _backingList, _objectInfo, _property->_type, RLMCollectionTypeArray); return RLMMixedToObjc(translateErrors([&] { return _backingList.sum(column); })); } - (id)averageOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingList, _objectInfo, _type, RLMCollectionTypeArray); + auto column = columnForProperty(property, _backingList, _objectInfo, _property->_type, RLMCollectionTypeArray); auto value = translateErrors([&] { return _backingList.average(column); }); return value ? RLMMixedToObjc(*value) : nil; } - (void)deleteObjectsFromRealm { - if (_type != RLMPropertyTypeObject) { - @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.", RLMTypeToString(_type)); + auto type = _property->_type; + if (type != RLMPropertyTypeObject) { + @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.", RLMTypeToString(type)); } // delete all target rows from the realm RLMObservationTracker tracker(_realm, true); @@ -415,7 +415,7 @@ - (RLMResults *)distinctResultsUsingKeyPaths:(NSArray *)keyPaths { } - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { - if (_type != RLMPropertyTypeObject) { + if (_property->_type != RLMPropertyTypeObject) { @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects"); } auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group); @@ -424,7 +424,7 @@ - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { } - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { - if (_type != RLMPropertyTypeObject) { + if (_property->_type != RLMPropertyTypeObject) { @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects"); } realm::Query query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, @@ -439,7 +439,7 @@ - (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { size_t c = self.count; NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:indexes.count]; NSUInteger i = [indexes firstIndex]; - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); while (i != NSNotFound) { // Given KVO relies on `objectsAtIndexes` we need to make sure // that no out of bounds exceptions are generated. This disallows us to mirror @@ -484,7 +484,9 @@ - (RLMFastEnumerator *)fastEnumerator { return translateErrors([&] { return [[RLMFastEnumerator alloc] initWithBackingCollection:_backingList collection:self - classInfo:*_objectInfo]; + classInfo:_objectInfo + parentInfo:_ownerInfo + property:_property]; }); } @@ -497,7 +499,7 @@ - (instancetype)resolveInRealm:(RLMRealm *)realm { return translateErrors([&] { return [[self.class alloc] initWithBackingCollection:_backingList.freeze(realm->_realm) parentInfo:&parentInfo - property:parentInfo.rlmObjectSchema[_key]]; + property:parentInfo.rlmObjectSchema[_property.name]]; }); } @@ -529,7 +531,7 @@ - (instancetype)thaw { - (RLMManagedArrayHandoverMetadata *)objectiveCMetadata { RLMManagedArrayHandoverMetadata *metadata = [[RLMManagedArrayHandoverMetadata alloc] init]; metadata.parentClassName = _ownerInfo->rlmObjectSchema.className; - metadata.key = _key; + metadata.key = _property.name; return metadata; } diff --git a/Realm/RLMManagedDictionary.mm b/Realm/RLMManagedDictionary.mm index fa3c1ec7e2..73e38504d8 100644 --- a/Realm/RLMManagedDictionary.mm +++ b/Realm/RLMManagedDictionary.mm @@ -32,6 +32,7 @@ #import "RLMThreadSafeReference_Private.hpp" #import "RLMUtil.hpp" +#import #import #import #import @@ -94,6 +95,7 @@ @implementation RLMManagedDictionary { RLMRealm *_realm; RLMClassInfo *_objectInfo; RLMClassInfo *_ownerInfo; + RLMProperty *_property; std::unique_ptr _observationInfo; } @@ -102,6 +104,11 @@ - (RLMManagedDictionary *)initWithBackingCollection:(realm::object_store::Dictio property:(__unsafe_unretained RLMProperty *const)property { if (property.type == RLMPropertyTypeObject) self = [self initWithObjectClassName:property.objectClassName keyType:property.dictionaryKeyType]; + else if (property.type == RLMPropertyTypeAny) + // Because the property is type mixed and we don't know if it will contain a dictionary when the schema + // is created, we set RLMPropertyTypeString by default. + // If another type is used for the dictionary key in a mixed dictionary context, this will thrown by core. + self = [self initWithObjectType:property.type optional:property.optional keyType:RLMPropertyTypeString]; else self = [self initWithObjectType:property.type optional:property.optional keyType:property.dictionaryKeyType]; if (self) { @@ -113,20 +120,11 @@ - (RLMManagedDictionary *)initWithBackingCollection:(realm::object_store::Dictio _objectInfo = &parentInfo->linkTargetType(property.index); else _objectInfo = _ownerInfo; - _key = property.name; + _property = property; } return self; } -- (RLMManagedDictionary *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject - property:(__unsafe_unretained RLMProperty *const)property { - __unsafe_unretained RLMRealm *const realm = parentObject->_realm; - auto col = parentObject->_info->tableColumn(property); - return [self initWithBackingCollection:realm::object_store::Dictionary(realm->_realm, parentObject->_row, col) - parentInfo:parentObject->_info - property:property]; -} - - (RLMManagedDictionary *)initWithParent:(realm::Obj)parent property:(__unsafe_unretained RLMProperty *const)property parentInfo:(RLMClassInfo&)info { @@ -136,6 +134,13 @@ - (RLMManagedDictionary *)initWithParent:(realm::Obj)parent property:property]; } +- (RLMManagedDictionary *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject + property:(__unsafe_unretained RLMProperty *const)property { + return [self initWithParent:parentObject->_row + property:property + parentInfo:*parentObject->_info]; +} + void RLMDictionaryValidateObservationKey(__unsafe_unretained NSString *const keyPath, __unsafe_unretained RLMDictionary *const dictionary) { if (![keyPath isEqualToString:RLMInvalidatedKey]) { @@ -176,7 +181,7 @@ static void changeDictionary(__unsafe_unretained RLMManagedDictionary *const dic dict->_backingCollection.get_parent_object_key(), *dict->_ownerInfo); if (obsInfo) { - tracker.willChange(obsInfo, dict->_key); + tracker.willChange(obsInfo, dict->_property.name); } translateErrors(f); @@ -244,18 +249,27 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state - (nullable id)objectForKey:(id)key { return translateErrors([&]() -> id { [self.realm verifyThread]; - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); if (auto value = _backingCollection.try_get_any(context.unbox(key))) { - return context.box(*value); + if (value->is_type(realm::type_Dictionary)) { + return context.box(_backingCollection.get_dictionary(realm::PathElement{context.unbox(key)})); + } + else if (value->is_type(realm::type_List)) { + return context.box(_backingCollection.get_list(realm::PathElement{context.unbox(key)})); + } + else { + return context.box(*value); + } } + return nil; }); } - (void)setObject:(id)obj forKey:(id)key { changeDictionary(self, ^{ - RLMAccessorContext c(*_objectInfo); - _backingCollection.insert(c, c.unbox(RLMDictionaryKey(self, key)), + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); + _backingCollection.insert(context, context.unbox(RLMDictionaryKey(self, key)), RLMDictionaryValue(self, obj)); }); } @@ -267,7 +281,7 @@ - (void)removeAllObjects { } - (void)removeObjectsForKeys:(NSArray *)keyArray { - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); changeDictionary(self, [&] { for (id key in keyArray) { _backingCollection.try_erase(context.unbox(key)); @@ -277,17 +291,17 @@ - (void)removeObjectsForKeys:(NSArray *)keyArray { - (void)removeObjectForKey:(id)key { changeDictionary(self, ^{ - RLMAccessorContext context(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); _backingCollection.try_erase(context.unbox(key)); }); } - (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block { - RLMAccessorContext c(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); BOOL stop = false; @autoreleasepool { for (auto&& [key, value] : _backingCollection) { - block(c.box(key), c.box(value), &stop); + block(context.box(key), _backingCollection.get(context, key.get_string()), &stop); if (stop) { break; } @@ -306,12 +320,12 @@ - (void)mergeDictionary:(id)dictionary clear:(bool)clear { } changeDictionary(self, ^{ - RLMAccessorContext c(*_objectInfo); + RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); if (clear) { _backingCollection.remove_all(); } [dictionary enumerateKeysAndObjectsUsingBlock:[&](id key, id value, BOOL *) { - _backingCollection.insert(c, c.unbox(RLMDictionaryKey(self, key)), + _backingCollection.insert(context, context.unbox(RLMDictionaryKey(self, key)), RLMDictionaryValue(self, value)); }]; }); @@ -351,7 +365,7 @@ - (void)setValue:(id)value forKey:(nonnull NSString *)key { } - (id)minOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingCollection, _objectInfo, _type, RLMCollectionTypeDictionary); + auto column = columnForProperty(property, _backingCollection, _objectInfo, _property->_type, RLMCollectionTypeDictionary); auto value = translateErrors([&] { return _backingCollection.as_results().min(column); }); @@ -359,7 +373,7 @@ - (id)minOfProperty:(NSString *)property { } - (id)maxOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingCollection, _objectInfo, _type, RLMCollectionTypeDictionary); + auto column = columnForProperty(property, _backingCollection, _objectInfo, _property->_type, RLMCollectionTypeDictionary); auto value = translateErrors([&] { return _backingCollection.as_results().max(column); }); @@ -367,7 +381,7 @@ - (id)maxOfProperty:(NSString *)property { } - (id)sumOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingCollection, _objectInfo, _type, RLMCollectionTypeDictionary); + auto column = columnForProperty(property, _backingCollection, _objectInfo, _property->_type, RLMCollectionTypeDictionary); auto value = translateErrors([&] { return _backingCollection.as_results().sum(column); }); @@ -375,7 +389,7 @@ - (id)sumOfProperty:(NSString *)property { } - (id)averageOfProperty:(NSString *)property { - auto column = columnForProperty(property, _backingCollection, _objectInfo, _type, RLMCollectionTypeDictionary); + auto column = columnForProperty(property, _backingCollection, _objectInfo, _property->_type, RLMCollectionTypeDictionary); auto value = translateErrors([&] { return _backingCollection.as_results().average(column); }); @@ -383,8 +397,9 @@ - (id)averageOfProperty:(NSString *)property { } - (void)deleteObjectsFromRealm { - if (_type != RLMPropertyTypeObject) { - @throw RLMException(@"Cannot delete objects from RLMManagedDictionary: only RLMObjects can be deleted.", RLMTypeToString(_type), _optional? @"?": @""); + auto type = _property->_type; + if (type != RLMPropertyTypeObject) { + @throw RLMException(@"Cannot delete objects from RLMManagedDictionary: only RLMObjects can be deleted.", RLMTypeToString(type), _optional? @"?": @""); } // delete all target rows from the realm RLMObservationTracker tracker(_realm, true); @@ -415,7 +430,7 @@ - (RLMResults *)distinctResultsUsingKeyPaths:(NSArray *)keyPaths { } - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { - if (_type != RLMPropertyTypeObject) { + if (_property->_type != RLMPropertyTypeObject) { @throw RLMException(@"Querying is currently only implemented for dictionaries of Realm Objects"); } auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group); @@ -443,7 +458,10 @@ - (RLMFastEnumerator *)fastEnumerator { return translateErrors([&] { return [[RLMFastEnumerator alloc] initWithBackingDictionary:_backingCollection dictionary:self - classInfo:*_objectInfo]; + classInfo:_objectInfo + parentInfo:_ownerInfo + property:_property + ]; }); } @@ -456,7 +474,7 @@ - (instancetype)resolveInRealm:(RLMRealm *)realm { return translateErrors([&] { return [[self.class alloc] initWithBackingCollection:_backingCollection.freeze(realm->_realm) parentInfo:&parentInfo - property:parentInfo.rlmObjectSchema[_key]]; + property:parentInfo.rlmObjectSchema[_property.name]]; }); } @@ -519,7 +537,7 @@ void operator()(realm::DictionaryChangeSet const& changes) { - (RLMManagedCollectionHandoverMetadata *)objectiveCMetadata { RLMManagedCollectionHandoverMetadata *metadata = [[RLMManagedCollectionHandoverMetadata alloc] init]; metadata.parentClassName = _ownerInfo->rlmObjectSchema.className; - metadata.key = _key; + metadata.key = _property.name; return metadata; } diff --git a/Realm/RLMManagedSet.mm b/Realm/RLMManagedSet.mm index bc03584e2c..9cff4c1c6f 100644 --- a/Realm/RLMManagedSet.mm +++ b/Realm/RLMManagedSet.mm @@ -74,11 +74,11 @@ - (RLMManagedSet *)initWithBackingCollection:(realm::object_store::Set)set REALM_ASSERT(set.get_realm() == _realm->_realm); _backingSet = std::move(set); _ownerInfo = parentInfo; + _property = property; if (property.type == RLMPropertyTypeObject) _objectInfo = &parentInfo->linkTargetType(property.index); else _objectInfo = _ownerInfo; - _key = property.name; } return self; } @@ -138,7 +138,7 @@ static void changeSet(__unsafe_unretained RLMManagedSet *const set, set->_backingSet.get_parent_object_key(), *set->_ownerInfo); if (obsInfo) { - tracker.willChange(obsInfo, set->_key); + tracker.willChange(obsInfo, set->_property.name); } translateErrors(f); @@ -482,7 +482,9 @@ - (RLMFastEnumerator *)fastEnumerator { return translateErrors([&] { return [[RLMFastEnumerator alloc] initWithBackingCollection:_backingSet collection:self - classInfo:*_objectInfo]; + classInfo:_objectInfo + parentInfo:_ownerInfo + property:_property]; }); } @@ -499,7 +501,7 @@ - (instancetype)resolveInRealm:(RLMRealm *)realm { return translateErrors([&] { return [[self.class alloc] initWithBackingCollection:_backingSet.freeze(realm->_realm) parentInfo:&parentInfo - property:parentInfo.rlmObjectSchema[_key]]; + property:parentInfo.rlmObjectSchema[_property.name]]; }); } @@ -531,7 +533,7 @@ - (instancetype)thaw { - (RLMManagedSetHandoverMetadata *)objectiveCMetadata { RLMManagedSetHandoverMetadata *metadata = [[RLMManagedSetHandoverMetadata alloc] init]; metadata.parentClassName = _ownerInfo->rlmObjectSchema.className; - metadata.key = _key; + metadata.key = _property.name; return metadata; } diff --git a/Realm/RLMQueryUtil.mm b/Realm/RLMQueryUtil.mm index ec3922ae3c..4b33516430 100644 --- a/Realm/RLMQueryUtil.mm +++ b/Realm/RLMQueryUtil.mm @@ -30,6 +30,7 @@ #import #import #import +#import #import #import #import @@ -484,7 +485,6 @@ static Type type_for_name(NSString *name) { void apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema); - void apply_collection_operator_expression(KeyPath&& kp, id value, NSComparisonPredicate *pred); void apply_value_expression(KeyPath&& kp, id value, NSComparisonPredicate *pred); void apply_column_expression(KeyPath&& left, KeyPath&& right, NSComparisonPredicate *predicate); @@ -566,10 +566,9 @@ void add_collection_operation_constraint(NSPredicateOperatorType operatorType, const CollectionOperation& collectionOperation, R&& rhs, NSComparisonPredicateOptions comparisionOptions); - CollectionOperation collection_operation_from_key_path(KeyPath&& kp); ColumnReference column_reference_from_key_path(KeyPath&& kp, bool isAggregate); - + NSString* get_path_elements(std::vector &paths, NSExpression *expression); private: Query& m_query; @@ -1328,7 +1327,7 @@ KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, N ColumnReference QueryBuilder::column_reference_from_key_path(KeyPath&& kp, bool isAggregate) { - if (isAggregate && !kp.containsToManyRelationship) { + if (isAggregate && !kp.containsToManyRelationship && kp.property.type != RLMPropertyTypeAny) { throwException(@"Invalid predicate", @"Aggregate operations can only be used on key paths that include an collection property"); } else if (!isAggregate && kp.containsToManyRelationship) { @@ -1684,23 +1683,22 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression) void QueryBuilder::apply_map_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, NSComparisonPredicateOptions options, NSPredicateOperatorType operatorType, NSExpression *right) { - NSString *keyPath; - NSString *mapKey; - if (functionExpression.operand.expressionType == NSKeyPathExpressionType) { - NSExpression *mapItems = [functionExpression.arguments firstObject]; - NSExpression *linkCol = [[functionExpression.operand arguments] firstObject]; - NSExpression *mapCol = [mapItems.arguments firstObject]; - mapKey = [mapItems.arguments[1] constantValue]; - keyPath = [NSString stringWithFormat:@"%@.%@", linkCol.keyPath, mapCol.keyPath]; - } else { - keyPath = [functionExpression.arguments.firstObject keyPath]; - mapKey = [functionExpression.arguments[1] constantValue]; - } + std::vector pathElements; + NSString *keyPath = get_path_elements(pathElements, functionExpression); ColumnReference collectionColumn = column_reference_from_key_path(key_path_from_string(m_schema, objectSchema, keyPath), true); - RLMPrecondition(collectionColumn.property().dictionary, @"Invalid predicate", - @"Invalid keypath '%@': only dictionaries support subscript predicates.", functionExpression); - add_mixed_constraint(operatorType, options, std::move(collectionColumn.resolve().key(mapKey.UTF8String)), right.constantValue); + + if (collectionColumn.property().type == RLMPropertyTypeAny && !collectionColumn.property().dictionary) { + add_mixed_constraint(operatorType, options, std::move(collectionColumn.resolve().path(pathElements)), right.constantValue); + } else { + RLMPrecondition(collectionColumn.property().dictionary, @"Invalid predicate", + @"Invalid keypath '%@': only dictionaries and realm `Any` support subscript predicates.", functionExpression); + RLMPrecondition(pathElements.size() == 1, @"Invalid subscript size", + @"Invalid subscript size '%@': nested dictionaries queries are only allowed in mixed properties.", functionExpression); + RLMPrecondition(pathElements[0].is_key(), @"Invalid subscript type", + @"Invalid subscript type '%@'; only string keys are allowed as subscripts in dictionary queries.", functionExpression); + add_mixed_constraint(operatorType, options, std::move(collectionColumn.resolve().key(pathElements[0].get_key())), right.constantValue); + } } void QueryBuilder::apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, @@ -1850,6 +1848,47 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression) @"Only support compound, comparison, and constant predicates"); } } + +// This function returns the nested subscripts from a NSPredicate with the following format `anyCol[0]['key'][#any]` +// and its respective keypath (including any linked keypath) +// This will iterate each argument of the NSExpression and its nested NSExpressions, takes the constant subscript +// and creates a PathElement to be used in the query. If we use `#any` as a wildcard this will show in the parser +// predicate as NSKeyPathExpressionType. +NSString* QueryBuilder::get_path_elements(std::vector &paths, NSExpression *expression) { + NSString *keyPath = @""; + for (NSUInteger i = 0; i < expression.arguments.count; i++) { + NSString *nestedKeyPath = @""; + if (expression.arguments[i].expressionType == NSFunctionExpressionType) { + nestedKeyPath = get_path_elements(paths, expression.arguments[i]); + } else if (expression.arguments[i].expressionType == NSConstantValueExpressionType) { + id value = [expression.arguments[i] constantValue]; + if ([value isKindOfClass:[NSNumber class]]) { + paths.push_back(PathElement{[(NSNumber *)value intValue]}); + } else if ([value isKindOfClass:[NSString class]]) { + NSString *key = (NSString *)value; + paths.push_back(PathElement{key.UTF8String}); + } else { + throwException(@"Invalid subscript type", + @"Invalid subscript type '%@': Only `Strings` or index are allowed subscripts", expression); + } + } else if (expression.arguments[i].expressionType == NSKeyPathExpressionType) { + auto keyPath = [(id)expression.arguments[i] predicateFormat]; + if ([keyPath isEqual:@"#any"]) { + paths.emplace_back(); + } else { + nestedKeyPath = keyPath; + } + } else { + throwException(@"Invalid expression type", + @"Invalid expression type '%@': Subscripts queries don't allow any other expression types", expression); + } + if ([nestedKeyPath length] > 0) { + keyPath = ([keyPath length] > 0) ? [NSString stringWithFormat:@"%@.%@", keyPath, nestedKeyPath] : nestedKeyPath; + } + } + + return keyPath; +} } // namespace realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema, diff --git a/Realm/RLMSet.h b/Realm/RLMSet.h index fead2e0051..70fde81e6e 100644 --- a/Realm/RLMSet.h +++ b/Realm/RLMSet.h @@ -514,7 +514,7 @@ __attribute__((warn_unused_result)); /** `-[RLMSet init]` is not available because `RLMSet`s cannot be created directly. - ``RLMSet` properties on `RLMObject`s are lazily created when accessed. + `RLMSet` properties on `RLMObject`s are lazily created when accessed. */ - (instancetype)init __attribute__((unavailable("RLMSets cannot be created directly"))); diff --git a/Realm/RLMSet.mm b/Realm/RLMSet.mm index bfe44dfd49..975f105371 100644 --- a/Realm/RLMSet.mm +++ b/Realm/RLMSet.mm @@ -70,7 +70,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; } @@ -240,9 +240,9 @@ static void changeSet(__unsafe_unretained RLMSet *const set, } if (RLMObjectBase *parent = set->_parentObject) { - [parent willChangeValueForKey:set->_key]; + [parent willChangeValueForKey:set->_property.name]; f(); - [parent didChangeValueForKey:set->_key]; + [parent didChangeValueForKey:set->_property.name]; } else { f(); @@ -484,7 +484,7 @@ void RLMSetValidateMatchingObjectType(__unsafe_unretained RLMSet *const set, #pragma mark - Key Path Strings - (NSString *)propertyKey { - return _key; + return _property.name; } #pragma mark - Methods unsupported on unmanaged RLMSet instances diff --git a/Realm/RLMSet_Private.hpp b/Realm/RLMSet_Private.hpp index 0fde608cc8..cb20d276e2 100644 --- a/Realm/RLMSet_Private.hpp +++ b/Realm/RLMSet_Private.hpp @@ -41,7 +41,7 @@ class RLMObservationInfo; BOOL _optional; @public // The name of the property which this RLMSet represents - NSString *_key; + RLMProperty *_property; __weak RLMObjectBase *_parentObject; } @end diff --git a/Realm/RLMSwiftValueStorage.mm b/Realm/RLMSwiftValueStorage.mm index 80cfc33456..e075231c83 100644 --- a/Realm/RLMSwiftValueStorage.mm +++ b/Realm/RLMSwiftValueStorage.mm @@ -72,7 +72,7 @@ void attach(__unsafe_unretained RLMObjectBase *const obj, NSString *property) { : _realm(obj->_realm) , _object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row) , _columnName(prop.columnName.UTF8String) - , _ctx(*obj->_info) + , _ctx(obj, &obj->_info->objectSchema->persisted_properties[prop.index]) { } diff --git a/Realm/RLMUtil.hpp b/Realm/RLMUtil.hpp index 2fe906e274..57f48f3639 100644 --- a/Realm/RLMUtil.hpp +++ b/Realm/RLMUtil.hpp @@ -203,13 +203,17 @@ static inline void RLMNSStringToStdString(std::string &out, NSString *in) { options:0 range:{0, in.length} remainingRange:nullptr]; out.resize(size); } - -realm::Mixed RLMObjcToMixed(__unsafe_unretained id value, - __unsafe_unretained RLMRealm *realm=nil, +realm::Mixed RLMObjcToMixed(__unsafe_unretained id const value, + __unsafe_unretained RLMRealm *const realm=nil, realm::CreatePolicy createPolicy={}); +realm::Mixed RLMObjcToMixedPrimitives(__unsafe_unretained id const value, + __unsafe_unretained RLMRealm *const realm, + realm::CreatePolicy createPolicy); id RLMMixedToObjc(realm::Mixed const& value, __unsafe_unretained RLMRealm *realm=nil, - RLMClassInfo *classInfo=nullptr); + RLMClassInfo *classInfo=nullptr, + RLMProperty *property=nullptr, + realm::Obj obj={}); realm::Decimal128 RLMObjcToDecimal128(id value); realm::UUID RLMObjcToUUID(__unsafe_unretained id const value); diff --git a/Realm/RLMUtil.mm b/Realm/RLMUtil.mm index f10cd2fbde..c075536dd1 100644 --- a/Realm/RLMUtil.mm +++ b/Realm/RLMUtil.mm @@ -21,7 +21,7 @@ #import "RLMArray_Private.hpp" #import "RLMAccessor.hpp" #import "RLMDecimal128_Private.hpp" -#import "RLMDictionary_Private.h" +#import "RLMDictionary_Private.hpp" #import "RLMError_Private.hpp" #import "RLMObjectId_Private.hpp" #import "RLMObjectSchema_Private.hpp" @@ -37,6 +37,7 @@ #import "RLMValue.h" #import +#import #import #include @@ -47,7 +48,7 @@ #endif static inline RLMArray *asRLMArray(__unsafe_unretained id const value) { - return RLMDynamicCast(value) ?: RLMDynamicCast(value)._rlmCollection; + return RLMDynamicCast(value) ?: (RLMArray *)RLMDynamicCast(value)._rlmCollection; } static inline RLMSet *asRLMSet(__unsafe_unretained id const value) { @@ -55,7 +56,7 @@ } static inline RLMDictionary *asRLMDictionary(__unsafe_unretained id const value) { - return RLMDynamicCast(value) ?: RLMDynamicCast(value)._rlmCollection; + return RLMDynamicCast(value) ?: (RLMDictionary *)RLMDynamicCast(value)._rlmCollection; } static inline bool checkCollectionType(__unsafe_unretained id const collection, @@ -289,6 +290,7 @@ void RLMValidateValueForProperty(__unsafe_unretained id const obj, if (prop.type == RLMPropertyTypeObject && !validateObjects) { return; } + if (RLMIsObjectValidForProperty(obj, prop)) { return; } @@ -401,20 +403,34 @@ BOOL RLMIsRunningInPlayground() { } REALM_ASSERT([v conformsToProtocol:@protocol(RLMValue)]); } + + switch ([v rlm_anyValueType]) { + case RLMAnyValueTypeList: + return realm::Mixed(0, realm::CollectionType::List); + case RLMAnyValueTypeDictionary: + return realm::Mixed(0, realm::CollectionType::Dictionary); + default: + return RLMObjcToMixedPrimitives(v, realm, createPolicy); + } +} - RLMPropertyType type = [v rlm_valueType]; +realm::Mixed RLMObjcToMixedPrimitives(__unsafe_unretained id const value, + __unsafe_unretained RLMRealm *const realm, + realm::CreatePolicy createPolicy) { + RLMAnyValueType type = [value rlm_anyValueType]; return switch_on_type(static_cast(type), realm::util::overload{[&](realm::Obj*) { - // The RLMObjectBase may be unmanaged and therefor has no RLMClassInfo attached. + // The RLMObjectBase may be unmanaged and therefore has no RLMClassInfo attached. // So we fetch from the Realm instead. // If the Object is managed use it's RLMClassInfo instead so we do not have to do a // lookup in the table of schemas. - RLMObjectBase *objBase = v; + RLMObjectBase *objBase = value; RLMAccessorContext c{objBase->_info ? *objBase->_info : realm->_info[objBase->_objectSchema.className]}; - auto obj = c.unbox(v, createPolicy); + auto obj = c.unbox(value, createPolicy); return obj.is_valid() ? realm::Mixed(obj) : realm::Mixed(); }, [&](auto t) { RLMStatelessAccessorContext c; - return realm::Mixed(c.unbox>(v)); + auto mixed = realm::Mixed(c.unbox>(value)); + return mixed; }, [&](realm::Mixed*) -> realm::Mixed { REALM_UNREACHABLE(); }}); @@ -422,7 +438,9 @@ BOOL RLMIsRunningInPlayground() { id RLMMixedToObjc(realm::Mixed const& mixed, __unsafe_unretained RLMRealm *realm, - RLMClassInfo *classInfo) { + RLMClassInfo *classInfo, + RLMProperty *property, + realm::Obj obj) { if (mixed.is_null()) { return NSNull.null; } @@ -454,7 +472,15 @@ id RLMMixedToObjc(realm::Mixed const& mixed, case realm::type_UUID: return [[NSUUID alloc] initWithRealmUUID:mixed.get()]; default: - @throw RLMException(@"Invalid data type for RLMPropertyTypeAny property."); + if (mixed.is_type(realm::type_Dictionary)) { + return [[RLMManagedDictionary alloc] initWithParent:obj property:property parentInfo:*classInfo]; + } + else if (mixed.is_type(realm::type_List)) { + return [[RLMManagedArray alloc] initWithParent:obj property:property parentInfo:*classInfo]; + } + else { + @throw RLMException(@"Invalid data type for RLMPropertyTypeAny property."); + } } } diff --git a/Realm/RLMValue.h b/Realm/RLMValue.h index ac1f10ee45..0e1a81e3ad 100644 --- a/Realm/RLMValue.h +++ b/Realm/RLMValue.h @@ -16,8 +16,10 @@ // //////////////////////////////////////////////////////////////////////////// +#import #import #import +#import #import #import #import @@ -52,11 +54,17 @@ `RLMObject `RLMObjectId` `RLMDecimal128` + `RLMDictionary` + `RLMArray` + `NSArray` + `NSDictionary` */ @protocol RLMValue /// Describes the type of property stored. -@property (readonly) RLMPropertyType rlm_valueType; +@property (readonly) RLMAnyValueType rlm_valueType __attribute__((deprecated("Use `rlm_anyValueType` instead, which includes collection types as well"))); +/// Describes the type of property stored. +@property (readonly) RLMAnyValueType rlm_anyValueType; @end @@ -95,3 +103,19 @@ /// :nodoc: @interface RLMObjectId (RLMValue) @end + +/// :nodoc: +@interface NSDictionary (RLMValue) +@end + +/// :nodoc: +@interface NSArray (RLMValue) +@end + +/// :nodoc: +@interface RLMArray (RLMValue) +@end + +/// :nodoc: +@interface RLMDictionary (RLMValue) +@end diff --git a/Realm/RLMValue.mm b/Realm/RLMValue.mm index ec36455728..1e8a89ef12 100644 --- a/Realm/RLMValue.mm +++ b/Realm/RLMValue.mm @@ -23,9 +23,16 @@ @implementation NSData (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeData; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeData; +} @end @@ -33,9 +40,16 @@ - (RLMPropertyType)rlm_valueType { @implementation NSDate (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeDate; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeDate; +} @end @@ -43,6 +57,8 @@ - (RLMPropertyType)rlm_valueType { @implementation NSNumber (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { if ([self objCType][0] == 'c' && (self.intValue == 0 || self.intValue == 1)) { return RLMPropertyTypeBool; @@ -60,6 +76,25 @@ - (RLMPropertyType)rlm_valueType { @throw RLMException(@"Unknown numeric type on type RLMValue."); } } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + if ([self objCType][0] == 'c' && (self.intValue == 0 || self.intValue == 1)) { + return RLMAnyValueTypeBool; + } + else if (numberIsInteger(self)) { + return RLMAnyValueTypeInt; + } + else if (*@encode(float) == [self objCType][0]) { + return RLMAnyValueTypeFloat; + } + else if (*@encode(double) == [self objCType][0]) { + return RLMAnyValueTypeDouble; + } + else { + @throw RLMException(@"Unknown numeric type on type RLMValue."); + } +} @end @@ -67,9 +102,16 @@ - (RLMPropertyType)rlm_valueType { @implementation NSNull (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeAny; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeAny; +} @end @@ -77,9 +119,16 @@ - (RLMPropertyType)rlm_valueType { @implementation NSString (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeString; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeString; +} @end @@ -87,9 +136,16 @@ - (RLMPropertyType)rlm_valueType { @implementation NSUUID (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeUUID; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeUUID; +} @end @@ -97,9 +153,16 @@ - (RLMPropertyType)rlm_valueType { @implementation RLMDecimal128 (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeDecimal128; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeDecimal128; +} @end @@ -107,9 +170,16 @@ - (RLMPropertyType)rlm_valueType { @implementation RLMObjectBase (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeObject; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeObject; +} @end @@ -117,8 +187,79 @@ - (RLMPropertyType)rlm_valueType { @implementation RLMObjectId (RLMValue) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeObjectId; } +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeObjectId; +} + +@end + +#pragma mark Dictionary + +@implementation NSDictionary (RLMValue) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (RLMPropertyType)rlm_valueType { + return RLMPropertyTypeAny; +} +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeDictionary; +} + +@end + +@implementation RLMDictionary (RLMValue) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (RLMPropertyType)rlm_valueType { return RLMPropertyTypeAny; + return RLMPropertyTypeAny; +} +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeDictionary; +} + +@end + +#pragma mark Array + +@implementation NSArray (RLMValue) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (RLMPropertyType)rlm_valueType { + return RLMPropertyTypeAny; +} +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeList; +} + +@end + +@implementation RLMArray (RLMValue) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (RLMPropertyType)rlm_valueType { + return RLMPropertyTypeAny; +} +#pragma clang diagnostic pop + +- (RLMAnyValueType)rlm_anyValueType { + return RLMAnyValueTypeList; +} @end diff --git a/Realm/Tests/KVOTests.mm b/Realm/Tests/KVOTests.mm index 3b2e90f8cf..5d3d5df838 100644 --- a/Realm/Tests/KVOTests.mm +++ b/Realm/Tests/KVOTests.mm @@ -1707,6 +1707,64 @@ - (void)testObserveArrayCount { [mutator addObject:obj]; AssertChanged(r, @0, @1); } + +- (void)testMixedCollectionKVC { + KVOObject *obj = [self createObject]; + NSDictionary *d = @{ @"key2" : @"hello2", + @"key3" : @YES, + @"key4" : @123, + @"key5" : @456.789 }; + + NSArray *a = @[ @"hello2", @YES, @123, @456.789 ]; + + { + KVORecorder r(self, obj, @"anyCol"); + obj.anyCol = d; + AssertCollectionChanged(); + } + + { + KVORecorder r(self, obj, @"anyCol"); + [obj setValue:d forKey:@"anyCol"]; + AssertCollectionChanged(); + [obj setValue:nil forKey:@"anyCol"]; + AssertCollectionChanged(); + } + + { + KVORecorder r(self, obj, @"anyCol"); + obj.anyCol = a; + AssertCollectionChanged(); + } + + { + KVORecorder r(self, obj, @"anyCol"); + [obj setValue:a forKey:@"anyCol"]; + AssertCollectionChanged(); + [obj setValue:nil forKey:@"anyCol"]; + AssertCollectionChanged(); + } + + if (![obj respondsToSelector:@selector(setObject:forKeyedSubscript:)]) { + return; + } + + { + KVORecorder r(self, obj, @"anyCol"); + obj[@"anyCol"] = d; + AssertCollectionChanged(); + obj[@"anyCol"] = nil; + AssertCollectionChanged(); + } + + { + KVORecorder r(self, obj, @"anyCol"); + obj[@"anyCol"] = a; + AssertCollectionChanged(); + obj[@"anyCol"] = nil; + AssertCollectionChanged(); + } +} @end // Run tests on an unmanaged RLMObject instance diff --git a/Realm/Tests/NotificationTests.m b/Realm/Tests/NotificationTests.m index b061d6295f..d10be6cfbb 100644 --- a/Realm/Tests/NotificationTests.m +++ b/Realm/Tests/NotificationTests.m @@ -262,7 +262,7 @@ - (NSInteger)row { } @end -static RLMCollectionChange *getChange(RLMTestCase *self, void (^block)(RLMRealm *)) { +static RLMCollectionChange *getChange(RLMTestCase *self, void (^block)(RLMRealm *realm)) { [self prepare]; __block bool first = true; @@ -292,7 +292,7 @@ - (NSInteger)row { } static void ExpectChange(id self, NSArray *deletions, NSArray *insertions, - NSArray *modifications, void (^block)(RLMRealm *)) { + NSArray *modifications, void (^block)(RLMRealm *realm)) { RLMCollectionChange *changes = getChange(self, block); XCTAssertNotNil(changes); if (!changes) { @@ -1727,3 +1727,393 @@ - (void)testModifyUnobservedKeyPathArrayProperty { } @end + +static void ExpectObjectChange(RLMTestCase *self, void (^block)(RLMRealm *realm)) { + [self prepare]; + RLMResults *query = [self query]; + __block XCTestExpectation *expectation = nil; + RLMNotificationToken *token = [[query firstObject] addNotificationBlock:^(BOOL deleted, NSArray *changes, NSError *error) { + XCTAssertFalse(deleted); + XCTAssertNil(error); + XCTAssertEqual(changes.count, 1); + [expectation fulfill]; + }]; + + RLMRealm *realm = [RLMRealm defaultRealm]; + expectation = [self expectationWithDescription:@"collections_mixed_notifications"]; + [realm beginWriteTransaction]; + block(realm); + [realm commitWriteTransaction]; + [self waitForExpectationsWithTimeout:10.0 handler:nil]; + + [token invalidate]; +} + +static void ExpectMixedDictionaryChange(RLMTestCase *self, NSArray *deletions, NSArray *insertions, + NSArray *modifications, void (^block)(RLMRealm *realm)) { + [self prepare]; + MixedObject *mixedObject = [[self query] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + __block XCTestExpectation *expectation = nil; + RLMNotificationToken *token = [dictionary addNotificationBlock:^(RLMDictionary *dictionary, RLMDictionaryChange *changes, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(dictionary); + if (!changes) { + return; + } + + XCTAssertEqualObjects(deletions, changes.deletions); + XCTAssertEqualObjects(insertions, changes.insertions); + XCTAssertEqualObjects(modifications, changes.modifications); + + [expectation fulfill]; + }]; + + RLMRealm *realm = [RLMRealm defaultRealm]; + expectation = [self expectationWithDescription:@"collections_mixed_notifications"]; + [realm beginWriteTransaction]; + block(realm); + [realm commitWriteTransaction]; + [self waitForExpectationsWithTimeout:10.0 handler:nil]; + + [token invalidate]; +} + +@interface MixedDictionaryCollectionChangesetTests : RLMTestCase +@end + +@implementation MixedDictionaryCollectionChangesetTests + +- (NSDictionary *)testDictionary { + return @{ @"key2" : @"hello2", + @"key3" : @YES, + @"key4" : @123, + @"key5" : @456.789 }; +} + +- (void)prepare { + @autoreleasepool { + RLMRealm *realm = [RLMRealm defaultRealm]; + [realm transactionWithBlock:^{ + [realm deleteAllObjects]; + MixedObject *mixedObject = [MixedObject new]; + mixedObject.anyCol = [self testDictionary]; + [realm addObject:mixedObject]; + }]; + } +} + +- (RLMResults *)query { + return MixedObject.allObjects; +} + +- (void)testAddToMixedObservationWithKeypath { + [self prepare]; + __block XCTestExpectation *expectation = nil; + RLMRealm *realm = [RLMRealm defaultRealm]; + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMNotificationToken *token = [mixedObject addNotificationBlock:^(BOOL deleted, NSArray *changes, NSError *error) { + XCTAssertFalse(deleted); + XCTAssertNil(error); + XCTAssertEqual(changes.count, 1); + [expectation fulfill]; + } keyPaths:@[ @"anyCol" ]]; + + + expectation = [self expectationWithDescription:@"collections_mixed_notifications"]; + [realm beginWriteTransaction]; + mixedObject.anyCol = @{ @"key2": @987.321 }; + [realm commitWriteTransaction]; + [self waitForExpectationsWithTimeout:10.0 handler:nil]; + + [token invalidate]; +} + +- (void)testMixedDictionaryChangesInResults { + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = [self testDictionary]; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = @{ @"key": @987.321 }; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dict = (RLMDictionary *)mixedObject.anyCol; + dict[@"key"] = @NO; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dict = (RLMDictionary *)mixedObject.anyCol; + dict[@"key1"] = @"newvalue"; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dict = (RLMDictionary *)mixedObject.anyCol; + [dict removeAllObjects]; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dict = (RLMDictionary *)mixedObject.anyCol; + [dict removeObjectForKey:@"key5"]; + }); +} + +- (void)testMixedDictionaryObjectNotifications { + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = [self testDictionary]; + }); + + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + dictionary[@"key2"] = @NO; + }); + + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + dictionary[@"key4"] = [NSNull null]; + }); + + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + [dictionary removeAllObjects]; + }); +} + +- (void)testMixedDictionaryCollectionChanges { + ExpectMixedDictionaryChange(self, @[@"key2", @"key3", @"key4", @"key5"], @[@"key1", @"key3"], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = @{ @"key1": @YES, @"key3": @987.321 }; + }); + + ExpectMixedDictionaryChange(self, @[], @[], @[@"key3"], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + dictionary[@"key3"] = @NO; + }); + + ExpectMixedDictionaryChange(self, @[@"key4"], @[], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + dictionary[@"key4"] = nil; + }); + + ExpectMixedDictionaryChange(self, @[], @[@"key1"], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + dictionary[@"key1"] = @345; + }); + + ExpectMixedDictionaryChange(self, @[@"key2"], @[], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + [dictionary removeObjectForKey:@"key2"]; + }); + + ExpectMixedDictionaryChange(self, @[@"key2", @"key3", @"key4", @"key5"], @[], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)mixedObject.anyCol; + [dictionary removeAllObjects]; + }); +} + +@end + +static void ExpectMixedArrayChange(RLMTestCase *self, NSArray *deletions, NSArray *insertions, + NSArray *modifications, void (^block)(RLMRealm *realm)) { + [self prepare]; + MixedObject *mixedObject = [[self query] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + __block XCTestExpectation *expectation = nil; + RLMNotificationToken *token = [array addNotificationBlock:^(RLMArray *array, RLMCollectionChange *changes, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(array); + if (!changes) { + return; + } + + XCTAssertEqualObjects(deletions, changes.deletions); + XCTAssertEqualObjects(insertions, changes.insertions); + XCTAssertEqualObjects(modifications, changes.modifications); + [expectation fulfill]; + }]; + + RLMRealm *realm = [RLMRealm defaultRealm]; + expectation = [self expectationWithDescription:@"collections_mixed_notifications"]; + [realm beginWriteTransaction]; + block(realm); + [realm commitWriteTransaction]; + [self waitForExpectationsWithTimeout:10.0 handler:nil]; + + [token invalidate]; + } + +@interface MixedArrayCollectionChangesetTests : RLMTestCase +@end + +@implementation MixedArrayCollectionChangesetTests + +- (NSArray *)testArray { + return @[ @"hello2", @YES, @123, @456.789 ]; +} + +- (void)prepare { + @autoreleasepool { + RLMRealm *realm = [RLMRealm defaultRealm]; + [realm transactionWithBlock:^{ + [realm deleteAllObjects]; + MixedObject *mixedObject = [MixedObject new]; + mixedObject.anyCol = [self testArray]; + [realm addObject:mixedObject]; + }]; + } +} + +- (RLMResults *)query { + return MixedObject.allObjects; +} + +- (void)testAddToMixedObservationWithKeypath { + [self prepare]; + __block XCTestExpectation *expectation = nil; + RLMRealm *realm = [RLMRealm defaultRealm]; + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMNotificationToken *token = [mixedObject addNotificationBlock:^(BOOL deleted, NSArray *changes, NSError *error) { + XCTAssertFalse(deleted); + XCTAssertNil(error); + XCTAssertEqual(changes.count, 1); + [expectation fulfill]; + } keyPaths:@[ @"anyCol" ]]; + + expectation = [self expectationWithDescription:@"collections_mixed_notifications"]; + [realm beginWriteTransaction]; + mixedObject.anyCol = @[ @987.321 ]; + [realm commitWriteTransaction]; + [self waitForExpectationsWithTimeout:10.0 handler:nil]; + + [token invalidate]; +} + +- (void)testMixedArrayChangesInResults { + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = [self testArray]; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = @[ @987.321 ]; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + array[0] = @NO; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array addObject:@"newvalue"]; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array insertObject:@765 atIndex:1]; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array removeLastObject]; + }); + + ExpectChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array removeAllObjects]; + }); +} + +- (void)testMixedArrayObjectNotifications { + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = [self testArray]; + }); + + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array addObject:@NO]; + }); + + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + array[3] = @"hey"; + }); + + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array removeObjectAtIndex:2]; + }); + + ExpectObjectChange(self, ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array removeAllObjects]; + }); +} + + +- (void)testMixedArrayCollectionChanges { + ExpectMixedArrayChange(self, @[@0, @1, @2, @3], @[@0, @1], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + mixedObject.anyCol = @[ @YES, @987.321 ]; + }); + + ExpectMixedArrayChange(self, @[], @[@4], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array addObject: @NO]; + }); + + ExpectMixedArrayChange(self, @[], @[@1], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array insertObject:@"hey" atIndex:1]; + }); + + ExpectMixedArrayChange(self, @[], @[], @[@0], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + array[0] = [RLMObjectId objectId]; + }); + + ExpectMixedArrayChange(self, @[@3], @[], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array removeLastObject]; + }); + + ExpectMixedArrayChange(self, @[@0, @1, @2, @3], @[], @[], ^(RLMRealm *realm) { + MixedObject *mixedObject = [[MixedObject allObjectsInRealm:realm] firstObject]; + RLMArray *array = (RLMArray *)mixedObject.anyCol; + [array removeAllObjects]; + }); +} + +@end + diff --git a/Realm/Tests/PrimitiveRLMValuePropertyTests.m b/Realm/Tests/PrimitiveRLMValuePropertyTests.m index ac12055245..2df167db92 100644 --- a/Realm/Tests/PrimitiveRLMValuePropertyTests.m +++ b/Realm/Tests/PrimitiveRLMValuePropertyTests.m @@ -142,20 +142,20 @@ - (void)initValues { } - (void)testType { - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeDate); } - (void)testInitNull { @@ -225,26 +225,26 @@ - (void)testUpdateBoolType { XCTAssert([(NSNumber *)managed.decimalVal isEqual:@NO]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:@NO]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:@NO]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeBool); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeBool); } - (void)testUpdateIntType { @@ -288,26 +288,26 @@ - (void)testUpdateIntType { XCTAssert([(NSNumber *)managed.decimalVal isEqual:@2]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:@2]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:@2]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeInt); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeInt); } - (void)testUpdateFloatType { @@ -351,26 +351,26 @@ - (void)testUpdateFloatType { XCTAssert([(NSNumber *)managed.decimalVal isEqual:@2.2f]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:@2.2f]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:@2.2f]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeFloat); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeFloat); } - (void)testUpdateDoubleType { @@ -414,26 +414,26 @@ - (void)testUpdateDoubleType { XCTAssert([(NSNumber *)managed.decimalVal isEqual:@3.3]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:@3.3]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:@3.3]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeDouble); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeDouble); } - (void)testUpdateStringType { @@ -477,26 +477,26 @@ - (void)testUpdateStringType { XCTAssert([(NSNumber *)managed.decimalVal isEqual:@"four"]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:@"four"]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:@"four"]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeString); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeString); } - (void)testUpdateDataType { @@ -540,26 +540,26 @@ - (void)testUpdateDataType { XCTAssert([(NSNumber *)managed.decimalVal isEqual:data(5)]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:data(5)]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:data(5)]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeData); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeData); } - (void)testUpdateDateType { @@ -603,26 +603,26 @@ - (void)testUpdateDateType { XCTAssert([(NSNumber *)managed.decimalVal isEqual:date(6)]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:date(6)]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:date(6)]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeDate); } - (void)testUpdateDecimal { @@ -666,26 +666,26 @@ - (void)testUpdateDecimal { XCTAssert([(NSNumber *)managed.decimalVal isEqual:decimal128(7)]); XCTAssert([(NSNumber *)managed.objectIdVal isEqual:decimal128(7)]); XCTAssert([(NSNumber *)managed.uuidVal isEqual:decimal128(7)]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeDecimal128); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeDecimal128); } - (void)testUpdateObjectIdType { @@ -729,26 +729,26 @@ - (void)testUpdateObjectIdType { XCTAssert([(NSUUID *)managed.decimalVal isEqual:objectId(8)]); XCTAssert([(NSUUID *)managed.objectIdVal isEqual:objectId(8)]); XCTAssert([(NSUUID *)managed.uuidVal isEqual:objectId(8)]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeObjectId); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeObjectId); } - (void)testUpdateUuidType { @@ -792,26 +792,26 @@ - (void)testUpdateUuidType { XCTAssert([(NSUUID *)managed.decimalVal isEqual:uuid(@"137DECC8-B300-4954-A233-F89909F4FD89")]); XCTAssert([(NSUUID *)managed.objectIdVal isEqual:uuid(@"137DECC8-B300-4954-A233-F89909F4FD89")]); XCTAssert([(NSUUID *)managed.uuidVal isEqual:uuid(@"137DECC8-B300-4954-A233-F89909F4FD89")]); - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.decimalVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.objectIdVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(unmanaged.uuidVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.decimalVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.objectIdVal.rlm_valueType, RLMPropertyTypeUUID); - XCTAssertEqual(managed.uuidVal.rlm_valueType, RLMPropertyTypeUUID); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.decimalVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.objectIdVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(unmanaged.uuidVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.decimalVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.objectIdVal.rlm_anyValueType, RLMAnyValueTypeUUID); + XCTAssertEqual(managed.uuidVal.rlm_anyValueType, RLMAnyValueTypeUUID); } @end diff --git a/Realm/Tests/PrimitiveRLMValuePropertyTests.tpl.m b/Realm/Tests/PrimitiveRLMValuePropertyTests.tpl.m index e08034b5f9..a74336036a 100644 --- a/Realm/Tests/PrimitiveRLMValuePropertyTests.tpl.m +++ b/Realm/Tests/PrimitiveRLMValuePropertyTests.tpl.m @@ -87,20 +87,20 @@ - (void)initValues { } - (void)testType { - XCTAssertEqual(unmanaged.boolVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(unmanaged.intVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(unmanaged.floatVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(unmanaged.doubleVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(unmanaged.stringVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(unmanaged.dataVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(unmanaged.dateVal.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(managed.boolVal.rlm_valueType, RLMPropertyTypeBool); - XCTAssertEqual(managed.intVal.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(managed.floatVal.rlm_valueType, RLMPropertyTypeFloat); - XCTAssertEqual(managed.doubleVal.rlm_valueType, RLMPropertyTypeDouble); - XCTAssertEqual(managed.stringVal.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(managed.dataVal.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(managed.dateVal.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual(unmanaged.boolVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(unmanaged.intVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(unmanaged.floatVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(unmanaged.doubleVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(unmanaged.stringVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(unmanaged.dataVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(unmanaged.dateVal.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(managed.boolVal.rlm_anyValueType, RLMAnyValueTypeBool); + XCTAssertEqual(managed.intVal.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(managed.floatVal.rlm_anyValueType, RLMAnyValueTypeFloat); + XCTAssertEqual(managed.doubleVal.rlm_anyValueType, RLMAnyValueTypeDouble); + XCTAssertEqual(managed.stringVal.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(managed.dataVal.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(managed.dateVal.rlm_anyValueType, RLMAnyValueTypeDate); } - (void)testInitNull { @@ -114,61 +114,61 @@ - (void)testInitNull { - (void)testUpdateBoolType { $rlmValue = @NO; XCTAssert([(NSNumber *)$rlmValue isEqual:@NO]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeBool); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeBool); } - (void)testUpdateIntType { $rlmValue = @2; XCTAssert([(NSNumber *)$rlmValue isEqual:@2]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeInt); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeInt); } - (void)testUpdateFloatType { $rlmValue = @2.2f; XCTAssert([(NSNumber *)$rlmValue isEqual:@2.2f]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeFloat); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeFloat); } - (void)testUpdateDoubleType { $rlmValue = @3.3; XCTAssert([(NSNumber *)$rlmValue isEqual:@3.3]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeDouble); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeDouble); } - (void)testUpdateStringType { $rlmValue = @"four"; XCTAssert([(NSNumber *)$rlmValue isEqual:@"four"]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeString); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeString); } - (void)testUpdateDataType { $rlmValue = data(5); XCTAssert([(NSNumber *)$rlmValue isEqual:data(5)]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeData); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeData); } - (void)testUpdateDateType { $rlmValue = date(6); XCTAssert([(NSNumber *)$rlmValue isEqual:date(6)]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeDate); } - (void)testUpdateDecimal { $rlmValue = decimal128(7); XCTAssert([(NSNumber *)$rlmValue isEqual:decimal128(7)]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeDecimal128); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeDecimal128); } - (void)testUpdateObjectIdType { $rlmValue = objectId(8); XCTAssert([(NSUUID *)$rlmValue isEqual:objectId(8)]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeObjectId); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeObjectId); } - (void)testUpdateUuidType { $rlmValue = uuid(@"137DECC8-B300-4954-A233-F89909F4FD89"); XCTAssert([(NSUUID *)$rlmValue isEqual:uuid(@"137DECC8-B300-4954-A233-F89909F4FD89")]); - XCTAssertEqual($rlmValue.rlm_valueType, RLMPropertyTypeUUID); + XCTAssertEqual($rlmValue.rlm_anyValueType, RLMAnyValueTypeUUID); } @end diff --git a/Realm/Tests/QueryTests.m b/Realm/Tests/QueryTests.m index 12996db4a6..7ca67ba930 100644 --- a/Realm/Tests/QueryTests.m +++ b/Realm/Tests/QueryTests.m @@ -3805,11 +3805,23 @@ - (void)testDictionaryQueryKeySubscriptWithObjectCol { - (void)testDictionarySubscriptThrowsException { RLMRealm *realm = [self realm]; RLMAssertThrowsWithReason(([realm objects:@"ArrayPropertyObject" where:@"array['invalid'] = NULL"]), - @"Invalid keypath 'array[\"invalid\"]': only dictionaries support subscript predicates."); + @"Invalid keypath 'array[\"invalid\"]': only dictionaries and realm `Any` support subscript predicates."); RLMAssertThrowsWithReason(([realm objects:@"SetPropertyObject" where:@"set['invalid'] = NULL"]), - @"Invalid keypath 'set[\"invalid\"]': only dictionaries support subscript predicates."); + @"Invalid keypath 'set[\"invalid\"]': only dictionaries and realm `Any` support subscript predicates."); RLMAssertThrowsWithReason(([realm objects:@"OwnerObject" where:@"dog['dogName'] = NULL"]), @"Aggregate operations can only be used on key paths that include an collection property"); + RLMAssertThrows(([realm objects:@"DictionaryPropertyObject" where:@"stringDictionary[%@] = NULL", [RLMObjectId objectId]]), + @"Invalid subscript type 'anyCol[[a-z0-9]+]': Only `Strings` or index are allowed subscripts"); + RLMAssertThrowsWithReason(([realm objects:@"DictionaryPropertyObject" where:@"stringDictionary['aKey']['bKey'] = NULL"]), + @"Invalid subscript size 'stringDictionary[\"aKey\"][\"bKey\"]': nested dictionaries queries are only allowed in mixed properties."); + RLMAssertThrowsWithReason(([realm objects:@"DictionaryPropertyObject" where:@"stringDictionary[0] = NULL"]), + @"Invalid subscript type 'stringDictionary[0]'; only string keys are allowed as subscripts in dictionary queries."); +} + +- (void)testMixedSubscriptsThrowsException { + RLMRealm *realm = [self realm]; + RLMAssertThrows(([realm objects:@"AllTypesObject" where:@"anyCol[%@] = NULL", [RLMObjectId objectId]]), + @"Invalid subscript type 'anyCol[[a-z0-9]+]': Only `Strings` or index are allowed subscripts"); } - (void)testCollectionsQueryAllValuesAllKeys { diff --git a/Realm/Tests/RLMValueTests.m b/Realm/Tests/RLMValueTests.m index 797dd8e0ba..d32dd7eb9e 100644 --- a/Realm/Tests/RLMValueTests.m +++ b/Realm/Tests/RLMValueTests.m @@ -28,42 +28,52 @@ @implementation RLMValueTests - (void)testIntType { id v = @123; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeInt); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeInt); } - (void)testFloatType { id v = @123.456f; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeFloat); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeFloat); } - (void)testStringType { id v = @"hello"; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeString); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeString); } - (void)testDataType { id v = [NSData dataWithBytes:"hey" length:3]; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeData); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeData); } - (void)testDateType { id v = [NSDate date]; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeDate); } - (void)testObjectType { id v = [[StringObject alloc] init]; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeObject); } - (void)testObjectIdType { id v = [RLMObjectId objectId]; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeObjectId); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeObjectId); } - (void)testDecimal128Type { id v = [RLMDecimal128 decimalWithNumber:@123.456]; - XCTAssertEqual(v.rlm_valueType, RLMPropertyTypeDecimal128); + XCTAssertEqual(v.rlm_anyValueType, RLMAnyValueTypeDecimal128); +} + +- (void)testDictionaryType { + NSDictionary *dictionary = @{ @"key1" : @"hello" }; + XCTAssertEqual(dictionary.rlm_anyValueType, RLMAnyValueTypeDictionary); +} + +- (void)testArrayType { + NSArray *array = @[ @"hello", @123456 ]; + XCTAssertEqual(array.rlm_anyValueType, RLMAnyValueTypeList); } #pragma mark - Comparison @@ -73,8 +83,8 @@ - (void)testNumberEquals { id v2 = @123; XCTAssertEqualObjects(v1, v2); - XCTAssertEqual(v1.rlm_valueType, RLMPropertyTypeInt); - XCTAssertEqual(v2.rlm_valueType, RLMPropertyTypeInt); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeInt); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeInt); XCTAssertNotEqual(v2, @456); } @@ -83,8 +93,8 @@ - (void)testStringEquals { id v2 = @"hello"; XCTAssertEqual(v1, v2); - XCTAssertEqual(v1.rlm_valueType, RLMPropertyTypeString); - XCTAssertEqual(v2.rlm_valueType, RLMPropertyTypeString); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeString); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeString); XCTAssertNotEqual(v2, @"there"); } @@ -93,8 +103,8 @@ - (void)testDataEquals { id v1 = [d copy]; id v2 = [d copy]; XCTAssertEqual(v1, v2); - XCTAssertEqual(v1.rlm_valueType, RLMPropertyTypeData); - XCTAssertEqual(v2.rlm_valueType, RLMPropertyTypeData); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeData); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeData); XCTAssertNotEqual(v1, [NSData dataWithBytes:"there" length:5]); } @@ -103,8 +113,8 @@ - (void)testDateEquals { id v1 = [d copy]; id v2 = [d copy]; XCTAssertEqual(v1, v2); - XCTAssertEqual(v1.rlm_valueType, RLMPropertyTypeDate); - XCTAssertEqual(v2.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeDate); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeDate); XCTAssertNotEqual(v1, [NSDate dateWithTimeIntervalSince1970:0]); } @@ -114,8 +124,8 @@ - (void)testObjectEquals { id v2 = so; XCTAssertEqual(v1, so); XCTAssertEqual(v2, so); - XCTAssertEqual(v1.rlm_valueType, RLMPropertyTypeObject); - XCTAssertEqual(v2.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeObject); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeObject); XCTAssertEqual(v1, v2); XCTAssertNotEqual(v1, [[StringObject alloc] init]); } @@ -126,8 +136,8 @@ - (void)testObjectIdEquals { id v2 = oid; XCTAssertEqual(v1, oid); XCTAssertEqual(v2, oid); - XCTAssertEqual(v1.rlm_valueType, RLMPropertyTypeObjectId); - XCTAssertEqual(v2.rlm_valueType, RLMPropertyTypeObjectId); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeObjectId); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeObjectId); XCTAssertEqual(v1, v2); XCTAssertNotEqual(v1, [RLMObjectId objectId]); } @@ -138,12 +148,50 @@ - (void)testDecimal128Equals { id v2 = d; XCTAssertEqual(v1, d); XCTAssertEqual(v2, d); - XCTAssertEqual(v1.rlm_valueType, RLMPropertyTypeDecimal128); - XCTAssertEqual(v2.rlm_valueType, RLMPropertyTypeDecimal128); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeDecimal128); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeDecimal128); XCTAssertEqual(v1, v2); XCTAssertNotEqual(v1, [RLMDecimal128 decimalWithNumber:@456.123]); } +- (void)testDictionaryAnyEquals { + NSDictionary *dictionary = @{ @"key2" : @"hello2", + @"key3" : @YES, + @"key4" : @123, + @"key5" : @456.789, + @"key6" : [NSData dataWithBytes:"hey" length:3], + @"key7" : [NSDate date], + @"key8" : [[MixedObject alloc] init], + @"key9" : [RLMObjectId objectId], + @"key10" : [RLMDecimal128 decimalWithNumber:@123.456] }; + id v1 = dictionary; + id v2 = dictionary; + XCTAssertEqual(v1, dictionary); + XCTAssertEqual(v2, dictionary); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeDictionary); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeDictionary); + XCTAssertEqual(v1, v2); +} + +- (void)testArrayAnyEquals { + NSArray *array = @[ @"hello2", + @YES, + @123, + @456.789, + [NSData dataWithBytes:"hey" length:3], + [NSDate date], + [[MixedObject alloc] init], + [RLMObjectId objectId], + [RLMDecimal128 decimalWithNumber:@123.456] ]; + id v1 = array; + id v2 = array; + XCTAssertEqual(v1, array); + XCTAssertEqual(v2, array); + XCTAssertEqual(v1.rlm_anyValueType, RLMAnyValueTypeList); + XCTAssertEqual(v2.rlm_anyValueType, RLMAnyValueTypeList); + XCTAssertEqual(v1, v2); +} + #pragma mark - Managed Values - (void)testCreateManagedObjectManagedChild { @@ -162,19 +210,71 @@ - (void)testCreateManagedObjectManagedChild { XCTAssertNotNil(mo0.anyCol); XCTAssertTrue([((StringObject *)mo0.anyCol).stringCol isEqualToString:so.stringCol]); - XCTAssertEqual(mo0.anyCol.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(mo0.anyCol.rlm_anyValueType, RLMAnyValueTypeObject); XCTAssertTrue([((StringObject *)mo0.anyArray.firstObject).stringCol isEqualToString:so.stringCol]); XCTAssertNotNil(mo1.anyCol); XCTAssertTrue([((StringObject *)mo1.anyCol).stringCol isEqualToString:so.stringCol]); - XCTAssertEqual(mo1.anyCol.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(mo1.anyCol.rlm_anyValueType, RLMAnyValueTypeObject); XCTAssertTrue([((StringObject *)mo1.anyArray.firstObject).stringCol isEqualToString:so.stringCol]); + XCTAssertEqual([StringObject allObjectsInRealm:r].count, 1U); } -- (void)testCreateManagedObjectUnmanagedChild { +- (void)testCreateManagedObjectInAnyDictionary { StringObject *so = [[StringObject alloc] init]; so.stringCol = @"hello"; + + RLMRealm *r = [self realmWithTestPath]; + [r beginWriteTransaction]; + [r addObject:so]; + + NSDictionary *dictionary = @{ @"key1" : so }; + MixedObject *mo0 = [MixedObject createInRealm:r withValue:@[dictionary, @[dictionary]]]; + + MixedObject *mo1 = [[MixedObject alloc] init]; + mo1.anyCol = dictionary; + [mo1.anyArray addObject:dictionary]; + [r commitWriteTransaction]; + + XCTAssertNotNil(mo0.anyCol); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo0.anyCol)[@"key1"]).stringCol isEqualToString:so.stringCol]); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo0.anyArray.firstObject)[@"key1"]).stringCol isEqualToString:so.stringCol]); + + XCTAssertNotNil(mo1.anyCol); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo1.anyCol)[@"key1"]).stringCol isEqualToString:so.stringCol]); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo1.anyArray[0])[@"key1"]).stringCol isEqualToString:so.stringCol]); +} +- (void)testCreateManagedObjectInAnyArray { + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + + RLMRealm *r = [self realmWithTestPath]; + [r beginWriteTransaction]; + [r addObject:so]; + + NSArray *array = @[so]; + MixedObject *mo0 = [MixedObject createInRealm:r withValue:@[array, @[array]]]; + + MixedObject *mo1 = [[MixedObject alloc] init]; + mo1.anyCol = array; + [mo1.anyArray addObject:array]; + [r commitWriteTransaction]; + + XCTAssertNotNil(mo0.anyCol); + XCTAssertTrue([((StringObject *)((NSArray *)mo0.anyCol)[0]).stringCol isEqualToString:so.stringCol]); + XCTAssertTrue([((StringObject *)((NSArray *)mo0.anyArray.firstObject)[0]).stringCol isEqualToString:so.stringCol]); + + XCTAssertNotNil(mo1.anyCol); + XCTAssertTrue([((StringObject *)((NSArray *)mo1.anyCol)[0]).stringCol isEqualToString:so.stringCol]); + XCTAssertTrue([((StringObject *)((NSArray *)mo1.anyArray[0])[0]).stringCol isEqualToString:so.stringCol]); +} + + +- (void)testCreateManagedObjectUnmanagedChild { + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + StringObject *so1 = [[StringObject alloc] init]; so1.stringCol = @"hello2"; @@ -193,15 +293,77 @@ - (void)testCreateManagedObjectUnmanagedChild { XCTAssertNotNil(mo0.anyCol); XCTAssertTrue([((StringObject *)mo0.anyCol).stringCol isEqualToString:so.stringCol]); - XCTAssertEqual(mo0.anyCol.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(mo0.anyCol.rlm_anyValueType, RLMAnyValueTypeObject); XCTAssertTrue([((StringObject *)mo0.anyArray[0]).stringCol isEqualToString:so.stringCol]); XCTAssertNotNil(mo1.anyCol); XCTAssertTrue([((StringObject *)mo1.anyCol).stringCol isEqualToString:so1.stringCol]); - XCTAssertEqual(mo1.anyCol.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(mo1.anyCol.rlm_anyValueType, RLMAnyValueTypeObject); XCTAssertTrue([((StringObject *)mo1.anyArray[0]).stringCol isEqualToString:so.stringCol]); } +- (void)testCreateManagedObjectUnmanagedChildInAnyDictionary { + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + + StringObject *so1 = [[StringObject alloc] init]; + so1.stringCol = @"hello2"; + + RLMRealm *r = [self realmWithTestPath]; + [r beginWriteTransaction]; + + NSDictionary *dictionary = @{ @"key1" : so }; + MixedObject *mo0 = [MixedObject createInRealm:r withValue:@[dictionary, @[dictionary]]]; + + MixedObject *mo1 = [[MixedObject alloc] init]; + [mo1.anyArray addObject:dictionary]; + [r addObject:mo1]; + [r commitWriteTransaction]; + + XCTAssertThrows(mo1.anyCol = so1); + [r beginWriteTransaction]; + mo1.anyCol = @{ @"key2" : so1 }; + + XCTAssertNotNil(mo0.anyCol); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo0.anyCol)[@"key1"]).stringCol isEqualToString:so.stringCol]); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo0.anyArray.firstObject)[@"key1"]).stringCol isEqualToString:so.stringCol]); + + XCTAssertNotNil(mo1.anyCol); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo1.anyCol)[@"key2"]).stringCol isEqualToString:so1.stringCol]); + XCTAssertTrue([((StringObject *)((NSDictionary *)mo1.anyArray[0])[@"key1"]).stringCol isEqualToString:so.stringCol]); +} + +- (void)testCreateManagedObjectUnmanagedChildInAnyArray { + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + + StringObject *so1 = [[StringObject alloc] init]; + so1.stringCol = @"hello2"; + + RLMRealm *r = [self realmWithTestPath]; + [r beginWriteTransaction]; + + NSArray *array = @[so]; + MixedObject *mo0 = [MixedObject createInRealm:r withValue:@[array, @[array]]]; + + MixedObject *mo1 = [[MixedObject alloc] init]; + [mo1.anyArray addObject:array]; + [r addObject:mo1]; + [r commitWriteTransaction]; + + XCTAssertThrows(mo1.anyCol = so1); + [r beginWriteTransaction]; + mo1.anyCol = @[so1]; + + XCTAssertNotNil(mo0.anyCol); + XCTAssertTrue([((StringObject *)((NSArray *)mo0.anyCol)[0]).stringCol isEqualToString:so.stringCol]); + XCTAssertTrue([((StringObject *)((NSArray *)mo0.anyArray.firstObject)[0]).stringCol isEqualToString:so.stringCol]); + + XCTAssertNotNil(mo1.anyCol); + XCTAssertTrue([((StringObject *)((NSArray *)mo1.anyCol)[0]).stringCol isEqualToString:so1.stringCol]); + XCTAssertTrue([((StringObject *)((NSArray *)mo1.anyArray[0])[0]).stringCol isEqualToString:so.stringCol]); +} + // difference between adding object and not! - (void)testCreateManagedObject { RLMRealm *r = [self realmWithTestPath]; @@ -210,7 +372,7 @@ - (void)testCreateManagedObject { MixedObject *mo = [MixedObject createInRealm:r withValue:@[so, @[]]]; [r commitWriteTransaction]; XCTAssertTrue([((StringObject *)mo.anyCol).stringCol isEqualToString:so.stringCol]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeObject); } - (void)testCreateManagedInt { @@ -221,7 +383,7 @@ - (void)testCreateManagedInt { XCTAssertTrue([(NSNumber *)mo.anyCol isEqualToNumber:@123456789]); XCTAssertTrue([mo.anyArray[0] isEqualToNumber:@123456]); XCTAssertTrue([mo.anyArray[1] isEqualToNumber:@67890]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeInt); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeInt); } - (void)testCreateManagedFloat { @@ -232,7 +394,7 @@ - (void)testCreateManagedFloat { XCTAssertTrue([(NSNumber *)mo.anyCol isEqualToNumber:@1234.5f]); XCTAssertTrue([mo.anyArray[0] isEqualToNumber:[NSNumber numberWithFloat:12345.6f]]); XCTAssertTrue([mo.anyArray[1] isEqualToNumber:[NSNumber numberWithFloat:678.9f]]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeFloat); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeFloat); } - (void)testCreateManagedDouble { @@ -243,7 +405,7 @@ - (void)testCreateManagedDouble { XCTAssertTrue([(NSNumber *)mo.anyCol isEqualToNumber:@1234.5]); XCTAssertTrue([mo.anyArray[0] isEqualToNumber:[NSNumber numberWithDouble:12345.6]]); XCTAssertTrue([mo.anyArray[1] isEqualToNumber:[NSNumber numberWithDouble:678.9]]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeDouble); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeDouble); } - (void)testCreateManagedString { @@ -254,7 +416,7 @@ - (void)testCreateManagedString { XCTAssertTrue([(NSString *)mo.anyCol isEqualToString:@"hello"]); XCTAssertTrue([mo.anyArray[0] isEqualToString:@"over"]); XCTAssertTrue([mo.anyArray[1] isEqualToString:@"there"]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeString); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeString); } - (void)testCreateManagedData { @@ -272,7 +434,7 @@ - (void)testCreateManagedData { encoding:NSUTF8StringEncoding] isEqualToString:@"hey"]); XCTAssertTrue([[[NSString alloc] initWithData:mo.anyArray[1] encoding:NSUTF8StringEncoding] isEqualToString:@"you"]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeData); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeData); } - (void)testCreateManagedDate { @@ -288,7 +450,7 @@ - (void)testCreateManagedDate { XCTAssertEqualWithAccuracy(d1.timeIntervalSince1970, ((NSDate *)mo.anyCol).timeIntervalSince1970, 1); XCTAssertEqualWithAccuracy(d1.timeIntervalSince1970, ((NSDate *)mo.anyArray[0]).timeIntervalSince1970, 1); XCTAssertEqualWithAccuracy(d2.timeIntervalSince1970, ((NSDate *)mo.anyArray[1]).timeIntervalSince1970, 1); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeDate); } - (void)testCreateManagedObjectId { @@ -303,7 +465,7 @@ - (void)testCreateManagedObjectId { XCTAssertTrue([(RLMObjectId *)mo.anyCol isEqual:oid1]); XCTAssertTrue([(RLMObjectId *)mo.anyArray[0] isEqual:oid1]); XCTAssertTrue([(RLMObjectId *)mo.anyArray[1] isEqual:oid2]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeObjectId); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeObjectId); } - (void)testCreateManagedDecimal128 { @@ -318,7 +480,103 @@ - (void)testCreateManagedDecimal128 { XCTAssertTrue([(RLMDecimal128 *)mo.anyCol isEqual:d1]); XCTAssertTrue([(RLMDecimal128 *)mo.anyArray[0] isEqual:d1]); XCTAssertTrue([(RLMDecimal128 *)mo.anyArray[1] isEqual:d2]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeDecimal128); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeDecimal128); +} + +- (void)testCreateManagedDictionary { + RLMRealm *r = [self realmWithTestPath]; + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + RLMObjectId *oid = [RLMObjectId objectId]; + NSDate *d = [NSDate date]; + NSDictionary *d1 = @{ @"key2" : @"hello2", + @"key3" : @YES, + @"key4" : @123, + @"key5" : @456.789, + @"key6" : [NSData dataWithBytes:"hey" length:3], + @"key7" : d, + @"key8" : so, + @"key9" : oid, + @"key10" : [RLMDecimal128 decimalWithNumber:@123.456] }; + NSDictionary *d2 = @{ @"key1" : @"hello" }; + [r beginWriteTransaction]; + [MixedObject createInRealm:r withValue:@[d1, @[d1, d2]]]; + [r commitWriteTransaction]; + + XCTAssertEqual([MixedObject allObjectsInRealm:r].count, 1U); + MixedObject *result = [[MixedObject allObjectsInRealm:r] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)result.anyCol; + XCTAssertTrue([dictionary[@"key2"] isEqual:@"hello2"]); + XCTAssertTrue([dictionary[@"key3"] isEqual:@YES]); + XCTAssertTrue([dictionary[@"key4"] isEqual:@123]); + XCTAssertTrue([dictionary[@"key5"] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)dictionary[@"key6"] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)dictionary[@"key7"]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)dictionary[@"key8"]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)dictionary[@"key9"]) isEqual:oid]); + XCTAssertTrue([dictionary[@"key10"] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMDictionary *dictionary1 = (RLMDictionary *)result.anyArray.firstObject; + XCTAssertTrue([dictionary1[@"key2"] isEqual:@"hello2"]); + XCTAssertTrue([dictionary1[@"key3"] isEqual:@YES]); + XCTAssertTrue([dictionary1[@"key4"] isEqual:@123]); + XCTAssertTrue([dictionary1[@"key5"] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)dictionary1[@"key6"] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)dictionary1[@"key7"]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)dictionary1[@"key8"]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)dictionary1[@"key9"]) isEqual:oid]); + XCTAssertTrue([dictionary1[@"key10"] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMDictionary *dictionary2 = (RLMDictionary *)result.anyArray.lastObject; + XCTAssertTrue([dictionary2[@"key1"] isEqual:@"hello"]); +} + +- (void)testCreateManagedArray { + RLMRealm *r = [self realmWithTestPath]; + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + RLMObjectId *oid = [RLMObjectId objectId]; + NSDate *d = [NSDate date]; + NSArray *d1 = @[ @"hello2", + @YES, + @123, + @456.789, + [NSData dataWithBytes:"hey" length:3], + d, + so, + oid, + [RLMDecimal128 decimalWithNumber:@123.456] ]; + NSArray *d2 = @[@"hello"]; + [r beginWriteTransaction]; + [MixedObject createInRealm:r withValue:@[d1, @[d1, d2]]]; + [r commitWriteTransaction]; + + XCTAssertEqual([MixedObject allObjectsInRealm:r].count, 1U); + MixedObject *result = [[MixedObject allObjectsInRealm:r] firstObject]; + RLMArray *array = ((RLMArray *)result.anyCol); + XCTAssertTrue([array[0] isEqual:@"hello2"]); + XCTAssertTrue([array[1] isEqual:@YES]); + XCTAssertTrue([array[2] isEqual:@123]); + XCTAssertTrue([array[3] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)array[4] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)array[5]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)array[6]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)array[7]) isEqual:oid]); + XCTAssertTrue([array[8] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMArray *array1 = (RLMArray *)result.anyArray.firstObject; + XCTAssertTrue([array1[0] isEqual:@"hello2"]); + XCTAssertTrue([array1[1] isEqual:@YES]); + XCTAssertTrue([array1[2] isEqual:@123]); + XCTAssertTrue([array1[3] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)array1[4] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)array1[5]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)array1[6]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)array1[7]) isEqual:oid]); + XCTAssertTrue([array1[8] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMArray *array2 = (RLMArray *)result.anyArray.lastObject; + XCTAssertTrue([array2[0] isEqual:@"hello"]); } #pragma mark - Add Managed Values @@ -338,7 +596,7 @@ - (void)testAddManagedObject { XCTAssertNotNil(mo1.anyCol); XCTAssertTrue([((StringObject *)mo1.anyCol).stringCol isEqualToString:so.stringCol]); - XCTAssertEqual(mo1.anyCol.rlm_valueType, RLMPropertyTypeObject); + XCTAssertEqual(mo1.anyCol.rlm_anyValueType, RLMAnyValueTypeObject); } - (void)testAddManagedInt { @@ -354,7 +612,7 @@ - (void)testAddManagedInt { XCTAssertTrue([(NSNumber *)mo.anyCol isEqualToNumber:@123456789]); XCTAssertTrue([mo.anyArray[0] isEqualToNumber:@123456]); XCTAssertTrue([mo.anyArray[1] isEqualToNumber:@67890]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeInt); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeInt); } - (void)testAddManagedFloat { @@ -369,7 +627,7 @@ - (void)testAddManagedFloat { XCTAssertTrue([(NSNumber *)mo.anyCol isEqualToNumber:@1234.5f]); XCTAssertTrue([mo.anyArray[0] isEqualToNumber:[NSNumber numberWithFloat:12345.6f]]); XCTAssertTrue([mo.anyArray[1] isEqualToNumber:[NSNumber numberWithFloat:678.9f]]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeFloat); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeFloat); } - (void)testAddManagedString { @@ -384,7 +642,7 @@ - (void)testAddManagedString { XCTAssertTrue([(NSString *)mo.anyCol isEqualToString:@"hello"]); XCTAssertTrue([mo.anyArray[0] isEqualToString:@"over"]); XCTAssertTrue([mo.anyArray[1] isEqualToString:@"there"]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeString); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeString); } - (void)testAddManagedData { @@ -405,7 +663,7 @@ - (void)testAddManagedData { encoding:NSUTF8StringEncoding] isEqualToString:@"hey"]); XCTAssertTrue([[[NSString alloc] initWithData:mo.anyArray[1] encoding:NSUTF8StringEncoding] isEqualToString:@"you"]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeData); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeData); } - (void)testAddManagedDate { @@ -424,7 +682,7 @@ - (void)testAddManagedDate { XCTAssertEqualWithAccuracy(d1.timeIntervalSince1970, ((NSDate *)mo.anyCol).timeIntervalSince1970, 1.0); XCTAssertEqualWithAccuracy(d1.timeIntervalSince1970, ((NSDate *)mo.anyArray[0]).timeIntervalSince1970, 1.0); XCTAssertEqualWithAccuracy(d2.timeIntervalSince1970, ((NSDate *)mo.anyArray[1]).timeIntervalSince1970, 1.0); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeDate); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeDate); } - (void)testAddManagedObjectId { @@ -442,7 +700,7 @@ - (void)testAddManagedObjectId { XCTAssertTrue([(RLMObjectId *)mo.anyCol isEqual:oid1]); XCTAssertTrue([(RLMObjectId *)mo.anyArray[0] isEqual:oid1]); XCTAssertTrue([(RLMObjectId *)mo.anyArray[1] isEqual:oid2]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeObjectId); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeObjectId); } - (void)testAddManagedDecimal128 { @@ -460,7 +718,193 @@ - (void)testAddManagedDecimal128 { XCTAssertTrue([(RLMDecimal128 *)mo.anyCol isEqual:d1]); XCTAssertTrue([(RLMDecimal128 *)mo.anyArray[0] isEqual:d1]); XCTAssertTrue([(RLMDecimal128 *)mo.anyArray[1] isEqual:d2]); - XCTAssertEqual(mo.anyCol.rlm_valueType, RLMPropertyTypeDecimal128); + XCTAssertEqual(mo.anyCol.rlm_anyValueType, RLMAnyValueTypeDecimal128); +} + +- (void)testAddManagedDictionary { + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + RLMObjectId *oid = [RLMObjectId objectId]; + NSDate *d = [NSDate date]; + NSDictionary *d1 = @{ @"key2" : @"hello2", + @"key3" : @YES, + @"key4" : @123, + @"key5" : @456.789, + @"key6" : [NSData dataWithBytes:"hey" length:3], + @"key7" : d, + @"key8" : so, + @"key9" : oid, + @"key10" : [RLMDecimal128 decimalWithNumber:@123.456] }; + NSDictionary *d2 = @{ @"key1" : @"hello" }; + MixedObject *mo = [[MixedObject alloc] init]; + mo.anyCol = d1; + [mo.anyArray addObjects:@[d1, d2]]; + + RLMRealm *r = [self realmWithTestPath]; + [r beginWriteTransaction]; + [r addObject:mo]; + [r commitWriteTransaction]; + + XCTAssertEqual([MixedObject allObjectsInRealm:r].count, 1U); + MixedObject *result = [[MixedObject allObjectsInRealm:r] firstObject]; + RLMDictionary *dictionary = (RLMDictionary *)result.anyCol; + XCTAssertTrue([dictionary[@"key2"] isEqual:@"hello2"]); + XCTAssertTrue([dictionary[@"key3"] isEqual:@YES]); + XCTAssertTrue([dictionary[@"key4"] isEqual:@123]); + XCTAssertTrue([dictionary[@"key5"] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)dictionary[@"key6"] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)dictionary[@"key7"]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)dictionary[@"key8"]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)dictionary[@"key9"]) isEqual:oid]); + XCTAssertTrue([dictionary[@"key10"] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMDictionary *dictionary1 = (RLMDictionary *)result.anyArray.firstObject; + XCTAssertTrue([dictionary1[@"key2"] isEqual:@"hello2"]); + XCTAssertTrue([dictionary1[@"key3"] isEqual:@YES]); + XCTAssertTrue([dictionary1[@"key4"] isEqual:@123]); + XCTAssertTrue([dictionary1[@"key5"] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)dictionary1[@"key6"] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)dictionary1[@"key7"]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)dictionary1[@"key8"]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)dictionary1[@"key9"]) isEqual:oid]); + XCTAssertTrue([dictionary1[@"key10"] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMDictionary *dictionary2 = (RLMDictionary *)result.anyArray.lastObject; + XCTAssertTrue([dictionary2[@"key1"] isEqual:@"hello"]); +} + +- (void)testAddManagedArray { + RLMRealm *r = [self realmWithTestPath]; + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + RLMObjectId *oid = [RLMObjectId objectId]; + NSDate *d = [NSDate date]; + NSArray *d1 = @[ @"hello2", + @YES, + @123, + @456.789, + [NSData dataWithBytes:"hey" length:3], + d, + so, + oid, + [RLMDecimal128 decimalWithNumber:@123.456] ]; + NSArray *d2 = @[@"hello"]; + MixedObject *mo = [[MixedObject alloc] init]; + mo.anyCol = d1; + [mo.anyArray addObjects:@[d1, d2]]; + + [r beginWriteTransaction]; + [r addObject:mo]; + [r commitWriteTransaction]; + + XCTAssertEqual([MixedObject allObjectsInRealm:r].count, 1U); + MixedObject *result = [[MixedObject allObjectsInRealm:r] firstObject]; + RLMArray *array = ((RLMArray *)result.anyCol); + XCTAssertTrue([array[0] isEqual:@"hello2"]); + XCTAssertTrue([array[1] isEqual:@YES]); + XCTAssertTrue([array[2] isEqual:@123]); + XCTAssertTrue([array[3] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)array[4] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)array[5]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)array[6]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)array[7]) isEqual:oid]); + XCTAssertTrue([array[8] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMArray *array1 = (RLMArray *)result.anyArray.firstObject; + XCTAssertTrue([array1[0] isEqual:@"hello2"]); + XCTAssertTrue([array1[1] isEqual:@YES]); + XCTAssertTrue([array1[2] isEqual:@123]); + XCTAssertTrue([array1[3] isEqual:@456.789]); + XCTAssertTrue([[[NSString alloc] initWithData:(NSData *)array1[4] encoding:NSUTF8StringEncoding] isEqual:@"hey"]); + XCTAssertEqualWithAccuracy(((NSDate *)array1[5]).timeIntervalSince1970, d.timeIntervalSince1970, 1.0); + XCTAssertTrue([((StringObject *)array1[6]).stringCol isEqual:@"hello"]); + XCTAssertTrue([((RLMObjectId *)array1[7]) isEqual:oid]); + XCTAssertTrue([array1[8] isEqual:[RLMDecimal128 decimalWithNumber:@123.456]]); + + RLMArray *array2 = (RLMArray *)result.anyArray.lastObject; + XCTAssertTrue([array2[0] isEqual:@"hello"]); +} + +- (void)testDeleteAndUpdateValuesInManagedDictionary { + RLMRealm *r = [self realmWithTestPath]; + StringObject *so = [[StringObject alloc] init]; + so.stringCol = @"hello"; + NSDictionary *d1 = @{ @"key2" : @"hello2", + @"key3" : @YES}; + NSDictionary *d2 = @{ @"key1" : @"hello" }; + MixedObject *mo = [[MixedObject alloc] init]; + mo.anyCol = d1; + [mo.anyArray addObjects:@[d1, d2]]; + + [r beginWriteTransaction]; + [r addObject:mo]; + [r commitWriteTransaction]; + + RLMDictionary *dictionary = (RLMDictionary *)mo.anyCol; + XCTAssertTrue([dictionary[@"key2"] isEqual:@"hello2"]); + XCTAssertTrue([dictionary[@"key3"] isEqual:@YES]); + + RLMDictionary *dictionary2 = (RLMDictionary *)mo.anyArray.lastObject; + XCTAssertTrue([dictionary2[@"key1"] isEqual:@"hello"]); + + [r beginWriteTransaction]; + dictionary[@"key2"] = @1234; + dictionary2[@"key1"] = @123.456; + [r commitWriteTransaction]; + + XCTAssertTrue([dictionary[@"key2"] isEqual:@1234]); + XCTAssertTrue([dictionary2[@"key1"] isEqual:@123.456]); + + [r beginWriteTransaction]; + [dictionary removeObjectForKey:@"key3"]; + [dictionary2 removeObjectForKey:@"key1"]; + [r commitWriteTransaction]; + + XCTAssertNil(dictionary[@"key3"]); + XCTAssertNil(dictionary2[@"key1"]); +} + +- (void)testDeleteAndUpdateValuesInArray { + NSArray *d1 = @[ @"hello2", + @YES]; + NSArray *d2 = @[@"hello"]; + MixedObject *mo = [[MixedObject alloc] init]; + mo.anyCol = d1; + [mo.anyArray addObjects:@[d1, d2]]; + + RLMRealm *r = [self realmWithTestPath]; + [r beginWriteTransaction]; + [r addObject:mo]; + [r commitWriteTransaction]; + + XCTAssertEqual([MixedObject allObjectsInRealm:r].count, 1U); + MixedObject *result = [[MixedObject allObjectsInRealm:r] firstObject]; + RLMArray *array = (RLMArray *)result.anyCol; + XCTAssertTrue([array[0] isEqual:@"hello2"]); + XCTAssertTrue([array[1] isEqual:@YES]); + XCTAssertEqual(array.count, 2U); + + RLMArray *array2 = (RLMArray *)mo.anyArray.lastObject; + XCTAssertTrue([array2[0] isEqual:@"hello"]); + + [r beginWriteTransaction]; + array[0] = @1234; + array2[0] = @123.456; + [r commitWriteTransaction]; + + XCTAssertTrue([array[0] isEqual:@1234]); + XCTAssertTrue([array2[0] isEqual:@123.456]); + XCTAssertEqual(array.count, 2U); + XCTAssertEqual(array2.count, 1U); + + [r beginWriteTransaction]; + [array removeObjectAtIndex:0]; + [array2 removeObjectAtIndex:0]; + [r commitWriteTransaction]; + + XCTAssertFalse([array[0] isEqual:@1234]); + XCTAssertEqual(array.count, 1U); + XCTAssertEqual(array2.count, 0U); } #pragma mark - Dynamic Object Accessor @@ -520,5 +964,4 @@ - (void)testDynamicSchema { [self waitForExpectations:@[ex] timeout:1.0]; (void)[[MixedObject allObjectsInRealm:realm2].firstObject anyCol]; } - @end diff --git a/Realm/Tests/SectionedResultsTests.m b/Realm/Tests/SectionedResultsTests.m index 654320b732..bf0afc4907 100644 --- a/Realm/Tests/SectionedResultsTests.m +++ b/Realm/Tests/SectionedResultsTests.m @@ -168,24 +168,24 @@ - (NSDictionary *)keyPathsAndValues { } - (id)sectionKeyForValue:(id)value { - switch (value.rlm_valueType) { - case RLMPropertyTypeInt: + switch (value.rlm_anyValueType) { + case RLMAnyValueTypeInt: return [NSNumber numberWithInt:(((NSNumber *)value).intValue % 2)]; - case RLMPropertyTypeBool: + case RLMAnyValueTypeBool: return value; - case RLMPropertyTypeFloat: + case RLMAnyValueTypeFloat: return [(NSNumber *)value isEqualToNumber:@(1.1f * 1)] ? @(1.1f) : @(2.2f); - case RLMPropertyTypeDouble: + case RLMAnyValueTypeDouble: return [(NSNumber *)value isEqualToNumber:@(1.11 * 1)] ? @(1.11) : @(2.2); - case RLMPropertyTypeString: + case RLMAnyValueTypeString: return [(NSString *)value substringToIndex:1]; - case RLMPropertyTypeDate: { + case RLMAnyValueTypeDate: { NSCalendar *calendar = [NSCalendar currentCalendar]; [calendar setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; NSDateComponents *comp = [calendar components:NSCalendarUnitWeekday fromDate:(NSDate *)value]; return [NSNumber numberWithInteger:(NSInteger)comp.weekday]; } - case RLMPropertyTypeDecimal128: + case RLMAnyValueTypeDecimal128: switch ((int)((RLMDecimal128 *)value).doubleValue) { case 1: return @"one"; @@ -196,7 +196,7 @@ - (NSDictionary *)keyPathsAndValues { default: XCTFail(); } - case RLMPropertyTypeUUID: + case RLMAnyValueTypeUUID: if ([(NSUUID *)value isEqual:[[NSUUID alloc] initWithUUIDString:@"00000000-0000-0000-0000-000000000000"]]) { return @"a"; } else if ([(NSUUID *)value isEqual:[[NSUUID alloc] initWithUUIDString:@"137DECC8-B300-4954-A233-F89909F4FD89"]]) { @@ -204,7 +204,7 @@ - (NSDictionary *)keyPathsAndValues { } else if ([(NSUUID *)value isEqual:[[NSUUID alloc] initWithUUIDString:@"b84e8912-a7c2-41cd-8385-86d200d7b31e"]]) { return @"c"; } - case RLMPropertyTypeAny: + case RLMAnyValueTypeAny: return [NSNumber numberWithInt:(((NSNumber *)value).intValue % 2)];; default: XCTFail(); diff --git a/Realm/Tests/Swift/SwiftDynamicTests.swift b/Realm/Tests/Swift/SwiftDynamicTests.swift index b005a3f490..b547b95cdf 100644 --- a/Realm/Tests/Swift/SwiftDynamicTests.swift +++ b/Realm/Tests/Swift/SwiftDynamicTests.swift @@ -156,4 +156,28 @@ class SwiftRLMDynamicTests: RLMTestCase { XCTAssertTrue((robj2["objectCol"] as! RLMObject)["stringCol"] as! String == "string") XCTAssertTrue((robj2["mixedObjectCol"] as! RLMObject)["anyCol"] as! String == "string") } + + func testDynamicTypesMixedCollection_objc() { + let obj1 = AllTypesObject.values(5, + stringObject: StringObject(value: ["newString"]), + mixedObject: MixedObject(value: [["string", 45, false]]))! + + autoreleasepool { + // open realm in autoreleasepool to create tables and then dispose + let realm = self.realmWithTestPath() + realm.beginWriteTransaction() + _ = AllTypesObject.create(in: realm, withValue: obj1) + try! realm.commitWriteTransaction() + } + + let dyrealm = realm(withTestPathAndSchema: nil) + let results = dyrealm.allObjects(AllTypesObject.className()) + XCTAssertEqual(results.count, UInt(1)) + let robj1 = results[0] + + // Mixed List + XCTAssertTrue(((robj1["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedArray)[0] as! String == "string") + XCTAssertTrue(((robj1["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedArray)[1] as! Int == 45) + XCTAssertTrue(((robj1["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedArray)[2] as! Bool == false) + } } diff --git a/RealmSwift/AnyRealmValue.swift b/RealmSwift/AnyRealmValue.swift index 3ca489585e..afba8420e6 100644 --- a/RealmSwift/AnyRealmValue.swift +++ b/RealmSwift/AnyRealmValue.swift @@ -21,6 +21,8 @@ import Foundation import Realm /// A enum for storing and retrieving values associated with an `AnyRealmValue` property. +/// `AnyRealmValue` can also store a collection (List, Dictionary) of `AnyRealmValue`, meaning that you can have +/// nested collections inside a `AnyRealmValue`. public enum AnyRealmValue: Hashable { /// Represents `nil` case none @@ -46,6 +48,10 @@ public enum AnyRealmValue: Hashable { case decimal128(Decimal128) /// A UUID type. case uuid(UUID) + /// Dictionary type. + case dictionary(Map) + /// List type. + case list(List) /// Returns an `Int` if that is what the stored value is, otherwise `nil`. public var intValue: Int? { @@ -139,6 +145,22 @@ public enum AnyRealmValue: Hashable { return o as? T } + /// Returns a `Map` if that is what the stored value is, otherwise `nil`. + public var dictionaryValue: Map? { + guard case let .dictionary(d) = self else { + return nil + } + return d + } + + /// Returns a `List` if that is what the stored value is, otherwise `nil`. + public var listValue: List? { + guard case let .list(l) = self else { + return nil + } + return l + } + /// Returns a `DynamicObject` if the stored value is an `Object`, otherwise `nil`. /// /// Note: This allows access to an object stored in `AnyRealmValue` where you may not have @@ -156,4 +178,59 @@ public enum AnyRealmValue: Hashable { public init() { self = .none } + + /// Returns a `AnyRealmValue` storing a `Map`. + /// + /// - Parameter dictionary: A Swift's dictionary of `AnyRealmValue` values. + /// - Returns: Returns an `AnyRealmValue` storing a `Map`. + public static func fromDictionary(_ dictionary: Dictionary) -> AnyRealmValue { + let map = Map() + map.merge(dictionary, uniquingKeysWith: { $1 }) + return AnyRealmValue.dictionary(map) + } + + /// Returns a `AnyRealmValue` storing a `List`. + /// + /// - Parameter array: A Swift's array of `AnyRealmValue`. + /// - Returns: Returns a `AnyRealmValue` storing a `List`. + public static func fromArray(_ array: Array) -> AnyRealmValue { + let list = List() + list.append(objectsIn: array) + return AnyRealmValue.list(list) + } + + // MARK: - Hashable + + public func hash(into hasher: inout Hasher) { + switch self { + case let .int(i): + hasher.combine(i) + case let .bool(b): + hasher.combine(b) + case let .float(f): + hasher.combine(f) + case let .double(d): + hasher.combine(d) + case let .string(s): + hasher.combine(s) + case let .data(d): + hasher.combine(d) + case let .date(d): + hasher.combine(d) + case let .objectId(o): + hasher.combine(o) + case let .decimal128(d): + hasher.combine(d) + case let .uuid(u): + hasher.combine(u) + case let .object(o): + hasher.combine(o) + case .dictionary: + hasher.combine(12) + case .list: + hasher.combine(13) + case .none: + hasher.combine(14) + } + } } diff --git a/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift b/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift index 94fdad29a6..a7d3808ce4 100644 --- a/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift +++ b/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift @@ -34,8 +34,8 @@ public extension ObjectiveCSupport { return b as NSNumber case let .float(f): return f as NSNumber - case let .double(f): - return f as NSNumber + case let .double(d): + return d as NSNumber case let .string(s): return s as NSString case let .data(d): @@ -50,6 +50,10 @@ public extension ObjectiveCSupport { return u as NSUUID case let .object(o): return o + case let .dictionary(d): + return d.rlmDictionary + case let .list(l): + return l.rlmArray default: return nil } @@ -64,62 +68,73 @@ public extension ObjectiveCSupport { return .none } - switch value.rlm_valueType { - case RLMPropertyType.int: + switch value.rlm_anyValueType { + case RLMAnyValueType.int: guard let val = value as? NSNumber else { return .none } return .int(val.intValue) - case RLMPropertyType.bool: + case RLMAnyValueType.bool: guard let val = value as? NSNumber else { return .none } return .bool(val.boolValue) - case RLMPropertyType.float: + case RLMAnyValueType.float: guard let val = value as? NSNumber else { return .none } return .float(val.floatValue) - case RLMPropertyType.double: + case RLMAnyValueType.double: guard let val = value as? NSNumber else { return .none } return .double(val.doubleValue) - case RLMPropertyType.string: + case RLMAnyValueType.string: guard let val = value as? String else { return .none } return .string(val) - case RLMPropertyType.data: + case RLMAnyValueType.data: guard let val = value as? Data else { return .none } return .data(val) - case RLMPropertyType.date: + case RLMAnyValueType.date: guard let val = value as? Date else { return .none } return .date(val) - case RLMPropertyType.objectId: + case RLMAnyValueType.objectId: guard let val = value as? ObjectId else { return .none } return .objectId(val) - case RLMPropertyType.decimal128: + case RLMAnyValueType.decimal128: guard let val = value as? Decimal128 else { return .none } return .decimal128(val) - case RLMPropertyType.UUID: + case RLMAnyValueType.UUID: guard let val = value as? UUID else { return .none } return .uuid(val) - case RLMPropertyType.object: + case RLMAnyValueType.object: guard let val = value as? Object else { return .none } return .object(val) + case RLMAnyValueType.dictionary: + guard let val = value as? RLMDictionary else { + return .none + } + let d = Map(objc: val) + return AnyRealmValue.dictionary(d) + case RLMAnyValueType.list: + guard let val = value as? RLMArray else { + return .none + } + return AnyRealmValue.list(List(collection: val)) default: return .none } diff --git a/RealmSwift/Query.swift b/RealmSwift/Query.swift index 95b7429196..77219d6025 100644 --- a/RealmSwift/Query.swift +++ b/RealmSwift/Query.swift @@ -171,6 +171,17 @@ public struct Query { throwRealmException("Cannot apply a keypath to \(buildPredicate(node))") } + private func anySubscript(appending key: CollectionSubscript) -> QueryNode { + if case .keyPath = node { + return .mapAnySubscripts(keyPathErasingAnyPrefix(), keys: [key]) + } else if case let .mapAnySubscripts(kp, keys) = node { + var tmpKeys = keys + tmpKeys.append(key) + return .mapAnySubscripts(kp, keys: tmpKeys) + } + throwRealmException("Cannot add subscript to \(buildPredicate(node))") + } + // MARK: Comparable /// :nodoc: @@ -289,6 +300,24 @@ extension Query where T == Bool { } } +// MARK: Mixed + +extension Query where T == AnyRealmValue { + /// :nodoc: + public subscript(position: Int) -> Query { + .init(anySubscript(appending: .index(position))) + + } + /// :nodoc: + public subscript(key: String) -> Query { + .init(anySubscript(appending: .key(key))) + } + /// Query all indexes or keys in a mixed nested collecttion. + public var any: Query { + .init(anySubscript(appending: .all)) + } +} + // MARK: OptionalProtocol extension Query where T: OptionalProtocol { @@ -894,9 +923,16 @@ private indirect enum QueryNode { case subqueryCount(_ child: QueryNode) case mapSubscript(_ keyPath: QueryNode, key: Any) + case mapAnySubscripts(_ keyPath: QueryNode, keys: [CollectionSubscript]) case geoWithin(_ keyPath: QueryNode, _ value: QueryNode) } +private enum CollectionSubscript { + case index(Int) + case key(String) + case all +} + private func buildPredicate(_ root: QueryNode, subqueryCount: Int = 0) -> (String, [Any]) { let formatStr = NSMutableString() let arguments = NSMutableArray() @@ -971,6 +1007,7 @@ private func buildPredicate(_ root: QueryNode, subqueryCount: Int = 0) -> (Strin if options.contains(.requiresAny) { formatStr.append("ANY ") } + formatStr.append(kp.joined(separator: ".")) case .not(let child): if case .keyPath = child, @@ -1001,6 +1038,21 @@ private func buildPredicate(_ root: QueryNode, subqueryCount: Int = 0) -> (Strin build(keyPath) formatStr.append("[%@]") arguments.add(key) + case .mapAnySubscripts(let keyPath, let keys): + build(keyPath) + for key in keys { + switch key { + case .index(let index): + formatStr.append("[%@]") + arguments.add(index) + case .key(let key): + formatStr.append("[%@]") + arguments.add(key) + case .all: + formatStr.append("[%K]") + arguments.add("#any") + } + } case .geoWithin(let keyPath, let value): buildExpression(keyPath, QueryNode.Operator.in.rawValue, value, prefix: nil) } @@ -1045,6 +1097,8 @@ private struct SubqueryRewriter { return node case .mapSubscript: throwRealmException("Subqueries do not support map subscripts.") + case .mapAnySubscripts: + throwRealmException("Subqueries do not support AnyRealmValue subscripts.") case .geoWithin(let keyPath, let value): return .geoWithin(keyPath, value) } diff --git a/RealmSwift/RealmCollection.swift b/RealmSwift/RealmCollection.swift index e9e1140adf..aec497a8b4 100644 --- a/RealmSwift/RealmCollection.swift +++ b/RealmSwift/RealmCollection.swift @@ -61,8 +61,9 @@ public protocol _RealmMapValue { let next = generatorBase.next() if let next = next as? Element.Key { let key: Element.Key = next - let val: Element.Value = dynamicBridgeCast(fromObjectiveC: collection[key as AnyObject]!) - return SingleMapEntry(key: key, value: val) as? Element + if let val = collection[key as AnyObject].map(Element.Value._rlmFromObjc(_:)), let val { + return SingleMapEntry(key: key, value: val) as? Element + } } return nil } @@ -85,7 +86,7 @@ public protocol _RealmMapValue { public mutating func next() -> Element? { let next = generatorBase.next() if let key = next as? Key, - let value = collection[key as AnyObject].map(dynamicBridgeCast) as? Value { + let value = collection[key as AnyObject].map(Value._rlmFromObjc(_:)), let value { return (key: key, value: value) } return nil diff --git a/RealmSwift/Tests/AnyRealmValueTests.swift b/RealmSwift/Tests/AnyRealmValueTests.swift index b7e09e4dbd..e66d91507f 100644 --- a/RealmSwift/Tests/AnyRealmValueTests.swift +++ b/RealmSwift/Tests/AnyRealmValueTests.swift @@ -641,6 +641,10 @@ class AnyRealmValueMutableSetTests: AnyRea XCTAssertEqual(kvc as! String, s) case let .uuid(u): XCTAssertEqual(kvc as! UUID, u) + case let .dictionary(d): + XCTAssertEqual(kvc as! Map, d) + case let .list(l): + XCTAssertEqual(kvc as! List, l) } assertThrows(mutableSet.value(forKey: "not self"), named: "NSUnknownKeyException") @@ -937,6 +941,10 @@ class AnyRealmValueMapTests: AnyRealmValue XCTAssertEqual(kvc as! String, s) case let .uuid(u): XCTAssertEqual(kvc as! UUID, u) + case let .dictionary(d): + XCTAssertEqual(kvc as! Map, d) + case let .list(l): + XCTAssertEqual(kvc as! List, l) } } diff --git a/RealmSwift/Tests/KVOTests.swift b/RealmSwift/Tests/KVOTests.swift index 3664d20b67..20194d37ea 100644 --- a/RealmSwift/Tests/KVOTests.swift +++ b/RealmSwift/Tests/KVOTests.swift @@ -53,6 +53,7 @@ class SwiftKVOObject: Object { let otherFloatCol = RealmProperty() let otherDoubleCol = RealmProperty() let otherBoolCol = RealmProperty() + let otherAnyCol = RealmProperty() @objc dynamic var optStringCol: String? @objc dynamic var optBinaryCol: Data? @objc dynamic var optDateCol: Date? @@ -71,6 +72,7 @@ class SwiftKVOObject: Object { let arrayDate = List() let arrayDecimal = List() let arrayObjectId = List() + let arrayAny = List() let arrayOptBool = List() let arrayOptInt8 = List() @@ -97,6 +99,7 @@ class SwiftKVOObject: Object { let setDate = MutableSet() let setDecimal = MutableSet() let setObjectId = MutableSet() + let setAny = MutableSet() let setOptBool = MutableSet() let setOptInt8 = MutableSet() @@ -123,6 +126,7 @@ class SwiftKVOObject: Object { let mapDate = Map() let mapDecimal = Map() let mapObjectId = Map() + let mapAny = Map() let mapOptBool = Map() let mapOptInt8 = Map() @@ -456,6 +460,21 @@ class KVOTests: TestCase { } } + func testCollectionInMixedKVO() { + let (obj, obs) = getObject(SwiftKVOObject()) + + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value = AnyRealmValue.fromDictionary([ + "key1": .int(1234)]) } + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value.dictionaryValue?["key1"] = .string("hello") } + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value.dictionaryValue?["key1"] = nil } + + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value = AnyRealmValue.fromArray([.bool(true)]) } + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value.listValue?[0] = .float(123.456) } + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value.listValue?.append(.bool(true)) } + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value.listValue?.insert(.date(Date()), at: 1) } + observeSetChange(obs, "otherAnyCol") { obj.otherAnyCol.value.listValue?.remove(at: 0) } + } + func testTypedObservation() { let (obj, obs) = getObject(SwiftKVOObject()) diff --git a/RealmSwift/Tests/MixedCollectionTest.swift b/RealmSwift/Tests/MixedCollectionTest.swift new file mode 100644 index 0000000000..4c62c6ce23 --- /dev/null +++ b/RealmSwift/Tests/MixedCollectionTest.swift @@ -0,0 +1,989 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +import XCTest +import RealmSwift + +class MixedCollectionTest: TestCase { + func testAnyMixedDictionary() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + let d = Date() + let oid = ObjectId.generate() + let uuid = UUID() + + func assertObject(_ o: AnyRealmTypeObject) { + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key1"], .string("hello")) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key2"], .bool(false)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key3"], .int(234)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key4"], .float(789.123)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key5"], .double(12345.678901)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key6"], .data(Data("a".utf8))) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key7"], .date(d)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key9"], .objectId(oid)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key10"], .uuid(uuid)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key11"], .decimal128(Decimal128(number: 567))) + + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key1"]?.stringValue, "hello") + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key2"]?.boolValue, false) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key3"]?.intValue, 234) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key4"]?.floatValue, 789.123) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key5"]?.doubleValue, 12345.678901) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key6"]?.dataValue, Data("a".utf8)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key7"]?.dateValue, d) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key8"]?.object(SwiftStringObject.self)?.stringCol, so.stringCol) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key9"]?.objectIdValue, oid) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key10"]?.uuidValue, uuid) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key11"]?.decimal128Value, Decimal128(number: 567)) + } + + let dictionary: Dictionary = [ + "key1": .string("hello"), + "key2": .bool(false), + "key3": .int(234), + "key4": .float(789.123), + "key5": .double(12345.678901), + "key6": .data(Data("a".utf8)), + "key7": .date(d), + "key8": .object(so), + "key9": .objectId(oid), + "key10": .uuid(uuid), + "key11": .decimal128(Decimal128(number: 567)) + ] + + let dictionary1: Dictionary = [ + "key": .none + ] + + let o = AnyRealmTypeObject() + // Unmanaged Set + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + assertObject(o) + // Unmanaged update + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary1) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key"], AnyRealmValue.none) + + // Add mixed collection to object + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key"], AnyRealmValue.none) + + // Update managed object + try realm.write { + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary1) + } + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key"], AnyRealmValue.none) + + // Create managed object + try realm.write { + let object = realm.create(AnyRealmTypeObject.self, value: [ "anyValue": dictionary ]) + assertObject(object) + } + + // Results + let result = realm.objects(AnyRealmTypeObject.self).last + XCTAssertNotNil(result) + assertObject(result!) + } + + func testAnyMixedDictionaryUpdateAndDelete() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + + let dictionary: Dictionary = [ + "key1": .string("hello"), + "key2": .bool(false), + "key3": .int(234), + ] + + let o = AnyRealmTypeObject() + // Unmanaged Set + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key1"], .string("hello")) + + // Unmanaged Update + o.anyValue.value.dictionaryValue?["key1"] = .object(so) + o.anyValue.value.dictionaryValue?["key2"] = .data(Data("a".utf8)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key1"]?.object(SwiftStringObject.self)?.stringCol, so.stringCol) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key2"], .data(Data("a".utf8))) + + // Add mixed collection to object + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key1"]?.object(SwiftStringObject.self)?.stringCol, so.stringCol) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key2"], .data(Data("a".utf8))) + + // Update managed object + try realm.write { + o.anyValue.value.dictionaryValue?["key1"] = .double(12345.678901) + o.anyValue.value.dictionaryValue?["key2"] = .float(789.123) + } + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key1"], .double(12345.678901)) + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key2"], .float(789.123)) + + let result = realm.objects(AnyRealmTypeObject.self).last + XCTAssertNotNil(result) + // Delete + try realm.write { + result?.anyValue.value.dictionaryValue?["key1"] = nil + result?.anyValue.value.dictionaryValue?["key2"] = nil + } + XCTAssertNil(result?.anyValue.value.dictionaryValue?["key1"]) + XCTAssertNil(result?.anyValue.value.dictionaryValue?["key2"]) + } + + func testAnyMixedNestedDictionary() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + + func assertDictionary1(_ o: AnyRealmTypeObject) { + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.dictionaryValue?["key2"]?.dictionaryValue?["key3"]?.object(SwiftStringObject.self)?.stringCol, "hello") + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key4"]?.boolValue, false) + } + + func assertDictionary2(_ o: AnyRealmTypeObject) { + XCTAssertEqual(o.anyValue.value.dictionaryValue?["key10"]?.dictionaryValue?["key11"]?.dictionaryValue?["key12"]?.dictionaryValue?["key1"]?.dictionaryValue?["key2"]?.dictionaryValue?["key3"]?.object(SwiftStringObject.self)?.stringCol, "hello") + } + + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary([ "key3": .object(so) ]) + let subDict3: AnyRealmValue = AnyRealmValue.fromDictionary([ "key2": subDict2 ]) + let subDict4: AnyRealmValue = AnyRealmValue.fromDictionary([ "key1": subDict3 ]) + let dictionary1: Dictionary = [ + "key0": subDict4, + "key4": .bool(false) + ] + + let subDict5: AnyRealmValue = AnyRealmValue.fromDictionary([ "key12": subDict4 ]) + let subDict6: AnyRealmValue = AnyRealmValue.fromDictionary([ "key11": subDict5 ]) + let dictionary2: Dictionary = [ + "key10": subDict6, + ] + + let o = AnyRealmTypeObject() + // Unmanaged Set + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary2) + // Unamanged Read + assertDictionary2(o) + + // Unmanaged update + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary1) + // Update assert + assertDictionary1(o) + + // Add mixed collection to object + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + // Add assert + assertDictionary1(o) + + // Update managed object + try realm.write { + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary2) + } + // Update assert + assertDictionary2(o) + + try realm.write { + let d = [ "key0": [ "key1": [ "key2": [ "key3": AnyRealmValue.object(so)]]], + "key4": AnyRealmValue.bool(false)] + let object = realm.create(AnyRealmTypeObject.self, value: [ "anyValue": d]) + assertDictionary1(object) + } + + // Results + let result = realm.objects(AnyRealmTypeObject.self).last + + // Results assert + XCTAssertNotNil(result) + assertDictionary1(result!) + } + + func testAnyMixedList() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + let d = Date() + let oid = ObjectId.generate() + let uuid = UUID() + + func assertObject(_ o: AnyRealmTypeObject) { + XCTAssertEqual(o.anyValue.value.listValue?[0], .string("hello")) + XCTAssertEqual(o.anyValue.value.listValue?[1], .bool(false)) + XCTAssertEqual(o.anyValue.value.listValue?[2], .int(234)) + XCTAssertEqual(o.anyValue.value.listValue?[3], .float(789.123)) + XCTAssertEqual(o.anyValue.value.listValue?[4], .double(12345.678901)) + XCTAssertEqual(o.anyValue.value.listValue?[5], .data(Data("a".utf8))) + XCTAssertEqual(o.anyValue.value.listValue?[6], .date(d)) + XCTAssertEqual(o.anyValue.value.listValue?[8], .objectId(oid)) + XCTAssertEqual(o.anyValue.value.listValue?[9], .uuid(uuid)) + XCTAssertEqual(o.anyValue.value.listValue?[10], .decimal128(Decimal128(number: 567))) + + XCTAssertEqual(o.anyValue.value.listValue?[0].stringValue, "hello") + XCTAssertEqual(o.anyValue.value.listValue?[1].boolValue, false) + XCTAssertEqual(o.anyValue.value.listValue?[2].intValue, 234) + XCTAssertEqual(o.anyValue.value.listValue?[3].floatValue, 789.123) + XCTAssertEqual(o.anyValue.value.listValue?[4].doubleValue, 12345.678901) + XCTAssertEqual(o.anyValue.value.listValue?[5].dataValue, Data("a".utf8)) + XCTAssertEqual(o.anyValue.value.listValue?[6].dateValue, d) + XCTAssertEqual(o.anyValue.value.listValue?[7].object(SwiftStringObject.self)?.stringCol, so.stringCol) + XCTAssertEqual(o.anyValue.value.listValue?[8].objectIdValue, oid) + XCTAssertEqual(o.anyValue.value.listValue?[9].uuidValue, uuid) + XCTAssertEqual(o.anyValue.value.listValue?[10].decimal128Value, Decimal128(number: 567)) + } + + let list: Array = [ + .string("hello"), + .bool(false), + .int(234), + .float(789.123), + .double(12345.678901), + .data(Data("a".utf8)), + .date(d), + .object(so), + .objectId(oid), + .uuid(uuid), + .decimal128(Decimal128(number: 567)) + ] + + let list1: Array = [ + .none + ] + + let o = AnyRealmTypeObject() + // Unmanaged Set + let l = AnyRealmValue.fromArray(list) + o.anyValue.value = l + assertObject(o) + // Unmanaged update + o.anyValue.value = AnyRealmValue.fromArray(list1) + XCTAssertEqual(o.anyValue.value.listValue?[0], AnyRealmValue.none) + + // Add mixed collection to object + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + XCTAssertEqual(o.anyValue.value.listValue?[0], AnyRealmValue.none) + + // Update managed object + try realm.write { + o.anyValue.value = AnyRealmValue.fromArray(list1) + } + XCTAssertEqual(o.anyValue.value.listValue?[0], AnyRealmValue.none) + + try realm.write { + let object = realm.create(AnyRealmTypeObject.self, value: [ "anyValue": list ]) + assertObject(object) + } + + // Results + let result = realm.objects(AnyRealmTypeObject.self).last + XCTAssertNotNil(result) + assertObject(result!) + } + + func testAnyMixedListUpdateAndDelete() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + + let list: Array = [ + .string("hello"), + .bool(false), + .int(234), + ] + + let o = AnyRealmTypeObject() + // Unmanaged Set + o.anyValue.value = AnyRealmValue.fromArray(list) + XCTAssertEqual(o.anyValue.value.listValue?[0], .string("hello")) + + // Unmanaged Update + o.anyValue.value.listValue?[0] = .object(so) + o.anyValue.value.listValue?[1] = .data(Data("a".utf8)) + XCTAssertEqual(o.anyValue.value.listValue?[0].object(SwiftStringObject.self)?.stringCol, so.stringCol) + XCTAssertEqual(o.anyValue.value.listValue?[1], .data(Data("a".utf8))) + + // Add mixed collection to object + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + XCTAssertEqual(o.anyValue.value.listValue?[0].object(SwiftStringObject.self)?.stringCol, so.stringCol) + XCTAssertEqual(o.anyValue.value.listValue?[1], .data(Data("a".utf8))) + + // Update managed object + try realm.write { + o.anyValue.value.listValue?[0] = .double(12345.678901) + o.anyValue.value.listValue?[1] = .float(789.123) + } + XCTAssertEqual(o.anyValue.value.listValue?[0], .double(12345.678901)) + XCTAssertEqual(o.anyValue.value.listValue?[1], .float(789.123)) + + let result = realm.objects(AnyRealmTypeObject.self).last + XCTAssertNotNil(result) + XCTAssertEqual(result?.anyValue.value.listValue?.count, 3) + + // Delete + try realm.write { + result?.anyValue.value.listValue?.remove(at: 0) + result?.anyValue.value.listValue?.remove(at: 0) + } + XCTAssertNotEqual(result?.anyValue.value.listValue?[0], .double(12345.678901)) + XCTAssertEqual(result?.anyValue.value.listValue?[0], .int(234)) + XCTAssertEqual(result?.anyValue.value.listValue?.count, 1) + } + + func testAnyMixedNestedArray() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + + func assertArray1(_ o: AnyRealmTypeObject) { + XCTAssertEqual(o.anyValue.value.listValue?[0].listValue?[0].listValue?[0].listValue?[0].object(SwiftStringObject.self)?.stringCol, "hello") + XCTAssertEqual(o.anyValue.value.listValue?[1].boolValue, false) + } + + func assertArray2(_ o: AnyRealmTypeObject) { + XCTAssertEqual(o.anyValue.value.listValue?[0].listValue?[0].listValue?[0].listValue?[0].listValue?[0].listValue?[0].object(SwiftStringObject.self)?.stringCol, "hello") + } + + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .object(so) ]) + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ subArray2 ]) + let subArray4: AnyRealmValue = AnyRealmValue.fromArray([ subArray3 ]) + let array1: Array = [ + subArray4, .bool(false) + ] + + let subArray5: AnyRealmValue = AnyRealmValue.fromArray([ subArray4 ]) + let subArray6: AnyRealmValue = AnyRealmValue.fromArray([ subArray5 ]) + let array2: Array = [ + subArray6 + ] + + let o = AnyRealmTypeObject() + // Unmanaged Set + o.anyValue.value = AnyRealmValue.fromArray(array2) + // Unamanged Read + assertArray2(o) + + // Unmanaged update + o.anyValue.value = AnyRealmValue.fromArray(array1) + // Update assert + assertArray1(o) + + // Add mixed collection to object + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + // Add assert + assertArray1(o) + + // Update managed object + try realm.write { + o.anyValue.value = AnyRealmValue.fromArray(array2) + } + // Update assert + assertArray2(o) + + try realm.write { + let d = [[[[ AnyRealmValue.object(so)]]], AnyRealmValue.bool(false)] + let object = realm.create(AnyRealmTypeObject.self, value: [ "anyValue": d]) + assertArray1(object) + } + + // Results + let result = realm.objects(AnyRealmTypeObject.self).last + XCTAssertNotNil(result) + assertArray1(result!) + } + + func testMixedNestedCollection() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .object(so) ]) + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary([ "key1": subArray2 ]) + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ subArray2, subDict2]) + let subDict3: AnyRealmValue = AnyRealmValue.fromDictionary([ "key2": subArray3 ]) + let subArray4: AnyRealmValue = AnyRealmValue.fromArray([ subDict3 ]) + let subDict4: AnyRealmValue = AnyRealmValue.fromDictionary([ "key3": subArray4 ]) + let subArray5: AnyRealmValue = AnyRealmValue.fromArray([ subDict4 ]) + let subDict5: AnyRealmValue = AnyRealmValue.fromDictionary([ "key4": subArray5 ]) + let subArray6: AnyRealmValue = AnyRealmValue.fromArray([ subDict5 ]) + let subDict6: AnyRealmValue = AnyRealmValue.fromDictionary([ "key5": subArray6 ]) + let dictionary: Dictionary = [ + "key0": subDict6, + ] + + func assertMixed(_ o: AnyRealmTypeObject) { + let baseNested: List? = o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key5"]?.listValue?[0].dictionaryValue?["key4"]?.listValue?[0].dictionaryValue?["key3"]?.listValue + let nested1: String? = baseNested?[0].dictionaryValue?["key2"]?.listValue?[0].listValue?[0].object(SwiftStringObject.self)?.stringCol + XCTAssertEqual(nested1, "hello") + let nested2: String? = baseNested?[0].dictionaryValue?["key2"]?.listValue?[1].dictionaryValue?["key1"]?.listValue?[0].object(SwiftStringObject.self)?.stringCol + XCTAssertEqual(nested2, "hello") + } + + let o = AnyRealmTypeObject() + // Unmanaged Set + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + // Unamanged Read + assertMixed(o) + + // Unmanaged update + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + // Update assert + assertMixed(o) + + // Add mixed collection to object + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + // Add assert + assertMixed(o) + + // Update managed object + try realm.write { + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + } + // Update assert + assertMixed(o) + + try realm.write { + let d = ["key0": ["key5": [["key4": [["key3": [["key2": [[AnyRealmValue.object(so)], ["key1": [AnyRealmValue.object(so)]]]]]]]]]]] + let object = realm.create(AnyRealmTypeObject.self, value: [ "anyValue": d]) + assertMixed(object) + } + + // Results + let result = realm.objects(AnyRealmTypeObject.self).last + + // Results assert + XCTAssertNotNil(result) + assertMixed(result!) + } + + func testMixedCollectionEquality() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .object(so) ]) + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary([ "key1": subArray2 ]) + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ subArray2, subDict2]) + let subDict3: AnyRealmValue = AnyRealmValue.fromDictionary([ "key2": subArray3 ]) + let subArray4: AnyRealmValue = AnyRealmValue.fromArray([ subDict3 ]) + let subDict4: AnyRealmValue = AnyRealmValue.fromDictionary([ "key3": subArray4 ]) + let subArray5: AnyRealmValue = AnyRealmValue.fromArray([ subDict4 ]) + let subDict5: AnyRealmValue = AnyRealmValue.fromDictionary([ "key4": subArray5 ]) + let subArray6: AnyRealmValue = AnyRealmValue.fromArray([ subDict5 ]) + let subDict6: AnyRealmValue = AnyRealmValue.fromDictionary([ "key5": subArray6 ]) + let dictionary: Dictionary = [ + "key0": subDict6, + ] + + XCTAssertEqual(AnyRealmValue.fromDictionary([:]), AnyRealmValue.fromDictionary([:])) // Empty mixed lists should be equal + XCTAssertEqual(AnyRealmValue.fromDictionary(dictionary), AnyRealmValue.fromDictionary(dictionary)) // Unamanged mixed list should be equal + + var dictionary2 = dictionary + dictionary2["newKey"] = .bool(false) + XCTAssertNotEqual(AnyRealmValue.fromDictionary(dictionary), AnyRealmValue.fromDictionary(dictionary2)) + + let anyValue = AnyRealmValue.fromDictionary(dictionary) + let anyValue2 = AnyRealmValue.fromDictionary(dictionary) + anyValue2.dictionaryValue?["newKey"] = .bool(false) + XCTAssertNotEqual(anyValue, anyValue2) + + let mixedObject = AnyRealmTypeObject() + mixedObject.anyValue.value = anyValue + let mixedObject2 = mixedObject + + XCTAssertEqual(mixedObject.anyValue, mixedObject2.anyValue) + XCTAssertEqual(mixedObject.anyValue.value, mixedObject2.anyValue.value) + XCTAssertEqual(mixedObject.anyValue, mixedObject2.anyValue, "instances should be equal by `==` operator") + XCTAssertTrue(mixedObject.isEqual(mixedObject2), "instances should be equal by `isEqual` method") + + XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue, mixedObject.anyValue.value.dictionaryValue) + + let realm = realmWithTestPath() + try realm.write { + realm.add(mixedObject) + realm.add(mixedObject2) + } + + XCTAssertEqual(mixedObject.anyValue.value, mixedObject2.anyValue.value) + XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key0"], mixedObject2.anyValue.value.dictionaryValue?["key0"]) + XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key5"]?.listValue?[0], mixedObject2.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key5"]?.listValue?[0]) + + let mixedObject3 = AnyRealmTypeObject() + mixedObject3.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + + try realm.write { + realm.add(mixedObject3) + } + + XCTAssertNotEqual(mixedObject.anyValue.value, mixedObject3.anyValue.value) + XCTAssertNotEqual(mixedObject.anyValue.value.dictionaryValue?["key0"], mixedObject3.anyValue.value.dictionaryValue?["key0"]) + XCTAssertNotEqual(mixedObject.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key5"]?.listValue?[0], mixedObject3.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key5"]?.listValue?[0]) + } + + func testMixedCollectionModernObjectEquality() throws { + let so = SwiftStringObject() + so.stringCol = "hello" + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .object(so) ]) + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary([ "key1": subArray2 ]) + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ subArray2, subDict2]) + let subDict3: AnyRealmValue = AnyRealmValue.fromDictionary([ "key2": subArray3 ]) + let subArray4: AnyRealmValue = AnyRealmValue.fromArray([ subDict3 ]) + let subDict4: AnyRealmValue = AnyRealmValue.fromDictionary([ "key3": subArray4 ]) + let subArray5: AnyRealmValue = AnyRealmValue.fromArray([ subDict4 ]) + let subDict5: AnyRealmValue = AnyRealmValue.fromDictionary([ "key4": subArray5 ]) + let subArray6: AnyRealmValue = AnyRealmValue.fromArray([ subDict5 ]) + let subDict6: AnyRealmValue = AnyRealmValue.fromDictionary([ "key5": subArray6 ]) + let dictionary: Dictionary = [ + "key0": subDict6, + ] + + XCTAssertEqual(AnyRealmValue.fromDictionary([:]), AnyRealmValue.fromDictionary([:])) // Empty mixed lists should be equal + XCTAssertEqual(AnyRealmValue.fromDictionary(dictionary), AnyRealmValue.fromDictionary(dictionary)) // Unamanged mixed list should be equal + + var dictionary2 = dictionary + dictionary2["newKey"] = .bool(false) + XCTAssertNotEqual(AnyRealmValue.fromDictionary(dictionary), AnyRealmValue.fromDictionary(dictionary2)) + + let anyValue = AnyRealmValue.fromDictionary(dictionary) + let anyValue2 = AnyRealmValue.fromDictionary(dictionary) + anyValue2.dictionaryValue?["newKey"] = .bool(false) + XCTAssertNotEqual(anyValue, anyValue2) + + // Unmanaged equality + let mixedObject = ModernAllTypesObject() + mixedObject.anyCol = anyValue + XCTAssertEqual(mixedObject.anyCol, anyValue) + XCTAssertEqual(mixedObject.anyCol, AnyRealmValue.fromDictionary(dictionary)) + let mixedObject2 = mixedObject + XCTAssertEqual(mixedObject2.anyCol, anyValue) + XCTAssertEqual(mixedObject2.anyCol, AnyRealmValue.fromDictionary(dictionary)) + + XCTAssertEqual(mixedObject.anyCol, mixedObject2.anyCol) + XCTAssertTrue(mixedObject.anyCol == mixedObject2.anyCol, "instances should be equal by `==` operator") + XCTAssertTrue(mixedObject.isEqual(mixedObject2), "instances should be equal by `isEqual` method") + XCTAssertEqual(mixedObject.anyCol.dictionaryValue, mixedObject.anyCol.dictionaryValue) + + let realm = realmWithTestPath() + try realm.write { + realm.add(mixedObject) + realm.add(mixedObject2) + } + + XCTAssertEqual(mixedObject.anyCol, mixedObject2.anyCol) + XCTAssertTrue(mixedObject.anyCol == mixedObject2.anyCol, "instances should be equal by `==` operator") + XCTAssertTrue(mixedObject.isEqual(mixedObject2), "instances should be equal by `isEqual` method") + + let mixedObject3 = ModernAllTypesObject() + mixedObject3.anyCol = AnyRealmValue.fromDictionary(dictionary) + + try realm.write { + realm.add(mixedObject3) + } + + XCTAssertNotEqual(mixedObject.anyCol, mixedObject3.anyCol) + XCTAssertNotEqual(mixedObject.anyCol.dictionaryValue?["key0"], mixedObject3.anyCol.dictionaryValue?["key0"]) + XCTAssertNotEqual(mixedObject.anyCol.dictionaryValue?["key0"]?.dictionaryValue?["key5"]?.listValue?[0], mixedObject3.anyCol.dictionaryValue?["key0"]?.dictionaryValue?["key5"]?.listValue?[0]) + } + + func testMixedCollectionObjectNotifications() throws { + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ .int(3) ]) + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ subArray3 ]) + let subDict1: AnyRealmValue = AnyRealmValue.fromDictionary([ "key1": subArray2 ]) + let dictionary: Dictionary = [ + "key0": subDict1, + ] + + func expectChange(_ name: String) -> ((ObjectChange) -> Void) { + let exp = expectation(description: "Object changes for mixed collections") + return { change in + if case .change(_, let properties) = change { + XCTAssertEqual(properties.count, 1) + if let prop = properties.first { + XCTAssertEqual(prop.name, name) + } + } else { + XCTFail("expected .change, got \(change)") + } + exp.fulfill() + } + } + + func assertObjectNotification(_ object: Object, block: @escaping () -> Void) { + let token = object.observe(expectChange("anyValue")) + let realm = realmWithTestPath() + try! realm.write { + block() + } + + waitForExpectations(timeout: 2) + token.invalidate() + } + + let o = AnyRealmTypeObject() + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + + assertObjectNotification(o) { + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + } + + assertObjectNotification(o) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.listValue?[0].listValue?[0] = .bool(true) + } + + assertObjectNotification(o) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.listValue?.append(.float(33.33)) + } + + assertObjectNotification(o) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.listValue?.removeLast() + } + + assertObjectNotification(o) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.listValue?.removeAll() + } + + assertObjectNotification(o) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key2"] = .string("nowhere") + } + + assertObjectNotification(o) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key2"] = nil + } + + assertObjectNotification(o) { + o.anyValue.value.dictionaryValue?.removeAll() + } + } + + func testMixedCollectionDictionaryNotifications() throws { + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary([ "key5": .float(43) ]) + let subDict1: AnyRealmValue = AnyRealmValue.fromDictionary([ "key1": subDict2 ]) + let dictionary: Dictionary = [ + "key0": subDict1, + "key3": .decimal128(Decimal128(1)), + ] + + func expectChanges(_ deletions: [String], _ insertions: [String], _ modifications: [String]) -> ((RealmMapChange>) -> Void) { + let exp = expectation(description: "Dictionary changes for mixed collections") + return { change in + switch change { + case .initial: + break + case .update(_, deletions: let d, insertions: let i, modifications: let m): + XCTAssertEqual(d.count, deletions.count) + XCTAssertEqual(d, deletions) + XCTAssertEqual(i.count, insertions.count) + XCTAssertEqual(i, insertions) + XCTAssertEqual(m.count, modifications.count) + XCTAssertEqual(m, modifications) + exp.fulfill() + case .error(let error): + XCTFail("Unexpected error \(error)") + } + } + } + + func assertDictionaryNotification(_ dictionary: Map?, deletions: [String], insertions: [String], modifications: [String], block: @escaping () -> Void) { + let token = dictionary?.observe(expectChanges(deletions, insertions, modifications)) + let realm = realmWithTestPath() + try! realm.write { + block() + } + + waitForExpectations(timeout: 2) + token?.invalidate() + } + + let o = AnyRealmTypeObject() + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + + assertDictionaryNotification(o.anyValue.value.dictionaryValue, deletions: [], insertions: ["key2"], modifications: []) { + o.anyValue.value.dictionaryValue?["key2"] = AnyRealmValue.fromDictionary(dictionary) + } + + assertDictionaryNotification(o.anyValue.value.dictionaryValue, deletions: [], insertions: ["key10"], modifications: []) { + o.anyValue.value.dictionaryValue?["key10"] = AnyRealmValue.fromDictionary(dictionary) + } + + assertDictionaryNotification(o.anyValue.value.dictionaryValue?["key10"]?.dictionaryValue, deletions: [], insertions: [], modifications: ["key0"]) { + o.anyValue.value.dictionaryValue?["key10"]?.dictionaryValue?["key0"] = .string("new") + } + + assertDictionaryNotification(o.anyValue.value.dictionaryValue, deletions: ["key3"], insertions: [], modifications: []) { + o.anyValue.value.dictionaryValue?["key3"] = nil + } + + assertDictionaryNotification(o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.dictionaryValue, deletions: [], insertions: ["key6"], modifications: []) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.dictionaryValue?["key6"] = .date(Date()) + } + + assertDictionaryNotification(o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.dictionaryValue, deletions: ["key5", "key6"], insertions: [], modifications: []) { + o.anyValue.value.dictionaryValue?["key0"]?.dictionaryValue?["key1"]?.dictionaryValue?.removeAll() + } + } + + func testMixedCollectionArrayNotifications() throws { + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .float(43), .string("lunch"), .double(12.34) ]) + let subArray1: AnyRealmValue = AnyRealmValue.fromArray([ subArray2, .bool(false) ]) + let array: Array = [ + subArray1, .decimal128(Decimal128(1)), + ] + + func expectChanges(_ deletions: [Int], _ insertions: [Int], _ modifications: [Int]) -> ((RealmCollectionChange>) -> Void) { + let exp = expectation(description: "Dictionary changes for mixed collections") + return { change in + switch change { + case .initial: + break + case .update(_, deletions: let d, insertions: let i, modifications: let m): + XCTAssertEqual(d.count, deletions.count) + XCTAssertEqual(d, deletions) + XCTAssertEqual(i.count, insertions.count) + XCTAssertEqual(i, insertions) + XCTAssertEqual(m.count, modifications.count) + XCTAssertEqual(m, modifications) + exp.fulfill() + case .error(let error): + XCTFail("Unexpected error \(error)") + } + } + } + + func assertDictionaryNotification(_ list: List?, deletions: [Int], insertions: [Int], modifications: [Int], block: @escaping () -> Void) { + let token = list?.observe(expectChanges(deletions, insertions, modifications)) + let realm = realmWithTestPath() + try! realm.write { + block() + } + + waitForExpectations(timeout: 2) + token?.invalidate() + } + + let o = AnyRealmTypeObject() + o.anyValue.value = AnyRealmValue.fromArray(array) + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + + assertDictionaryNotification(o.anyValue.value.listValue, deletions: [], insertions: [2], modifications: []) { + o.anyValue.value.listValue?.append(AnyRealmValue.fromArray(array)) + } + + assertDictionaryNotification(o.anyValue.value.listValue?[0].listValue, deletions: [], insertions: [], modifications: [1]) { + o.anyValue.value.listValue?[0].listValue?[1] = .objectId(ObjectId.generate()) + } + + assertDictionaryNotification(o.anyValue.value.listValue?[0].listValue?[0].listValue, deletions: [2], insertions: [], modifications: []) { + o.anyValue.value.listValue?[0].listValue?[0].listValue?.removeLast() + } + + assertDictionaryNotification(o.anyValue.value.listValue?[0].listValue?[0].listValue, deletions: [0, 1], insertions: [], modifications: []) { + o.anyValue.value.listValue?[0].listValue?[0].listValue?.removeAll() + } + } + + func testReassignToMixedList() throws { + let list = AnyRealmValue.fromArray([.bool(true), AnyRealmValue.fromDictionary(["key": .int(12)]), .float(13.12)]) + + let mixedObject = AnyRealmTypeObject() + mixedObject.anyValue.value = list + + let realm = realmWithTestPath() + try realm.write { + realm.add(mixedObject) + } + XCTAssertEqual(mixedObject.anyValue.value.listValue?[1].dictionaryValue?["key"], .int(12)) + + try realm.write { + mixedObject.anyValue.value.listValue?.append(AnyRealmValue.fromArray([.double(20.20), .string("hello")])) + } + XCTAssertEqual(mixedObject.anyValue.value.listValue?[3].listValue?[0], .double(20.20)) + + try realm.write { + let listItem = mixedObject.anyValue.value.listValue?[0] + mixedObject.anyValue.value.listValue?.append(listItem!) + } + XCTAssertEqual(mixedObject.anyValue.value.listValue?[4], .bool(true)) + + try realm.write { + let listItem = mixedObject.anyValue.value.listValue?[3] + mixedObject.anyValue.value.listValue?.append(listItem!) + } + // TODO: Self-assignment - this doesn't work due to https://github.com/realm/realm-core/issues/7422 +// XCTAssertEqual(mixedObject.anyValue.value.listValue?[4].listValue?[0], .double(20.20)) + + try realm.write { + let listItem = mixedObject.anyValue.value.listValue?[3] + mixedObject.anyValue.value.listValue?[3] = listItem! + } + // TODO: Self-assignment - this doesn't work due to https://github.com/realm/realm-core/issues/7422 +// XCTAssertEqual(mixedObject.anyValue.value.listValue?[3].listValue?[0], .double(20.20)) + + try realm.write { + mixedObject.anyValue.value.listValue?[1] = AnyRealmValue.fromDictionary(["new-key": .int(3)]) + } + XCTAssertEqual(mixedObject.anyValue.value.listValue?[1].dictionaryValue?["new-key"], .int(3)) + + try realm.write { + let listItem = mixedObject.anyValue.value.listValue?[1] + mixedObject.anyValue.value.listValue?.append(listItem!) + } + // TODO: Self-assignment - this doesn't work due to https://github.com/realm/realm-core/issues/7422 +// XCTAssertEqual(mixedObject.anyValue.value.listValue?[3].dictionaryValue?["new-key"], .int(3)) + + try realm.write { + let listItem = mixedObject.anyValue.value.listValue?[1] + mixedObject.anyValue.value.listValue?[3] = listItem! + } + // TODO: Self-assignment - this doesn't work due to https://github.com/realm/realm-core/issues/7422 +// XCTAssertEqual(mixedObject.anyValue.value.listValue?[3].dictionaryValue?["new-key"], .int(3)) + } + + func testReassignToMixedDictionary() throws { + let dictionary = AnyRealmValue.fromDictionary(["key1": .bool(true), "key2": AnyRealmValue.fromDictionary(["key4": .int(12)]), "key3": .float(13.12)]) + + let mixedObject = AnyRealmTypeObject() + mixedObject.anyValue.value = dictionary + + let realm = realmWithTestPath() + try realm.write { + realm.add(mixedObject) + } + XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key2"]?.dictionaryValue?["key4"], .int(12)) + + try realm.write { + mixedObject.anyValue.value.dictionaryValue?["key4"] = AnyRealmValue.fromDictionary(["new-key": .int(3)]) + } + XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key4"]?.dictionaryValue?["new-key"], .int(3)) + + try realm.write { + let dictItem = mixedObject.anyValue.value.dictionaryValue?["key1"] + mixedObject.anyValue.value.dictionaryValue?["key5"] = dictItem + } + XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key5"], .bool(true)) + + try realm.write { + let dictItem = mixedObject.anyValue.value.dictionaryValue?["key2"] + mixedObject.anyValue.value.dictionaryValue?["key6"] = dictItem + } + // TODO: Self-assignment - this doesn't work due to https://github.com/realm/realm-core/issues/7422 +// XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key6"]?.dictionaryValue?["key4"], .int(12)) + + try realm.write { + mixedObject.anyValue.value.dictionaryValue?["key7"] = AnyRealmValue.fromArray([.string("hello"), .double(20.20)]) + } + XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key7"]?.listValue?[0], .string("hello")) + + try realm.write { + let dictItem = mixedObject.anyValue.value.dictionaryValue?["key7"] + mixedObject.anyValue.value.dictionaryValue?["key2"] = dictItem + } + // TODO: Self-assignment - this doesn't work due to https://github.com/realm/realm-core/issues/7422 +// XCTAssertEqual(mixedObject.anyValue.value.dictionaryValue?["key2"]?.listValue?[0], .string("hello")) + } + + func testEnumerationNestedCollection() throws { + var count = 0 + var accessNestedValue = false + func iterateNestedCollectionKeyValue(_ value: AnyRealmValue) { + count+=1 + switch value { + case .list(let l): + for item in l { + iterateNestedCollectionKeyValue(item) + } + case .dictionary(let d): + for (_, val) in d.asKeyValueSequence() { + iterateNestedCollectionKeyValue(val) + } + default: + accessNestedValue = true + } + } + + let so = SwiftStringObject() + so.stringCol = "hello" + + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .object(so) ]) + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary([ "key1": subArray2 ]) + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ subDict2 ]) + let subDict3: AnyRealmValue = AnyRealmValue.fromDictionary([ "key2": subArray3 ]) + let subArray4: AnyRealmValue = AnyRealmValue.fromArray([ subDict3 ]) + let subDict4: AnyRealmValue = AnyRealmValue.fromDictionary([ "key3": subArray4 ]) + let subArray5: AnyRealmValue = AnyRealmValue.fromArray([ subDict4 ]) + let subDict5: AnyRealmValue = AnyRealmValue.fromDictionary([ "key4": subArray5 ]) + let subArray6: AnyRealmValue = AnyRealmValue.fromArray([ subDict5 ]) + let subDict6: AnyRealmValue = AnyRealmValue.fromDictionary([ "key5": subArray6 ]) + let dictionary: Dictionary = [ + "key0": subDict6, + ] + + let o = AnyRealmTypeObject() + o.anyValue.value = AnyRealmValue.fromDictionary(dictionary) + + let realm = realmWithTestPath() + try realm.write { + realm.add(o) + } + + iterateNestedCollectionKeyValue(o.anyValue.value) + XCTAssertEqual(count, 12) + XCTAssertTrue(accessNestedValue) + + var countValue = 0 + var accessNestedValueValue = false + func iterateNestedCollectionValue(_ value: AnyRealmValue) { + countValue+=1 + switch value { + case .list(let l): + for item in l { + iterateNestedCollectionValue(item) + } + case .dictionary(let d): + for (val) in d { + iterateNestedCollectionValue(val.value) + } + default: + accessNestedValueValue = true + } + } + + iterateNestedCollectionValue(o.anyValue.value) + XCTAssertEqual(countValue, 12) + XCTAssertTrue(accessNestedValueValue) + } +} diff --git a/RealmSwift/Tests/ModernKVOTests.swift b/RealmSwift/Tests/ModernKVOTests.swift index 611854c076..0d8ca9afc6 100644 --- a/RealmSwift/Tests/ModernKVOTests.swift +++ b/RealmSwift/Tests/ModernKVOTests.swift @@ -367,6 +367,21 @@ class ModernKVOTests: TestCase { } } + func testCollectionInMixedKVO() { + let (obj, obs) = getObject(ModernAllTypesObject()) + + observeSetChange(obs, "anyCol") { obj.anyCol = AnyRealmValue.fromDictionary([ + "key1": .int(1234)]) } + observeSetChange(obs, "anyCol") { obj.anyCol.dictionaryValue?["key1"] = .string("hello") } + observeSetChange(obs, "anyCol") { obj.anyCol.dictionaryValue?["key1"] = nil } + + observeSetChange(obs, "anyCol") { obj.anyCol = AnyRealmValue.fromArray([ .int(1234)]) } + observeSetChange(obs, "anyCol") { obj.anyCol.listValue?[0] = .float(123.456) } + observeSetChange(obs, "anyCol") { obj.anyCol.listValue?.append(.bool(true)) } + observeSetChange(obs, "anyCol") { obj.anyCol.listValue?.insert(.date(Date()), at: 1) } + observeSetChange(obs, "anyCol") { obj.anyCol.listValue?.remove(at: 0) } + } + func testReadSharedSchemaFromObservedObject() { let obj = ModernAllTypesObject() obj.addObserver(self, forKeyPath: "boolCol", options: [.old, .new], context: nil) diff --git a/RealmSwift/Tests/ObjectCreationTests.swift b/RealmSwift/Tests/ObjectCreationTests.swift index 1c46cbf174..c6c6e2fcc1 100644 --- a/RealmSwift/Tests/ObjectCreationTests.swift +++ b/RealmSwift/Tests/ObjectCreationTests.swift @@ -198,7 +198,7 @@ class ObjectCreationTests: TestCase { return } verifySwiftObjectWithDictionaryLiteral(object, dictionary: SwiftObject.defaultValues(), boolObjectValue: false, - boolObjectListValues: []) + boolObjectListValues: []) // test realm properties are populated correctly XCTAssertEqual(object.realm!, realm) @@ -221,7 +221,7 @@ class ObjectCreationTests: TestCase { try! realm.write { let object = realm.create(SwiftOptionalDefaultValuesObject.self) self.verifySwiftOptionalObjectWithDictionaryLiteral(object, - dictionary: SwiftOptionalDefaultValuesObject.defaultValues(), boolObjectValue: true) + dictionary: SwiftOptionalDefaultValuesObject.defaultValues(), boolObjectValue: true) } } @@ -1445,6 +1445,8 @@ class ObjectCreationTests: TestCase { case .any: return ["hello"] case .linkingObjects: fatalError("not supported") case .UUID: return [UUID(uuidString: "137decc8-b300-4954-a233-f89909f4fd89")!, UUID(uuidString: "00000000-0000-0000-0000-000000000000")!] + default: + fatalError() } } @@ -1472,9 +1474,11 @@ class ObjectCreationTests: TestCase { case .object: return ["invalid", ["a"], ["boolCol": "a"], SwiftIntObject()] case .objectId: return ["invalid", 123] case .decimal128: return ["invalid"] - case .any: return [List()] + case .any: return [MutableSet()] case .linkingObjects: fatalError("not supported") case .UUID: return ["invalid"] + default: + fatalError() } } } diff --git a/RealmSwift/Tests/QueryTests.swift b/RealmSwift/Tests/QueryTests.swift index 36a76dfb4e..15904542d1 100644 --- a/RealmSwift/Tests/QueryTests.swift +++ b/RealmSwift/Tests/QueryTests.swift @@ -59,10 +59,10 @@ class QueryTests: TestCase { override func setUp() { realm = inMemoryRealm("QueryTests") try! realm.write { - let objModernCollectionsOfEnums = ModernCollectionsOfEnums() let objCustomPersistableCollections = CustomPersistableCollections() - let objModernAllTypesObject = ModernAllTypesObject() + let objModernCollectionsOfEnums = ModernCollectionsOfEnums() let objAllCustomPersistableTypes = AllCustomPersistableTypes() + let objModernAllTypesObject = ModernAllTypesObject() objModernAllTypesObject.boolCol = false objModernAllTypesObject.intCol = 3 @@ -420,10 +420,10 @@ class QueryTests: TestCase { objCustomPersistableCollections.mapOptUuid["foo"] = UUIDWrapper(persistedValue: UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!) objCustomPersistableCollections.mapOptUuid["bar"] = UUIDWrapper(persistedValue: UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09f")!) - realm.add(objModernCollectionsOfEnums) realm.add(objAllCustomPersistableTypes) - realm.add(objModernAllTypesObject) + realm.add(objModernCollectionsOfEnums) realm.add(objCustomPersistableCollections) + realm.add(objModernAllTypesObject) } } @@ -435,22 +435,22 @@ class QueryTests: TestCase { realm.beginWrite() realm.deleteAll() - let parentLinkToModernCollectionsOfEnums = realm.create(LinkToModernCollectionsOfEnums.self) - let childrenModernCollectionsOfEnums = [ModernCollectionsOfEnums(), ModernCollectionsOfEnums(), ModernCollectionsOfEnums()] - parentLinkToModernCollectionsOfEnums.list.append(objectsIn: childrenModernCollectionsOfEnums) - let parentLinkToCustomPersistableCollections = realm.create(LinkToCustomPersistableCollections.self) let childrenCustomPersistableCollections = [CustomPersistableCollections(), CustomPersistableCollections(), CustomPersistableCollections()] parentLinkToCustomPersistableCollections.list.append(objectsIn: childrenCustomPersistableCollections) - let parentLinkToModernAllTypesObject = realm.create(LinkToModernAllTypesObject.self) - let childrenModernAllTypesObject = [ModernAllTypesObject(), ModernAllTypesObject(), ModernAllTypesObject()] - parentLinkToModernAllTypesObject.list.append(objectsIn: childrenModernAllTypesObject) + let parentLinkToModernCollectionsOfEnums = realm.create(LinkToModernCollectionsOfEnums.self) + let childrenModernCollectionsOfEnums = [ModernCollectionsOfEnums(), ModernCollectionsOfEnums(), ModernCollectionsOfEnums()] + parentLinkToModernCollectionsOfEnums.list.append(objectsIn: childrenModernCollectionsOfEnums) let parentLinkToAllCustomPersistableTypes = realm.create(LinkToAllCustomPersistableTypes.self) let childrenAllCustomPersistableTypes = [AllCustomPersistableTypes(), AllCustomPersistableTypes(), AllCustomPersistableTypes()] parentLinkToAllCustomPersistableTypes.list.append(objectsIn: childrenAllCustomPersistableTypes) + let parentLinkToModernAllTypesObject = realm.create(LinkToModernAllTypesObject.self) + let childrenModernAllTypesObject = [ModernAllTypesObject(), ModernAllTypesObject(), ModernAllTypesObject()] + parentLinkToModernAllTypesObject.list.append(objectsIn: childrenModernAllTypesObject) + initForKeypathCollectionAggregates(childrenModernAllTypesObject, \.intCol) initForKeypathCollectionAggregates(childrenModernAllTypesObject, \.int8Col) @@ -986,6 +986,332 @@ class QueryTests: TestCase { } } + func testEqualAnyRealmList() { + let circleObject = self.circleObject + let object = objects()[0] + let list = List() + list.removeAll() + list.append(.none) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.none) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.int(123)) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.int(123)) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.bool(true)) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.bool(true)) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.float(123.456)) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.float(123.456)) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.double(123.456)) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.double(123.456)) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.string("FooBar")) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.string("FooBar")) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.data(Data(count: 64))) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.data(Data(count: 64))) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.date(Date(timeIntervalSince1970: 1000000))) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.date(Date(timeIntervalSince1970: 1000000))) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.object(circleObject)) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.object(circleObject)) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.objectId(ObjectId("61184062c1d8f096a3695046"))) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.objectId(ObjectId("61184062c1d8f096a3695046"))) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.decimal128(123.456)) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.decimal128(123.456)) + return $0.anyCol == .list(list) + } + list.removeAll() + list.append(.uuid(UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!)) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(.uuid(UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!)) + return $0.anyCol == .list(list) + } + } + + func testEqualAnyRealmDictionary() { + let circleObject = self.circleObject + let object = objects()[0] + let dictionary = Map() + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.none + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.none + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.int(123) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.int(123) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.bool(true) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.bool(true) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.float(123.456) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.float(123.456) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.double(123.456) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.double(123.456) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.string("FooBar") + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.string("FooBar") + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.data(Data(count: 64)) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.data(Data(count: 64)) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.date(Date(timeIntervalSince1970: 1000000)) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.date(Date(timeIntervalSince1970: 1000000)) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.object(circleObject) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.object(circleObject) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.objectId(ObjectId("61184062c1d8f096a3695046")) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.objectId(ObjectId("61184062c1d8f096a3695046")) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.decimal128(123.456) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.decimal128(123.456) + return $0.anyCol == .dictionary(dictionary) + } + dictionary.removeAll() + dictionary["key"] = AnyRealmValue.uuid(UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!) + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue.uuid(UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!) + return $0.anyCol == .dictionary(dictionary) + } + } + + func testNestedAnyRealmList() { + let object = objects()[0] + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .string("john"), .bool(false) ]) + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ subArray2, .double(76.54) ]) + let subArray4: AnyRealmValue = AnyRealmValue.fromArray([ subArray3, .int(99)]) + let array: Array = [ + subArray4, .float(20.34) + ] + + setAnyRealmValueCol(with: AnyRealmValue.fromArray(array), object: object) + assertQuery("(anyCol[%@] == %@)", values: [1, AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol[1] == .float(20.34) + } + + assertQuery("(anyCol[%K] == %@)", values: ["#any", AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol.any == .float(20.34) + } + + assertQuery("(anyCol[%@][%@] == %@)", values: [0, 1, AnyRealmValue.int(99)], count: 1) { + $0.anyCol[0][1] == .int(99) + } + + assertQuery("(anyCol[%@][%@][%@] == %@)", values: [0, 0, 1, AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol[0][0][1] == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%K] == %@)", values: [0, 0, "#any", AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol[0][0].any == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: [0, 0, 0, 0, AnyRealmValue.string("john")], count: 1) { + $0.anyCol[0][0][0][0] == .string("john") + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: [0, 0, 0, 1, AnyRealmValue.bool(false)], count: 1) { + $0.anyCol[0][0][0][1] == .bool(false) + } + + assertQuery("(anyCol[%@][%@][%@][%K] == %@)", values: [0, 0, 0, "#any", AnyRealmValue.string("john")], count: 1) { + $0.anyCol[0][0][0].any == .string("john") + } + + assertQuery("(anyCol[%@][%@][%@][%K] == %@)", values: [0, 0, 0, "#any", AnyRealmValue.bool(false)], count: 1) { + $0.anyCol[0][0][0].any == .bool(false) + } + + assertQuery("(anyCol[%@][%@] >= %@)", values: [0, 1, AnyRealmValue.int(99)], count: 1) { + $0.anyCol[0][1] >= .int(99) + } + + assertQuery("(anyCol[%@][%@] <= %@)", values: [0, 1, AnyRealmValue.int(99)], count: 1) { + $0.anyCol[0][1] <= .int(99) + } + + assertQuery("(anyCol[%@][%@] != %@)", values: [0, 1, AnyRealmValue.int(99)], count: 0) { + $0.anyCol[0][1] != .int(99) + } + + assertQuery("(anyCol[%@] == %@)", values: ["#any", AnyRealmValue.float(20.34)], count: 0) { + $0.anyCol["#any"] == .float(20.34) + } + } + + func testNestedAnyRealmDictionary() { + let object = objects()[0] + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary(["key6": .string("john"), "key7": .bool(false)]) + let subDict3: AnyRealmValue = AnyRealmValue.fromDictionary(["key4": subDict2, "key5": .double(76.54)]) + let subDict4: AnyRealmValue = AnyRealmValue.fromDictionary(["key2": subDict3, "key3": .int(99)]) + let dict: Dictionary = [ + "key0": subDict4, "key1": .float(20.34) + ] + + setAnyRealmValueCol(with: AnyRealmValue.fromDictionary(dict), object: object) + assertQuery("(anyCol[%@] == %@)", values: ["key1", AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol["key1"] == .float(20.34) + } + + assertQuery("(anyCol[%K] == %@)", values: ["#any", AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol.any == .float(20.34) + } + + assertQuery("(anyCol[%@][%@] == %@)", values: ["key0", "key3", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"]["key3"] == .int(99) + } + + assertQuery("(anyCol[%@][%K] == %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"].any == .int(99) + } + + assertQuery("(anyCol[%@][%@][%@] == %@)", values: ["key0", "key2", "key5", AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol["key0"]["key2"]["key5"] == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%K] == %@)", values: ["key0", "key2", "#any", AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol["key0"]["key2"].any == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: ["key0", "key2", "key4", "key6", AnyRealmValue.string("john")], count: 1) { + $0.anyCol["key0"]["key2"]["key4"]["key6"] == .string("john") + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: ["key0", "key2", "key4", "key7", AnyRealmValue.bool(false)], count: 1) { + $0.anyCol["key0"]["key2"]["key4"]["key7"] == .bool(false) + } + + assertQuery("(anyCol[%@][%K] >= %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"].any >= .int(99) + } + + assertQuery("(anyCol[%@][%K] <= %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"].any <= .int(99) + } + + assertQuery("(anyCol[%@][%@] != %@)", values: ["key0", "key3", AnyRealmValue.int(99)], count: 0) { + $0.anyCol["key0"]["key3"] != .int(99) + } + + assertQuery("(anyCol[%@][%@] == %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 0) { + $0.anyCol["key0"]["#any"] == .int(99) + } + } + func testEqualObject() { let nestedObject = ModernAllTypesObject() let object = objects().first! @@ -3685,23 +4011,17 @@ class QueryTests: TestCase { func testCollectionFromProperty() { try! realm.write { - let objModernCollectionsOfEnums = realm.objects(ModernCollectionsOfEnums.self).first! - _ = realm.create(LinkToModernCollectionsOfEnums.self, value: [ - "list": [objModernCollectionsOfEnums], - "set": [objModernCollectionsOfEnums], - "map": ["foo": objModernCollectionsOfEnums] - ]) let objCustomPersistableCollections = realm.objects(CustomPersistableCollections.self).first! _ = realm.create(LinkToCustomPersistableCollections.self, value: [ "list": [objCustomPersistableCollections], "set": [objCustomPersistableCollections], "map": ["foo": objCustomPersistableCollections] ]) - let objModernAllTypesObject = realm.objects(ModernAllTypesObject.self).first! - _ = realm.create(LinkToModernAllTypesObject.self, value: [ - "list": [objModernAllTypesObject], - "set": [objModernAllTypesObject], - "map": ["foo": objModernAllTypesObject] + let objModernCollectionsOfEnums = realm.objects(ModernCollectionsOfEnums.self).first! + _ = realm.create(LinkToModernCollectionsOfEnums.self, value: [ + "list": [objModernCollectionsOfEnums], + "set": [objModernCollectionsOfEnums], + "map": ["foo": objModernCollectionsOfEnums] ]) let objAllCustomPersistableTypes = realm.objects(AllCustomPersistableTypes.self).first! _ = realm.create(LinkToAllCustomPersistableTypes.self, value: [ @@ -3709,6 +4029,12 @@ class QueryTests: TestCase { "set": [objAllCustomPersistableTypes], "map": ["foo": objAllCustomPersistableTypes] ]) + let objModernAllTypesObject = realm.objects(ModernAllTypesObject.self).first! + _ = realm.create(LinkToModernAllTypesObject.self, value: [ + "list": [objModernAllTypesObject], + "set": [objModernAllTypesObject], + "map": ["foo": objModernAllTypesObject] + ]) } func test( @@ -9474,10 +9800,10 @@ private protocol LinkToTestObject: Object { var set: MutableSet { get } var map: Map { get } } -extension LinkToModernCollectionsOfEnums: LinkToTestObject {} extension LinkToCustomPersistableCollections: LinkToTestObject {} -extension LinkToModernAllTypesObject: LinkToTestObject {} +extension LinkToModernCollectionsOfEnums: LinkToTestObject {} extension LinkToAllCustomPersistableTypes: LinkToTestObject {} +extension LinkToModernAllTypesObject: LinkToTestObject {} private protocol QueryValue { static func queryValues() -> [Self] diff --git a/RealmSwift/Tests/QueryTests.swift.gyb b/RealmSwift/Tests/QueryTests.swift.gyb index 3213a68308..4bbcfb4db0 100644 --- a/RealmSwift/Tests/QueryTests.swift.gyb +++ b/RealmSwift/Tests/QueryTests.swift.gyb @@ -469,6 +469,160 @@ class QueryTests: TestCase { % end } + func testEqualAnyRealmList() { + let circleObject = self.circleObject + let object = objects()[0] + let list = List() + % for value in anyRealmValues: + list.removeAll() + list.append(${value.value(0)}) + setAnyRealmValueCol(with: .list(list), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.list(list), count: 1) { + let list = List() + list.append(${value.value(0)}) + return $0.anyCol == .list(list) + } + % end + } + + func testEqualAnyRealmDictionary() { + let circleObject = self.circleObject + let object = objects()[0] + let dictionary = Map() + % for value in anyRealmValues: + dictionary.removeAll() + dictionary["key"] = AnyRealmValue${value.value(0)} + setAnyRealmValueCol(with: .dictionary(dictionary), object: object) + assertQuery("(anyCol == %@)", AnyRealmValue.dictionary(dictionary), count: 1) { + let dictionary = Map() + dictionary["key"] = AnyRealmValue${value.value(0)} + return $0.anyCol == .dictionary(dictionary) + } + % end + } + + func testNestedAnyRealmList() { + let object = objects()[0] + let subArray2: AnyRealmValue = AnyRealmValue.fromArray([ .string("john"), .bool(false) ]) + let subArray3: AnyRealmValue = AnyRealmValue.fromArray([ subArray2, .double(76.54) ]) + let subArray4: AnyRealmValue = AnyRealmValue.fromArray([ subArray3, .int(99)]) + let array: Array = [ + subArray4, .float(20.34) + ] + + setAnyRealmValueCol(with: AnyRealmValue.fromArray(array), object: object) + assertQuery("(anyCol[%@] == %@)", values: [1, AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol[1] == .float(20.34) + } + + assertQuery("(anyCol[%K] == %@)", values: ["#any", AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol.any == .float(20.34) + } + + assertQuery("(anyCol[%@][%@] == %@)", values: [0, 1, AnyRealmValue.int(99)], count: 1) { + $0.anyCol[0][1] == .int(99) + } + + assertQuery("(anyCol[%@][%@][%@] == %@)", values: [0, 0, 1, AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol[0][0][1] == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%K] == %@)", values: [0, 0, "#any", AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol[0][0].any == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: [0, 0, 0, 0, AnyRealmValue.string("john")], count: 1) { + $0.anyCol[0][0][0][0] == .string("john") + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: [0, 0, 0, 1, AnyRealmValue.bool(false)], count: 1) { + $0.anyCol[0][0][0][1] == .bool(false) + } + + assertQuery("(anyCol[%@][%@][%@][%K] == %@)", values: [0, 0, 0, "#any", AnyRealmValue.string("john")], count: 1) { + $0.anyCol[0][0][0].any == .string("john") + } + + assertQuery("(anyCol[%@][%@][%@][%K] == %@)", values: [0, 0, 0, "#any", AnyRealmValue.bool(false)], count: 1) { + $0.anyCol[0][0][0].any == .bool(false) + } + + assertQuery("(anyCol[%@][%@] >= %@)", values: [0, 1, AnyRealmValue.int(99)], count: 1) { + $0.anyCol[0][1] >= .int(99) + } + + assertQuery("(anyCol[%@][%@] <= %@)", values: [0, 1, AnyRealmValue.int(99)], count: 1) { + $0.anyCol[0][1] <= .int(99) + } + + assertQuery("(anyCol[%@][%@] != %@)", values: [0, 1, AnyRealmValue.int(99)], count: 0) { + $0.anyCol[0][1] != .int(99) + } + + assertQuery("(anyCol[%@] == %@)", values: ["#any", AnyRealmValue.float(20.34)], count: 0) { + $0.anyCol["#any"] == .float(20.34) + } + } + + func testNestedAnyRealmDictionary() { + let object = objects()[0] + let subDict2: AnyRealmValue = AnyRealmValue.fromDictionary(["key6": .string("john"), "key7": .bool(false)]) + let subDict3: AnyRealmValue = AnyRealmValue.fromDictionary(["key4": subDict2, "key5": .double(76.54)]) + let subDict4: AnyRealmValue = AnyRealmValue.fromDictionary(["key2": subDict3, "key3": .int(99)]) + let dict: Dictionary = [ + "key0": subDict4, "key1": .float(20.34) + ] + + setAnyRealmValueCol(with: AnyRealmValue.fromDictionary(dict), object: object) + assertQuery("(anyCol[%@] == %@)", values: ["key1", AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol["key1"] == .float(20.34) + } + + assertQuery("(anyCol[%K] == %@)", values: ["#any", AnyRealmValue.float(20.34)], count: 1) { + $0.anyCol.any == .float(20.34) + } + + assertQuery("(anyCol[%@][%@] == %@)", values: ["key0", "key3", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"]["key3"] == .int(99) + } + + assertQuery("(anyCol[%@][%K] == %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"].any == .int(99) + } + + assertQuery("(anyCol[%@][%@][%@] == %@)", values: ["key0", "key2", "key5", AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol["key0"]["key2"]["key5"] == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%K] == %@)", values: ["key0", "key2", "#any", AnyRealmValue.double(76.54)], count: 1) { + $0.anyCol["key0"]["key2"].any == .double(76.54) + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: ["key0", "key2", "key4", "key6", AnyRealmValue.string("john")], count: 1) { + $0.anyCol["key0"]["key2"]["key4"]["key6"] == .string("john") + } + + assertQuery("(anyCol[%@][%@][%@][%@] == %@)", values: ["key0", "key2", "key4", "key7", AnyRealmValue.bool(false)], count: 1) { + $0.anyCol["key0"]["key2"]["key4"]["key7"] == .bool(false) + } + + assertQuery("(anyCol[%@][%K] >= %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"].any >= .int(99) + } + + assertQuery("(anyCol[%@][%K] <= %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 1) { + $0.anyCol["key0"].any <= .int(99) + } + + assertQuery("(anyCol[%@][%@] != %@)", values: ["key0", "key3", AnyRealmValue.int(99)], count: 0) { + $0.anyCol["key0"]["key3"] != .int(99) + } + + assertQuery("(anyCol[%@][%@] == %@)", values: ["key0", "#any", AnyRealmValue.int(99)], count: 0) { + $0.anyCol["key0"]["#any"] == .int(99) + } + } + func testEqualObject() { let nestedObject = ModernAllTypesObject() let object = objects().first!