diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae6f12d92..7c4ef91ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ x.y.z Release notes (yyyy-MM-dd) realm.objects(MixedObject.self).where { $0.anyValue[0][0][1] == .double(76.54) } ``` - The `.all` operator allows looking up in all keys or indexes. + 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) } ``` diff --git a/Realm/RLMCollection.mm b/Realm/RLMCollection.mm index 559a70046f..416a82f808 100644 --- a/Realm/RLMCollection.mm +++ b/Realm/RLMCollection.mm @@ -156,19 +156,11 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state NSUInteger batchCount = 0, count = state->extra[1]; @autoreleasepool { - if (!_parentInfo) { - RLMAccessorContext ctx = RLMAccessorContext(*_info); - for (NSUInteger index = state->state; index < count && batchCount < len; ++index) { - _strongBuffer[batchCount] = _results->get(ctx, index); - batchCount++; - } - } else { - // This is used by Dicitonary and List for nested collections. - RLMAccessorContext ctx = RLMAccessorContext(*_parentInfo, *_info, _property); - for (NSUInteger index = state->state; index < count && batchCount < len; ++index) { - _strongBuffer[batchCount] = _results->get(ctx, index); - batchCount++; - } + 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/RLMManagedArray.mm b/Realm/RLMManagedArray.mm index 41814ebf41..93191becde 100644 --- a/Realm/RLMManagedArray.mm +++ b/Realm/RLMManagedArray.mm @@ -20,7 +20,6 @@ #import "RLMAccessor.hpp" #import "RLMCollection_Private.hpp" -#import "RLMDictionary_Private.hpp" #import "RLMObjectSchema_Private.hpp" #import "RLMObjectStore.h" #import "RLMObject_Private.hpp" @@ -199,17 +198,8 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state - (id)objectAtIndex:(NSUInteger)index { return translateErrors([&]() -> id { - realm::Mixed value = _backingList.get_any(index); RLMAccessorContext context(*_ownerInfo, *_objectInfo, _property); - if (value.is_type(realm::type_Dictionary)) { - return context.box(_backingList.get_dictionary(realm::PathElement{(int)index})); - } - else if (value.is_type(realm::type_List)) { - return context.box(_backingList.get_list(realm::PathElement{(int)index})); - } - else { - return _backingList.get(context, index); - } + return _backingList.get(context, index); }); } @@ -507,7 +497,7 @@ - (BOOL)isFrozen { - (instancetype)resolveInRealm:(RLMRealm *)realm { auto& parentInfo = _ownerInfo->resolve(realm); return translateErrors([&] { - return [[RLMManagedArray alloc] initWithBackingCollection:_backingList.freeze(realm->_realm) + return [[self.class alloc] initWithBackingCollection:_backingList.freeze(realm->_realm) parentInfo:&parentInfo property:parentInfo.rlmObjectSchema[_property.name]]; }); diff --git a/Realm/RLMManagedDictionary.mm b/Realm/RLMManagedDictionary.mm index d9da579626..73e38504d8 100644 --- a/Realm/RLMManagedDictionary.mm +++ b/Realm/RLMManagedDictionary.mm @@ -19,7 +19,6 @@ #import "RLMDictionary_Private.hpp" #import "RLMAccessor.hpp" -#import "RLMArray_Private.hpp" #import "RLMCollection_Private.hpp" #import "RLMObjectSchema_Private.hpp" #import "RLMObjectStore.h" @@ -302,17 +301,7 @@ - (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop)) BOOL stop = false; @autoreleasepool { for (auto&& [key, value] : _backingCollection) { - id nestedValue; - if (value.is_type(realm::type_Dictionary)) { - nestedValue = context.box(_backingCollection.get_dictionary(key.get_string())); - } - else if (value.is_type(realm::type_List)) { - nestedValue = context.box(_backingCollection.get_list(key.get_string())); - } - else { - nestedValue = context.box(value); - } - block(context.box(key), nestedValue, &stop); + block(context.box(key), _backingCollection.get(context, key.get_string()), &stop); if (stop) { break; } @@ -483,7 +472,7 @@ - (BOOL)isFrozen { - (instancetype)resolveInRealm:(RLMRealm *)realm { auto& parentInfo = _ownerInfo->resolve(realm); return translateErrors([&] { - return [[RLMManagedDictionary alloc] initWithBackingCollection:_backingCollection.freeze(realm->_realm) + return [[self.class alloc] initWithBackingCollection:_backingCollection.freeze(realm->_realm) parentInfo:&parentInfo property:parentInfo.rlmObjectSchema[_property.name]]; }); diff --git a/Realm/RLMQueryUtil.mm b/Realm/RLMQueryUtil.mm index 2f5b998b4f..a0a307f75c 100644 --- a/Realm/RLMQueryUtil.mm +++ b/Realm/RLMQueryUtil.mm @@ -568,7 +568,7 @@ void add_collection_operation_constraint(NSPredicateOperatorType operatorType, CollectionOperation collection_operation_from_key_path(KeyPath&& kp); ColumnReference column_reference_from_key_path(KeyPath&& kp, bool isAggregate); - void get_path_elements(std::vector &paths, NSExpression *expression); + NSString* get_path_elements(std::vector &paths, NSExpression *expression); private: Query& m_query; @@ -1684,19 +1684,8 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression) NSComparisonPredicateOptions options, NSPredicateOperatorType operatorType, NSExpression *right) { std::vector pathElements; - get_path_elements(pathElements, functionExpression); + NSString *keyPath = get_path_elements(pathElements, functionExpression); - NSString *keyPath; - NSExpression *keyPathExpression = functionExpression; - - // We consider only `NSKeyPathExpressionType` values to build the keypath. - for (unsigned long i = 0; i < pathElements.size(); i++) { - if (keyPathExpression.arguments[0].expressionType == NSKeyPathExpressionType) { - keyPath = [NSString stringWithFormat:@"%@", keyPathExpression.arguments[0]]; - } else { - keyPathExpression = keyPathExpression.arguments[0]; - } - } ColumnReference collectionColumn = column_reference_from_key_path(key_path_from_string(m_schema, objectSchema, keyPath), true); if (collectionColumn.property().type == RLMPropertyTypeAny && !collectionColumn.property().dictionary) { @@ -1704,6 +1693,10 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression) } else { RLMPrecondition(collectionColumn.property().dictionary, @"Invalid predicate", @"Invalid keypath '%@': only dictionaries and mixed 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); } } @@ -1857,28 +1850,42 @@ bool is_self_value_for_key_path_function_expression(NSExpression *expression) } // This function returns the nested subscripts from a NSPredicate with the following format `anyCol[0]['key']['all']` -// This will iterate each argument of the NSExpression and its nested NSExpression's and take the constant value -// and create a PathElement for the query. -void QueryBuilder::get_path_elements(std::vector &paths, NSExpression *expression) { +// 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. +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) { - 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; - if ([key isEqual:@"all"]) { - paths.push_back(PathElement{}); - } else { - paths.push_back(PathElement{key.UTF8String}); - } + 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; + if ([key isEqual:@"*"]) { + paths.push_back(PathElement{}); + } else { + paths.push_back(PathElement{key.UTF8String}); } + } else { + throwException(@"Invalid subscript type", + @"Only `Strings` or index are allowed subscripts"); } + } else if (expression.arguments[i].expressionType == NSKeyPathExpressionType) { + nestedKeyPath = expression.arguments[i].keyPath; + } else { + throwException(@"Invalid expression type", + @"Subscripts queries don't allow any other expression types"); + } + if ([nestedKeyPath length] > 0) { + keyPath = ([keyPath length] > 0) ? [NSString stringWithFormat:@"%@.%@", keyPath, nestedKeyPath] : [NSString stringWithFormat:@"%@", nestedKeyPath]; } } + + return keyPath; } } // namespace diff --git a/Realm/RLMUtil.mm b/Realm/RLMUtil.mm index 955f942987..5ce1066acc 100644 --- a/Realm/RLMUtil.mm +++ b/Realm/RLMUtil.mm @@ -290,10 +290,6 @@ void RLMValidateValueForProperty(__unsafe_unretained id const obj, if (prop.type == RLMPropertyTypeObject && !validateObjects) { return; } - - if (prop.type == RLMPropertyTypeAny) { - return; - } if (RLMIsObjectValidForProperty(obj, prop)) { return; diff --git a/Realm/RLMValue.h b/Realm/RLMValue.h index 39ab349519..0e1a81e3ad 100644 --- a/Realm/RLMValue.h +++ b/Realm/RLMValue.h @@ -111,3 +111,11 @@ /// :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 ef7ddd8f98..c61b6abf2a 100644 --- a/Realm/RLMValue.mm +++ b/Realm/RLMValue.mm @@ -24,7 +24,7 @@ @implementation NSData (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeData; } @@ -41,7 +41,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation NSDate (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeDate; } @@ -58,7 +58,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation NSNumber (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { if ([self objCType][0] == 'c' && (self.intValue == 0 || self.intValue == 1)) { return RLMPropertyTypeBool; @@ -79,10 +79,21 @@ - (RLMPropertyType)rlm_valueType { #pragma clang diagnostic pop - (RLMAnyValueType)rlm_anyValueType { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return (RLMAnyValueType)self.rlm_valueType; -#pragma clang diagnostic pop + 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 @@ -92,7 +103,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation NSNull (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeAny; } @@ -109,7 +120,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation NSString (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeString; } @@ -126,7 +137,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation NSUUID (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeUUID; } @@ -143,7 +154,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation RLMDecimal128 (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeDecimal128; } @@ -160,7 +171,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation RLMObjectBase (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeObject; } @@ -177,7 +188,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation RLMObjectId (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { return RLMPropertyTypeObjectId; } @@ -194,7 +205,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation NSDictionary (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { REALM_UNREACHABLE(); } @@ -209,7 +220,7 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation RLMDictionary (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { REALM_UNREACHABLE(); } @@ -226,10 +237,11 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation NSArray (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { - REALM_UNREACHABLE(); + return RLMPropertyTypeAny; } +#pragma clang diagnostic pop - (RLMAnyValueType)rlm_anyValueType { return RLMAnyValueTypeList; @@ -240,10 +252,11 @@ - (RLMAnyValueType)rlm_anyValueType { @implementation RLMArray (RLMValue) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (RLMPropertyType)rlm_valueType { - REALM_UNREACHABLE(); + return RLMPropertyTypeAny; } +#pragma clang diagnostic pop - (RLMAnyValueType)rlm_anyValueType { return RLMAnyValueTypeList; diff --git a/Realm/Tests/ObjectTests.m b/Realm/Tests/ObjectTests.m index 1a25dbf79a..521b7d7c63 100644 --- a/Realm/Tests/ObjectTests.m +++ b/Realm/Tests/ObjectTests.m @@ -785,8 +785,7 @@ - (void)testCreateInRealmValidationForDictionary { } if ([keyToInvalidate isEqualToString:@"anyCol"]) { - // Mixed properties now can accept dictionary or list, so we are skipping testing invalid values for this. - continue; + obj = self; } invalidInput[keyToInvalidate] = obj; diff --git a/Realm/Tests/QueryTests.m b/Realm/Tests/QueryTests.m index 7e4bc43732..555343bd0b 100644 --- a/Realm/Tests/QueryTests.m +++ b/Realm/Tests/QueryTests.m @@ -3810,6 +3810,18 @@ - (void)testDictionarySubscriptThrowsException { @"Invalid keypath 'set[\"invalid\"]': only dictionaries and mixed 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"); + RLMAssertThrowsWithReason(([realm objects:@"DictionaryPropertyObject" where:@"stringDictionary[%@] = NULL", [RLMObjectId objectId]]), + @"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]; + RLMAssertThrowsWithReason(([realm objects:@"AllTypesObject" where:@"anyCol[%@] = NULL", [NSDate date]]), + @"Only `Strings` or index are allowed subscripts"); } - (void)testCollectionsQueryAllValuesAllKeys { diff --git a/Realm/Tests/Swift/SwiftDynamicTests.swift b/Realm/Tests/Swift/SwiftDynamicTests.swift index f9ab880314..b547b95cdf 100644 --- a/Realm/Tests/Swift/SwiftDynamicTests.swift +++ b/Realm/Tests/Swift/SwiftDynamicTests.swift @@ -161,43 +161,23 @@ class SwiftRLMDynamicTests: RLMTestCase { let obj1 = AllTypesObject.values(5, stringObject: StringObject(value: ["newString"]), mixedObject: MixedObject(value: [["string", 45, false]]))! - let obj2 = AllTypesObject.values(2, - stringObject: StringObject(value: ["newString1"]), - mixedObject: MixedObject(value: [["key1": "string", "key2": true, "key3": 12.12]]))! - let obj3 = AllTypesObject.values(2, - stringObject: StringObject(value: ["newString1"]), - mixedObject: MixedObject(value: [["key1": ["string", true, ["key3": 12.12]]]]))! autoreleasepool { // open realm in autoreleasepool to create tables and then dispose let realm = self.realmWithTestPath() realm.beginWriteTransaction() _ = AllTypesObject.create(in: realm, withValue: obj1) - _ = AllTypesObject.create(in: realm, withValue: obj2) - _ = AllTypesObject.create(in: realm, withValue: obj3) try! realm.commitWriteTransaction() } let dyrealm = realm(withTestPathAndSchema: nil) let results = dyrealm.allObjects(AllTypesObject.className()) - XCTAssertEqual(results.count, UInt(3)) + XCTAssertEqual(results.count, UInt(1)) let robj1 = results[0] - let robj2 = results[1] - let robj3 = results[2] // 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) - - // Mixed Dictionary - XCTAssertTrue(((robj2["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedDictionary)["key1"] as! String == "string") - XCTAssertTrue(((robj2["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedDictionary)["key2"] as! Bool == true) - XCTAssertTrue(((robj2["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedDictionary)["key3"] as! Double == 12.12) - - // Mixed Dictionary - XCTAssertTrue((((robj3["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedDictionary)["key1"] as! RLMManagedArray)[0] as! String == "string") - XCTAssertTrue((((robj3["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedDictionary)["key1"] as! RLMManagedArray)[1] as! Bool == true) - XCTAssertTrue(((((robj3["mixedObjectCol"] as! RLMObject)["anyCol"] as! RLMManagedDictionary)["key1"] as! RLMManagedArray)[2] as! RLMManagedDictionary)["key3"] as! Double == 12.12) } } diff --git a/RealmSwift/AnyRealmValue.swift b/RealmSwift/AnyRealmValue.swift index 67f9788673..0d0e41d278 100644 --- a/RealmSwift/AnyRealmValue.swift +++ b/RealmSwift/AnyRealmValue.swift @@ -205,12 +205,34 @@ public enum AnyRealmValue: Hashable { public func hash(into hasher: inout Hasher) { switch self { - case .dictionary: - hasher.combine(1) - case .list: - hasher.combine(2) - default: - break + case let .int(i): + hasher.combine(i.hashValue) + case let .bool(b): + hasher.combine(b.hashValue) + case let .float(f): + hasher.combine(f.hashValue) + case let .double(d): + hasher.combine(d.hashValue) + case let .string(s): + hasher.combine(s.hashValue) + case let .data(d): + hasher.combine(d.hashValue) + case let .date(d): + hasher.combine(d.hashValue) + case let .objectId(o): + hasher.combine(o.hashValue) + case let .decimal128(d): + hasher.combine(d.hashValue) + case let .uuid(u): + hasher.combine(u.hashValue) + case let .object(o): + hasher.combine(o.hashValue) + case let .dictionary(d): + hasher.combine(12) + case let .list(l): + hasher.combine(13) + case .none: + hasher.combine(14) } } } diff --git a/RealmSwift/Impl/ObjcBridgeable.swift b/RealmSwift/Impl/ObjcBridgeable.swift index 4124a14da9..8b17dba00a 100644 --- a/RealmSwift/Impl/ObjcBridgeable.swift +++ b/RealmSwift/Impl/ObjcBridgeable.swift @@ -163,27 +163,6 @@ extension AnyRealmValue: BuiltInObjcBridgeable { } } - -extension RLMManagedDictionary: BuiltInObjcBridgeable { - @objc public class func _rlmFromObjc(_ value: Any) -> Self? { - fatalError() - } - public var _rlmObjcValue: Any { - let map = Map(objc: self) - return AnyRealmValue.dictionary(map) - } -} - -extension RLMManagedArray: BuiltInObjcBridgeable { - @objc public class func _rlmFromObjc(_ value: Any) -> Self? { - fatalError() - } - public var _rlmObjcValue: Any { - let list = List(collection: self) - return AnyRealmValue.list(list) - } -} - // MARK: - Collections extension Map: BuiltInObjcBridgeable { diff --git a/RealmSwift/Object.swift b/RealmSwift/Object.swift index fe825aff26..eb31eb5d06 100644 --- a/RealmSwift/Object.swift +++ b/RealmSwift/Object.swift @@ -789,8 +789,6 @@ public final class DynamicObject: Object { return set.isOptional ? MutableSet(collection: set) : MutableSet(collection: set) case .object: return MutableSet(collection: set) - default: - fatalError() } } diff --git a/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift b/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift index f9bd2dd244..9c15c4f5dc 100644 --- a/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift +++ b/RealmSwift/ObjectiveCSupport+AnyRealmValue.swift @@ -20,21 +20,6 @@ import Foundation import Realm -/// :nodoc: -extension RLMDictionary: RLMValue { - /// :nodoc: - public var rlm_anyValueType: RLMAnyValueType { .dictionary } - /// :nodoc: - public var rlm_valueType: RLMAnyValueType { fatalError("Unreachable, this should use `rlm_anyValueType`") } -} -/// :nodoc: -extension RLMArray: RLMValue { - /// :nodoc: - public var rlm_anyValueType: RLMAnyValueType { .list } - /// :nodoc: - public var rlm_valueType: RLMAnyValueType { fatalError("Unreachable, this should use `rlm_anyValueType`") } -} - public extension ObjectiveCSupport { /// Convert an object boxed in `AnyRealmValue` to its @@ -49,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): diff --git a/RealmSwift/Query.swift b/RealmSwift/Query.swift index 05a2952283..affb979171 100644 --- a/RealmSwift/Query.swift +++ b/RealmSwift/Query.swift @@ -302,7 +302,7 @@ extension Query where T == AnyRealmValue { } /// Query all indexes or keys in a mixed nested collecttion. public var all: Query { - .init(appendKeyPath("['all']", options: [.isPath])) + .init(appendKeyPath("['*']", options: [.isPath])) } } diff --git a/RealmSwift/RealmCollection.swift b/RealmSwift/RealmCollection.swift index e9e1140adf..7cd4321315 100644 --- a/RealmSwift/RealmCollection.swift +++ b/RealmSwift/RealmCollection.swift @@ -84,9 +84,16 @@ public protocol _RealmMapValue { /// Advance to the next element and return it, or `nil` if no next element exists. 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 { - return (key: key, value: value) + if let key = next as? Key { + if let value = collection[key as AnyObject].map(dynamicBridgeCast) as? Value { + return (key: key, value: value) + } else if let value = collection[key as AnyObject] as? RLMDictionary { + let map = Map(objc: value) + return (key: key, value: AnyRealmValue.dictionary(map) as! Value) + } else if let value = collection[key as AnyObject] as? RLMArray { + let list = List(collection: value) + return (key: key, value: AnyRealmValue.list(list) as! Value) + } } return nil } diff --git a/RealmSwift/Tests/QueryTests.swift b/RealmSwift/Tests/QueryTests.swift index 1145ff25e2..4fabfcfdd2 100644 --- a/RealmSwift/Tests/QueryTests.swift +++ b/RealmSwift/Tests/QueryTests.swift @@ -1204,26 +1204,38 @@ class QueryTests: TestCase { $0.anyCol[1] == .none } - assertQuery("(anyCol['all'] == %@)", AnyRealmValue.none, count: 1) { + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { $0.anyCol.all == .none } + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { + $0.anyCol["*"] == .none + } + assertQuery("(anyCol[0][1] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol[0][1] == .int(99) } - assertQuery("(anyCol[0]['all'] == %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol[0]['*'] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol[0].all == .int(99) } + assertQuery("(anyCol[0]['*'] == %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol[0]["*"] == .int(99) + } + assertQuery("(anyCol[0][0][1] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol[0][0][1] == .double(76.54) } - assertQuery("(anyCol[0][0]['all'] == %@)", AnyRealmValue.double(76.54), count: 1) { + assertQuery("(anyCol[0][0]['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol[0][0].all == .double(76.54) } + assertQuery("(anyCol[0][0]['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { + $0.anyCol[0][0]["*"] == .double(76.54) + } + assertQuery("(anyCol[0][0][0][0] == %@)", AnyRealmValue.string("john"), count: 1) { $0.anyCol[0][0][0][0] == .string("john") } @@ -1232,14 +1244,22 @@ class QueryTests: TestCase { $0.anyCol[0][0][0][1] == .bool(false) } - assertQuery("(anyCol[0][0][0]['all'] == %@)", AnyRealmValue.string("john"), count: 1) { + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.string("john"), count: 1) { $0.anyCol[0][0][0].all == .string("john") } - assertQuery("(anyCol[0][0][0]['all'] == %@)", AnyRealmValue.bool(false), count: 1) { + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.string("john"), count: 1) { + $0.anyCol[0][0][0]["*"] == .string("john") + } + + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.bool(false), count: 1) { $0.anyCol[0][0][0].all == .bool(false) } + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.bool(false), count: 1) { + $0.anyCol[0][0][0]["*"] == .bool(false) + } + assertQuery("(anyCol[0][1] >= %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol[0][1] >= .int(99) } @@ -1267,26 +1287,38 @@ class QueryTests: TestCase { $0.anyCol["key1"] == .none } - assertQuery("(anyCol['all'] == %@)", AnyRealmValue.none, count: 1) { + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { $0.anyCol.all == .none } + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { + $0.anyCol["*"] == .none + } + assertQuery("(anyCol['key0']['key3'] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"]["key3"] == .int(99) } - assertQuery("(anyCol['key0']['all'] == %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol['key0']['*'] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"].all == .int(99) } + assertQuery("(anyCol['key0']['*'] == %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol["key0"]["*"] == .int(99) + } + assertQuery("(anyCol['key0']['key2']['key5'] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol["key0"]["key2"]["key5"] == .double(76.54) } - assertQuery("(anyCol['key0']['key2']['all'] == %@)", AnyRealmValue.double(76.54), count: 1) { + assertQuery("(anyCol['key0']['key2']['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol["key0"]["key2"].all == .double(76.54) } + assertQuery("(anyCol['key0']['key2']['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { + $0.anyCol["key0"]["key2"]["*"] == .double(76.54) + } + assertQuery("(anyCol['key0']['key2']['key4']['key6'] == %@)", AnyRealmValue.string("john"), count: 1) { $0.anyCol["key0"]["key2"]["key4"]["key6"] == .string("john") } @@ -1295,14 +1327,22 @@ class QueryTests: TestCase { $0.anyCol["key0"]["key2"]["key4"]["key7"] == .bool(false) } - assertQuery("(anyCol['key0']['all'] >= %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol['key0']['*'] >= %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"].all >= .int(99) } - assertQuery("(anyCol['key0']['all'] <= %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol['key0']['*'] >= %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol["key0"]["*"] >= .int(99) + } + + assertQuery("(anyCol['key0']['*'] <= %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"].all <= .int(99) } + assertQuery("(anyCol['key0']['*'] <= %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol["key0"]["*"] <= .int(99) + } + assertQuery("(anyCol['key0']['key3'] != %@)", AnyRealmValue.int(99), count: 0) { $0.anyCol["key0"]["key3"] != .int(99) } diff --git a/RealmSwift/Tests/QueryTests.swift.gyb b/RealmSwift/Tests/QueryTests.swift.gyb index ccefa35077..b38ea90dc8 100644 --- a/RealmSwift/Tests/QueryTests.swift.gyb +++ b/RealmSwift/Tests/QueryTests.swift.gyb @@ -515,26 +515,38 @@ class QueryTests: TestCase { $0.anyCol[1] == .none } - assertQuery("(anyCol['all'] == %@)", AnyRealmValue.none, count: 1) { + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { $0.anyCol.all == .none } + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { + $0.anyCol["*"] == .none + } + assertQuery("(anyCol[0][1] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol[0][1] == .int(99) } - assertQuery("(anyCol[0]['all'] == %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol[0]['*'] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol[0].all == .int(99) } + assertQuery("(anyCol[0]['*'] == %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol[0]["*"] == .int(99) + } + assertQuery("(anyCol[0][0][1] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol[0][0][1] == .double(76.54) } - assertQuery("(anyCol[0][0]['all'] == %@)", AnyRealmValue.double(76.54), count: 1) { + assertQuery("(anyCol[0][0]['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol[0][0].all == .double(76.54) } + assertQuery("(anyCol[0][0]['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { + $0.anyCol[0][0]["*"] == .double(76.54) + } + assertQuery("(anyCol[0][0][0][0] == %@)", AnyRealmValue.string("john"), count: 1) { $0.anyCol[0][0][0][0] == .string("john") } @@ -543,14 +555,22 @@ class QueryTests: TestCase { $0.anyCol[0][0][0][1] == .bool(false) } - assertQuery("(anyCol[0][0][0]['all'] == %@)", AnyRealmValue.string("john"), count: 1) { + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.string("john"), count: 1) { $0.anyCol[0][0][0].all == .string("john") } - assertQuery("(anyCol[0][0][0]['all'] == %@)", AnyRealmValue.bool(false), count: 1) { + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.string("john"), count: 1) { + $0.anyCol[0][0][0]["*"] == .string("john") + } + + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.bool(false), count: 1) { $0.anyCol[0][0][0].all == .bool(false) } + assertQuery("(anyCol[0][0][0]['*'] == %@)", AnyRealmValue.bool(false), count: 1) { + $0.anyCol[0][0][0]["*"] == .bool(false) + } + assertQuery("(anyCol[0][1] >= %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol[0][1] >= .int(99) } @@ -578,26 +598,38 @@ class QueryTests: TestCase { $0.anyCol["key1"] == .none } - assertQuery("(anyCol['all'] == %@)", AnyRealmValue.none, count: 1) { + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { $0.anyCol.all == .none } + assertQuery("(anyCol['*'] == %@)", AnyRealmValue.none, count: 1) { + $0.anyCol["*"] == .none + } + assertQuery("(anyCol['key0']['key3'] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"]["key3"] == .int(99) } - assertQuery("(anyCol['key0']['all'] == %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol['key0']['*'] == %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"].all == .int(99) } + assertQuery("(anyCol['key0']['*'] == %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol["key0"]["*"] == .int(99) + } + assertQuery("(anyCol['key0']['key2']['key5'] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol["key0"]["key2"]["key5"] == .double(76.54) } - assertQuery("(anyCol['key0']['key2']['all'] == %@)", AnyRealmValue.double(76.54), count: 1) { + assertQuery("(anyCol['key0']['key2']['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { $0.anyCol["key0"]["key2"].all == .double(76.54) } + assertQuery("(anyCol['key0']['key2']['*'] == %@)", AnyRealmValue.double(76.54), count: 1) { + $0.anyCol["key0"]["key2"]["*"] == .double(76.54) + } + assertQuery("(anyCol['key0']['key2']['key4']['key6'] == %@)", AnyRealmValue.string("john"), count: 1) { $0.anyCol["key0"]["key2"]["key4"]["key6"] == .string("john") } @@ -606,14 +638,22 @@ class QueryTests: TestCase { $0.anyCol["key0"]["key2"]["key4"]["key7"] == .bool(false) } - assertQuery("(anyCol['key0']['all'] >= %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol['key0']['*'] >= %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"].all >= .int(99) } - assertQuery("(anyCol['key0']['all'] <= %@)", AnyRealmValue.int(99), count: 1) { + assertQuery("(anyCol['key0']['*'] >= %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol["key0"]["*"] >= .int(99) + } + + assertQuery("(anyCol['key0']['*'] <= %@)", AnyRealmValue.int(99), count: 1) { $0.anyCol["key0"].all <= .int(99) } + assertQuery("(anyCol['key0']['*'] <= %@)", AnyRealmValue.int(99), count: 1) { + $0.anyCol["key0"]["*"] <= .int(99) + } + assertQuery("(anyCol['key0']['key3'] != %@)", AnyRealmValue.int(99), count: 0) { $0.anyCol["key0"]["key3"] != .int(99) }