1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2014 Realm Inc.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 ////////////////////////////////////////////////////////////////////////////
19 #import "RLMRealm_Private.hpp"
21 #import "RLMAnalytics.hpp"
22 #import "RLMArray_Private.hpp"
23 #import "RLMMigration_Private.h"
24 #import "RLMObject_Private.h"
25 #import "RLMObject_Private.hpp"
26 #import "RLMObjectSchema_Private.hpp"
27 #import "RLMObjectStore.h"
28 #import "RLMObservation.hpp"
29 #import "RLMProperty.h"
30 #import "RLMProperty_Private.h"
31 #import "RLMQueryUtil.hpp"
32 #import "RLMRealmConfiguration_Private.hpp"
33 #import "RLMRealmUtil.hpp"
34 #import "RLMSchema_Private.hpp"
35 #import "RLMSyncManager_Private.h"
36 #import "RLMSyncUtil_Private.hpp"
37 #import "RLMThreadSafeReference_Private.hpp"
38 #import "RLMUpdateChecker.hpp"
41 #include "impl/realm_coordinator.hpp"
42 #include "object_store.hpp"
44 #include "shared_realm.hpp"
46 #include <realm/disable_sync_to_disk.hpp>
47 #include <realm/util/scope_exit.hpp>
48 #include <realm/version.hpp>
50 #import "sync/sync_session.hpp"
52 using namespace realm;
55 @interface RLMRealmNotificationToken : RLMNotificationToken
56 @property (nonatomic, strong) RLMRealm *realm;
57 @property (nonatomic, copy) RLMNotificationBlock block;
60 @interface RLMRealm ()
61 @property (nonatomic, strong) NSHashTable<RLMRealmNotificationToken *> *notificationHandlers;
62 - (void)sendNotifications:(RLMNotification)notification;
65 void RLMDisableSyncToDisk() {
66 realm::disable_sync_to_disk();
69 static void RLMAddSkipBackupAttributeToItemAtPath(std::string const& path) {
70 [[NSURL fileURLWithPath:@(path.c_str())] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
73 @implementation RLMRealmNotificationToken
75 [_realm verifyThread];
76 [_realm.notificationHandlers removeObject:self];
81 - (void)suppressNextNotification {
82 // Temporarily replace the block with one which restores the old block
83 // rather than producing a notification.
85 // This briefly creates a retain cycle but it's fine because the block will
86 // be synchronously called shortly after this method is called. Unlike with
87 // collection notifications, this does not have to go through the object
88 // store or do fancy things to handle transaction coalescing because it's
89 // called synchronously by the obj-c code and not by the object store.
90 auto notificationBlock = _block;
91 _block = ^(RLMNotification, RLMRealm *) {
92 _block = notificationBlock;
97 if (_realm || _block) {
98 NSLog(@"RLMNotificationToken released without unregistering a notification. You must hold "
99 @"on to the RLMNotificationToken returned from addNotificationBlock and call "
100 @"-[RLMNotificationToken invalidate] when you no longer wish to receive RLMRealm notifications.");
105 static bool shouldForciblyDisableEncryption() {
106 static bool disableEncryption = getenv("REALM_DISABLE_ENCRYPTION");
107 return disableEncryption;
110 NSData *RLMRealmValidatedEncryptionKey(NSData *key) {
111 if (shouldForciblyDisableEncryption()) {
115 if (key && key.length != 64) {
116 @throw RLMException(@"Encryption key must be exactly 64 bytes long");
122 @implementation RLMRealm {
123 NSHashTable<RLMFastEnumerator *> *_collectionEnumerators;
124 bool _sendingNotifications;
127 + (BOOL)isCoreDebug {
128 return realm::Version::has_feature(realm::feature_Debug);
132 static bool initialized;
138 RLMCheckForUpdates();
142 - (instancetype)initPrivate {
148 return realm::ObjectStore::is_empty(self.group);
151 - (void)verifyThread {
153 _realm->verify_thread();
155 catch (std::exception const& e) {
156 @throw RLMException(e);
160 - (BOOL)inWriteTransaction {
161 return _realm->is_in_transaction();
164 - (realm::Group &)group {
165 return _realm->read_group();
168 - (BOOL)autorefresh {
169 return _realm->auto_refresh();
172 - (void)setAutorefresh:(BOOL)autorefresh {
173 _realm->set_auto_refresh(autorefresh);
176 + (instancetype)defaultRealm {
177 return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration rawDefaultConfiguration] error:nil];
180 + (instancetype)realmWithURL:(NSURL *)fileURL {
181 RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
182 configuration.fileURL = fileURL;
183 return [RLMRealm realmWithConfiguration:configuration error:nil];
186 + (void)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration
187 callbackQueue:(dispatch_queue_t)callbackQueue
188 callback:(RLMAsyncOpenRealmCallback)callback {
189 RLMRealm *strongReferenceToSyncedRealm = nil;
190 if (configuration.config.sync_config) {
191 NSError *error = nil;
192 strongReferenceToSyncedRealm = [RLMRealm uncachedSchemalessRealmWithConfiguration:configuration error:&error];
194 dispatch_async(callbackQueue, ^{
195 callback(nil, error);
200 static dispatch_queue_t queue = dispatch_queue_create("io.realm.asyncOpenDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
201 dispatch_async(queue, ^{
203 if (strongReferenceToSyncedRealm) {
204 // Sync behavior: get the raw session, then wait for it to download.
205 if (auto session = sync_session_for_realm(strongReferenceToSyncedRealm)) {
206 // Wait for the session to download, then open it.
207 session->wait_for_download_completion([=](std::error_code error_code) {
208 dispatch_async(callbackQueue, ^{
209 (void)strongReferenceToSyncedRealm;
210 NSError *error = nil;
211 if (error_code == std::error_code{}) {
214 // Try opening the Realm on the destination queue.
215 RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&error];
216 callback(localRealm, error);
220 callback(nil, make_sync_error(RLMSyncSystemErrorKindSession,
221 @(error_code.message().c_str()),
228 dispatch_async(callbackQueue, ^{
229 callback(nil, make_sync_error(RLMSyncSystemErrorKindSession,
230 @"Cannot asynchronously open synced Realm, because the associated session previously experienced a fatal error",
237 // Default behavior: just dispatch onto the destination queue and open the Realm.
238 dispatch_async(callbackQueue, ^{
240 NSError *error = nil;
241 RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&error];
242 callback(localRealm, error);
251 // ARC tries to eliminate calls to autorelease when the value is then immediately
252 // returned, but this results in significantly different semantics between debug
253 // and release builds for RLMRealm, so force it to always autorelease.
254 static id RLMAutorelease(__unsafe_unretained id value) {
255 // +1 __bridge_retained, -1 CFAutorelease
256 return value ? (__bridge id)CFAutorelease((__bridge_retained CFTypeRef)value) : nil;
259 + (instancetype)realmWithSharedRealm:(SharedRealm)sharedRealm schema:(RLMSchema *)schema {
260 RLMRealm *realm = [[RLMRealm alloc] initPrivate];
261 realm->_realm = sharedRealm;
262 realm->_dynamic = YES;
263 realm->_schema = schema;
264 realm->_info = RLMSchemaInfo(realm);
265 return RLMAutorelease(realm);
268 REALM_NOINLINE void RLMRealmTranslateException(NSError **error) {
272 catch (RealmFileException const& ex) {
274 case RealmFileException::Kind::PermissionDenied:
275 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFilePermissionDenied, ex), error);
277 case RealmFileException::Kind::IncompatibleLockFile: {
278 NSString *err = @"Realm file is currently open in another process "
279 "which cannot share access with this process. All "
280 "processes sharing a single file must be the same "
281 "architecture. For sharing files between the Realm "
282 "Browser and an iOS simulator, this means that you "
283 "must use a 64-bit simulator.";
284 RLMSetErrorOrThrow(RLMMakeError(RLMErrorIncompatibleLockFile,
285 File::PermissionDenied(err.UTF8String, ex.path())), error);
288 case RealmFileException::Kind::NotFound:
289 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileNotFound, ex), error);
291 case RealmFileException::Kind::Exists:
292 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileExists, ex), error);
294 case RealmFileException::Kind::BadHistoryError: {
295 NSString *err = @"Realm file's history format is incompatible with the "
296 "settings in the configuration object being used to open "
297 "the Realm. Note that Realms configured for sync cannot be "
298 "opened as non-synced Realms, and vice versa. Otherwise, the "
299 "file may be corrupt.";
300 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileAccess,
301 File::AccessError(err.UTF8String, ex.path())), error);
304 case RealmFileException::Kind::AccessError:
305 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileAccess, ex), error);
307 case RealmFileException::Kind::FormatUpgradeRequired:
308 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileFormatUpgradeRequired, ex), error);
311 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, ex), error);
315 catch (AddressSpaceExhausted const &ex) {
316 RLMSetErrorOrThrow(RLMMakeError(RLMErrorAddressSpaceExhausted, ex), error);
318 catch (SchemaMismatchException const& ex) {
319 RLMSetErrorOrThrow(RLMMakeError(RLMErrorSchemaMismatch, ex), error);
321 catch (std::system_error const& ex) {
322 RLMSetErrorOrThrow(RLMMakeError(ex), error);
324 catch (const std::exception &exp) {
325 RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), error);
329 REALM_NOINLINE static void translateSharedGroupOpenException(RLMRealmConfiguration *originalConfiguration, NSError **error) {
333 catch (RealmFileException const& ex) {
335 case RealmFileException::Kind::IncompatibleSyncedRealm: {
336 RLMRealmConfiguration *configuration = [originalConfiguration copy];
337 configuration.fileURL = [NSURL fileURLWithPath:@(ex.path().data())];
338 configuration.readOnly = YES;
340 NSError *intermediateError = RLMMakeError(RLMErrorIncompatibleSyncedFile, ex);
341 NSMutableDictionary *userInfo = [intermediateError.userInfo mutableCopy];
342 userInfo[RLMBackupRealmConfigurationErrorKey] = configuration;
343 NSError *finalError = [NSError errorWithDomain:intermediateError.domain code:intermediateError.code
345 RLMSetErrorOrThrow(finalError, error);
349 RLMRealmTranslateException(error);
354 RLMRealmTranslateException(error);
359 + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
360 bool dynamic = configuration.dynamic;
361 bool cache = configuration.cache;
362 bool readOnly = configuration.readOnly;
365 Realm::Config& config = configuration.config;
367 // try to reuse existing realm first
368 if (cache || dynamic) {
369 if (RLMRealm *realm = RLMGetThreadLocalCachedRealmForPath(config.path)) {
370 auto const& old_config = realm->_realm->config();
371 if (old_config.immutable() != config.immutable()
372 || old_config.read_only_alternative() != config.read_only_alternative()) {
373 @throw RLMException(@"Realm at path '%s' already opened with different read permissions", config.path.c_str());
375 if (old_config.in_memory != config.in_memory) {
376 @throw RLMException(@"Realm at path '%s' already opened with different inMemory settings", config.path.c_str());
378 if (realm->_dynamic != dynamic) {
379 @throw RLMException(@"Realm at path '%s' already opened with different dynamic settings", config.path.c_str());
381 if (old_config.encryption_key != config.encryption_key) {
382 @throw RLMException(@"Realm at path '%s' already opened with different encryption key", config.path.c_str());
384 return RLMAutorelease(realm);
389 configuration = [configuration copy];
390 Realm::Config& config = configuration.config;
392 RLMRealm *realm = [[RLMRealm alloc] initPrivate];
393 realm->_dynamic = dynamic;
395 // protects the realm cache and accessors cache
396 static std::mutex& initLock = *new std::mutex();
397 std::lock_guard<std::mutex> lock(initLock);
400 realm->_realm = Realm::get_shared_realm(config);
403 translateSharedGroupOpenException(configuration, error);
407 // if we have a cached realm on another thread we can skip a few steps and
408 // just grab its schema
410 // ensure that cachedRealm doesn't end up in this thread's autorelease pool
411 if (auto cachedRealm = RLMGetAnyCachedRealmForPath(config.path)) {
412 realm->_realm->set_schema_subset(cachedRealm->_realm->schema());
413 realm->_schema = cachedRealm.schema;
414 realm->_info = cachedRealm->_info.clone(cachedRealm->_realm->schema(), realm);
418 if (realm->_schema) { }
420 realm->_schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()];
421 realm->_info = RLMSchemaInfo(realm);
424 // set/align schema or perform migration if needed
425 RLMSchema *schema = configuration.customSchema ?: RLMSchema.sharedSchema;
427 Realm::MigrationFunction migrationFunction;
428 auto migrationBlock = configuration.migrationBlock;
429 if (migrationBlock && configuration.schemaVersion > 0) {
430 migrationFunction = [=](SharedRealm old_realm, SharedRealm realm, Schema& mutableSchema) {
431 RLMSchema *oldSchema = [RLMSchema dynamicSchemaFromObjectStoreSchema:old_realm->schema()];
432 RLMRealm *oldRealm = [RLMRealm realmWithSharedRealm:old_realm schema:oldSchema];
434 // The destination RLMRealm can't just use the schema from the
435 // SharedRealm because it doesn't have information about whether or
436 // not a class was defined in Swift, which effects how new objects
438 RLMRealm *newRealm = [RLMRealm realmWithSharedRealm:realm schema:schema.copy];
440 [[[RLMMigration alloc] initWithRealm:newRealm oldRealm:oldRealm schema:mutableSchema] execute:migrationBlock];
442 oldRealm->_realm = nullptr;
443 newRealm->_realm = nullptr;
448 realm->_realm->update_schema(schema.objectStoreCopy, config.schema_version,
449 std::move(migrationFunction));
452 RLMRealmTranslateException(error);
456 realm->_schema = schema;
457 realm->_info = RLMSchemaInfo(realm);
458 RLMRealmCreateAccessors(realm.schema);
461 // initializing the schema started a read transaction, so end it
467 RLMCacheRealm(config.path, realm);
471 realm->_realm->m_binding_context = RLMCreateBindingContext(realm);
472 realm->_realm->m_binding_context->realm = realm->_realm;
474 RLMAddSkipBackupAttributeToItemAtPath(config.path + ".management");
475 RLMAddSkipBackupAttributeToItemAtPath(config.path + ".lock");
476 RLMAddSkipBackupAttributeToItemAtPath(config.path + ".note");
479 return RLMAutorelease(realm);
482 + (instancetype)uncachedSchemalessRealmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
483 RLMRealm *realm = [[RLMRealm alloc] initPrivate];
485 realm->_realm = Realm::get_shared_realm(configuration.config);
488 translateSharedGroupOpenException(configuration, error);
494 + (void)resetRealmState {
495 RLMClearRealmCache();
496 realm::_impl::RealmCoordinator::clear_cache();
497 [RLMRealmConfiguration resetRealmConfigurationState];
500 - (void)verifyNotificationsAreSupported:(bool)isCollection {
502 if (_realm->config().immutable()) {
503 @throw RLMException(@"Read-only Realms do not change and do not have change notifications");
505 if (!_realm->can_deliver_notifications()) {
506 @throw RLMException(@"Can only add notification blocks from within runloops.");
508 if (isCollection && _realm->is_in_transaction()) {
509 @throw RLMException(@"Cannot register notification blocks from within write transactions.");
513 - (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block {
515 @throw RLMException(@"The notification block should not be nil");
517 [self verifyNotificationsAreSupported:false];
519 _realm->read_group();
521 if (!_notificationHandlers) {
522 _notificationHandlers = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
525 RLMRealmNotificationToken *token = [[RLMRealmNotificationToken alloc] init];
528 [_notificationHandlers addObject:token];
532 - (void)sendNotifications:(RLMNotification)notification {
533 NSAssert(!_realm->config().immutable(), @"Read-only realms do not have notifications");
534 if (_sendingNotifications) {
537 NSUInteger count = _notificationHandlers.count;
542 _sendingNotifications = true;
543 auto cleanup = realm::util::make_scope_exit([&]() noexcept {
544 _sendingNotifications = false;
547 // call this realm's notification blocks
549 if (auto block = [_notificationHandlers.anyObject block]) {
550 block(notification, self);
554 for (RLMRealmNotificationToken *token in _notificationHandlers.allObjects) {
555 if (auto block = token.block) {
556 block(notification, self);
562 - (RLMRealmConfiguration *)configuration {
563 RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
564 configuration.config = _realm->config();
565 configuration.dynamic = _dynamic;
566 configuration.customSchema = _schema;
567 return configuration;
570 - (void)beginWriteTransaction {
572 _realm->begin_transaction();
574 catch (std::exception &ex) {
575 @throw RLMException(ex);
579 - (void)commitWriteTransaction {
580 [self commitWriteTransaction:nil];
583 - (BOOL)commitWriteTransaction:(NSError **)outError {
585 _realm->commit_transaction();
589 RLMRealmTranslateException(outError);
594 - (BOOL)commitWriteTransactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens error:(NSError **)error {
595 for (RLMNotificationToken *token in tokens) {
596 if (token.realm != self) {
597 @throw RLMException(@"Incorrect Realm: only notifications for the Realm being modified can be skipped.");
599 [token suppressNextNotification];
603 _realm->commit_transaction();
607 RLMRealmTranslateException(error);
612 - (void)transactionWithBlock:(void(^)(void))block {
613 [self transactionWithBlock:block error:nil];
616 - (BOOL)transactionWithBlock:(void(^)(void))block error:(NSError **)outError {
617 [self beginWriteTransaction];
619 if (_realm->is_in_transaction()) {
620 return [self commitWriteTransaction:outError];
625 - (void)cancelWriteTransaction {
627 _realm->cancel_transaction();
629 catch (std::exception &ex) {
630 @throw RLMException(ex);
635 if (_realm->is_in_transaction()) {
636 NSLog(@"WARNING: An RLMRealm instance was invalidated during a write "
637 "transaction and all pending changes have been rolled back.");
640 [self detachAllEnumerators];
642 for (auto& objectInfo : _info) {
643 for (RLMObservationInfo *info : objectInfo.second.observedObjects) {
644 info->willChange(RLMInvalidatedKey);
648 _realm->invalidate();
650 for (auto& objectInfo : _info) {
651 for (RLMObservationInfo *info : objectInfo.second.observedObjects) {
652 info->didChange(RLMInvalidatedKey);
654 objectInfo.second.releaseTable();
658 - (nullable id)resolveThreadSafeReference:(RLMThreadSafeReference *)reference {
659 return [reference resolveReferenceInRealm:self];
663 Replaces all string columns in this Realm with a string enumeration column and compacts the
666 Cannot be called from a write transaction.
668 Compaction will not occur if other `RLMRealm` instances exist.
670 While compaction is in progress, attempts by other threads or processes to open the database will
673 Be warned that resource requirements for compaction is proportional to the amount of live data in
676 Compaction works by writing the database contents to a temporary database file and then replacing
677 the database with the temporary one. The name of the temporary file is formed by appending
678 `.tmp_compaction_space` to the name of the database.
680 @return YES if the compaction succeeded.
683 // compact() automatically ends the read transaction, but we need to clean
684 // up cached state and send invalidated notifications when that happens, so
685 // explicitly end it first unless we're in a write transaction (in which
686 // case compact() will throw an exception)
687 if (!_realm->is_in_transaction()) {
692 return _realm->compact();
694 catch (std::exception const& ex) {
695 @throw RLMException(ex);
701 if (_realm->is_in_transaction()) {
702 [self cancelWriteTransaction];
703 NSLog(@"WARNING: An RLMRealm instance was deallocated during a write transaction and all "
704 "pending changes have been rolled back. Make sure to retain a reference to the "
705 "RLMRealm for the duration of the write transaction.");
711 return _realm->refresh();
714 - (void)addObject:(__unsafe_unretained RLMObject *const)object {
715 RLMAddObjectToRealm(object, self, false);
718 - (void)addObjects:(id<NSFastEnumeration>)objects {
719 for (RLMObject *obj in objects) {
720 if (![obj isKindOfClass:RLMObjectBase.class]) {
721 @throw RLMException(@"Cannot insert objects of type %@ with addObjects:. Only RLMObjects are supported.",
722 NSStringFromClass(obj.class));
724 [self addObject:obj];
728 - (void)addOrUpdateObject:(RLMObject *)object {
729 // verify primary key
730 if (!object.objectSchema.primaryKeyProperty) {
731 @throw RLMException(@"'%@' does not have a primary key and can not be updated", object.objectSchema.className);
734 RLMAddObjectToRealm(object, self, true);
737 - (void)addOrUpdateObjects:(id<NSFastEnumeration>)objects {
738 for (RLMObject *obj in objects) {
739 if (![obj isKindOfClass:RLMObjectBase.class]) {
740 @throw RLMException(@"Cannot add or update objects of type %@ with addOrUpdateObjects:. Only RLMObjects are"
742 NSStringFromClass(obj.class));
744 [self addOrUpdateObject:obj];
748 - (void)deleteObject:(RLMObject *)object {
749 RLMDeleteObjectFromRealm(object, self);
752 - (void)deleteObjects:(id<NSFastEnumeration>)objects {
753 id idObjects = objects;
754 if ([idObjects respondsToSelector:@selector(realm)]
755 && [idObjects respondsToSelector:@selector(deleteObjectsFromRealm)]) {
756 if (self != (RLMRealm *)[idObjects realm]) {
757 @throw RLMException(@"Can only delete objects from the Realm they belong to.");
759 [idObjects deleteObjectsFromRealm];
762 if (auto array = RLMDynamicCast<RLMArray>(objects)) {
763 if (array.type != RLMPropertyTypeObject) {
764 @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.",
765 RLMTypeToString(array.type));
768 for (RLMObject *obj in objects) {
769 if (![obj isKindOfClass:RLMObjectBase.class]) {
770 @throw RLMException(@"Cannot delete objects of type %@ with deleteObjects:. Only RLMObjects can be deleted.",
771 NSStringFromClass(obj.class));
773 RLMDeleteObjectFromRealm(obj, self);
777 - (void)deleteAllObjects {
778 RLMDeleteAllObjectsFromRealm(self);
781 - (RLMResults *)allObjects:(NSString *)objectClassName {
782 return RLMGetObjects(self, objectClassName, nil);
785 - (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat, ... {
787 va_start(args, predicateFormat);
788 RLMResults *results = [self objects:objectClassName where:predicateFormat args:args];
793 - (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat args:(va_list)args {
794 return [self objects:objectClassName withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
797 - (RLMResults *)objects:(NSString *)objectClassName withPredicate:(NSPredicate *)predicate {
798 return RLMGetObjects(self, objectClassName, predicate);
801 - (RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey {
802 return RLMGetObject(self, className, primaryKey);
805 + (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error {
806 RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
808 config.fileURL = fileURL;
809 config.encryptionKey = RLMRealmValidatedEncryptionKey(key);
811 uint64_t version = Realm::get_schema_version(config.config);
812 if (version == realm::ObjectStore::NotVersioned) {
813 RLMSetErrorOrThrow([NSError errorWithDomain:RLMErrorDomain code:RLMErrorFail userInfo:@{NSLocalizedDescriptionKey:@"Cannot open an uninitialized realm in read-only mode"}], error);
818 translateSharedGroupOpenException(config, error);
819 return RLMNotVersioned;
823 + (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
824 if (RLMGetAnyCachedRealmForPath(configuration.config.path)) {
825 @throw RLMException(@"Cannot migrate Realms that are already open.");
828 NSError *localError; // Prevents autorelease
831 success = [RLMRealm realmWithConfiguration:configuration error:&localError] != nil;
833 if (!success && error) {
834 *error = localError; // Must set outside pool otherwise will free anyway
839 - (RLMObject *)createObject:(NSString *)className withValue:(id)value {
840 return (RLMObject *)RLMCreateObjectInRealmWithValue(self, className, value, false);
843 - (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error {
844 key = RLMRealmValidatedEncryptionKey(key);
845 NSString *path = fileURL.path;
848 _realm->write_copy(path.UTF8String, {static_cast<const char *>(key.bytes), key.length});
852 __autoreleasing NSError *dummyError;
856 RLMRealmTranslateException(error);
863 - (void)registerEnumerator:(RLMFastEnumerator *)enumerator {
864 if (!_collectionEnumerators) {
865 _collectionEnumerators = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
867 [_collectionEnumerators addObject:enumerator];
870 - (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator {
871 [_collectionEnumerators removeObject:enumerator];
874 - (void)detachAllEnumerators {
875 for (RLMFastEnumerator *enumerator in _collectionEnumerators) {
878 _collectionEnumerators = nil;