//////////////////////////////////////////////////////////////////////////// // // Copyright 2015 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 "RLMRealmConfiguration_Private.h" #import "RLMObjectSchema_Private.hpp" #import "RLMRealm_Private.h" #import "RLMSchema_Private.hpp" #import "RLMUtil.hpp" #import "schema.hpp" #import "shared_realm.hpp" #import "sync/sync_config.hpp" static NSString *const c_RLMRealmConfigurationProperties[] = { @"fileURL", @"inMemoryIdentifier", @"encryptionKey", @"readOnly", @"schemaVersion", @"migrationBlock", @"deleteRealmIfMigrationNeeded", @"shouldCompactOnLaunch", @"dynamic", @"customSchema", }; static NSString *const c_defaultRealmFileName = @"default.realm"; RLMRealmConfiguration *s_defaultConfiguration; NSString *RLMRealmPathForFileAndBundleIdentifier(NSString *fileName, NSString *bundleIdentifier) { return [RLMDefaultDirectoryForBundleIdentifier(bundleIdentifier) stringByAppendingPathComponent:fileName]; } NSString *RLMRealmPathForFile(NSString *fileName) { static NSString *directory = RLMDefaultDirectoryForBundleIdentifier(nil); return [directory stringByAppendingPathComponent:fileName]; } @implementation RLMRealmConfiguration { realm::Realm::Config _config; } - (realm::Realm::Config&)config { return _config; } + (instancetype)defaultConfiguration { return [[self rawDefaultConfiguration] copy]; } + (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration { if (!configuration) { @throw RLMException(@"Cannot set the default configuration to nil."); } @synchronized(c_defaultRealmFileName) { s_defaultConfiguration = [configuration copy]; } } + (RLMRealmConfiguration *)rawDefaultConfiguration { RLMRealmConfiguration *configuration; @synchronized(c_defaultRealmFileName) { if (!s_defaultConfiguration) { s_defaultConfiguration = [[RLMRealmConfiguration alloc] init]; } configuration = s_defaultConfiguration; } return configuration; } + (void)resetRealmConfigurationState { @synchronized(c_defaultRealmFileName) { s_defaultConfiguration = nil; } } - (instancetype)init { self = [super init]; if (self) { static NSURL *defaultRealmURL = [NSURL fileURLWithPath:RLMRealmPathForFile(c_defaultRealmFileName)]; self.fileURL = defaultRealmURL; self.schemaVersion = 0; self.cache = YES; // We have our own caching of RLMRealm instances, so the ObjectStore // cache is at best pointless, and may result in broken behavior when // a realm::Realm instance outlives the RLMRealm (due to collection // notifiers being in the middle of running when the RLMRealm is // dealloced) and then reused for a new RLMRealm _config.cache = false; } return self; } - (instancetype)copyWithZone:(NSZone *)zone { RLMRealmConfiguration *configuration = [[[self class] allocWithZone:zone] init]; configuration->_config = _config; configuration->_cache = _cache; configuration->_dynamic = _dynamic; configuration->_migrationBlock = _migrationBlock; configuration->_shouldCompactOnLaunch = _shouldCompactOnLaunch; configuration->_customSchema = _customSchema; return configuration; } - (NSString *)description { NSMutableString *string = [NSMutableString stringWithFormat:@"%@ {\n", self.class]; for (NSString *key : c_RLMRealmConfigurationProperties) { NSString *description = [[self valueForKey:key] description]; description = [description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]; [string appendFormat:@"\t%@ = %@;\n", key, description]; } return [string stringByAppendingString:@"}"]; } static void RLMNSStringToStdString(std::string &out, NSString *in) { out.resize([in maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]); if (out.empty()) { return; } NSUInteger size = out.size(); [in getBytes:&out[0] maxLength:size usedLength:&size encoding:NSUTF8StringEncoding options:0 range:{0, in.length} remainingRange:nullptr]; out.resize(size); } - (NSURL *)fileURL { if (_config.in_memory || _config.sync_config) { return nil; } return [NSURL fileURLWithPath:@(_config.path.c_str())]; } - (void)setFileURL:(NSURL *)fileURL { NSString *path = fileURL.path; if (path.length == 0) { @throw RLMException(@"Realm path must not be empty"); } _config.sync_config = nullptr; RLMNSStringToStdString(_config.path, path); _config.in_memory = false; } - (NSString *)inMemoryIdentifier { if (!_config.in_memory) { return nil; } return [@(_config.path.c_str()) lastPathComponent]; } - (void)setInMemoryIdentifier:(NSString *)inMemoryIdentifier { if (inMemoryIdentifier.length == 0) { @throw RLMException(@"In-memory identifier must not be empty"); } _config.sync_config = nullptr; RLMNSStringToStdString(_config.path, [NSTemporaryDirectory() stringByAppendingPathComponent:inMemoryIdentifier]); _config.in_memory = true; } - (NSData *)encryptionKey { return _config.encryption_key.empty() ? nil : [NSData dataWithBytes:_config.encryption_key.data() length:_config.encryption_key.size()]; } - (void)setEncryptionKey:(NSData * __nullable)encryptionKey { if (NSData *key = RLMRealmValidatedEncryptionKey(encryptionKey)) { auto bytes = static_cast(key.bytes); _config.encryption_key.assign(bytes, bytes + key.length); if (_config.sync_config) { auto& sync_encryption_key = self.config.sync_config->realm_encryption_key; sync_encryption_key = std::array(); std::copy_n(_config.encryption_key.begin(), 64, sync_encryption_key->begin()); } } else { _config.encryption_key.clear(); if (_config.sync_config) _config.sync_config->realm_encryption_key = realm::util::none; } } - (BOOL)readOnly { return _config.immutable(); } - (void)setReadOnly:(BOOL)readOnly { if (readOnly) { if (self.deleteRealmIfMigrationNeeded) { @throw RLMException(@"Cannot set `readOnly` when `deleteRealmIfMigrationNeeded` is set."); } else if (self.shouldCompactOnLaunch) { @throw RLMException(@"Cannot set `readOnly` when `shouldCompactOnLaunch` is set."); } _config.schema_mode = realm::SchemaMode::Immutable; } else if (self.readOnly) { _config.schema_mode = realm::SchemaMode::Automatic; } } - (uint64_t)schemaVersion { return _config.schema_version; } - (void)setSchemaVersion:(uint64_t)schemaVersion { if (schemaVersion == RLMNotVersioned) { @throw RLMException(@"Cannot set schema version to %llu (RLMNotVersioned)", RLMNotVersioned); } _config.schema_version = schemaVersion; } - (BOOL)deleteRealmIfMigrationNeeded { return _config.schema_mode == realm::SchemaMode::ResetFile; } - (void)setDeleteRealmIfMigrationNeeded:(BOOL)deleteRealmIfMigrationNeeded { if (deleteRealmIfMigrationNeeded) { if (self.readOnly) { @throw RLMException(@"Cannot set `deleteRealmIfMigrationNeeded` when `readOnly` is set."); } _config.schema_mode = realm::SchemaMode::ResetFile; } else if (self.deleteRealmIfMigrationNeeded) { _config.schema_mode = realm::SchemaMode::Automatic; } } - (NSArray *)objectClasses { return [_customSchema.objectSchema valueForKeyPath:@"objectClass"]; } - (void)setObjectClasses:(NSArray *)objectClasses { self.customSchema = [RLMSchema schemaWithObjectClasses:objectClasses]; } - (void)setDynamic:(bool)dynamic { _dynamic = dynamic; self.cache = !dynamic; } - (bool)disableFormatUpgrade { return _config.disable_format_upgrade; } - (void)setDisableFormatUpgrade:(bool)disableFormatUpgrade { _config.disable_format_upgrade = disableFormatUpgrade; } - (realm::SchemaMode)schemaMode { return _config.schema_mode; } - (void)setSchemaMode:(realm::SchemaMode)mode { _config.schema_mode = mode; } - (NSString *)pathOnDisk { return @(_config.path.c_str()); } - (void)setShouldCompactOnLaunch:(RLMShouldCompactOnLaunchBlock)shouldCompactOnLaunch { if (shouldCompactOnLaunch) { if (self.readOnly) { @throw RLMException(@"Cannot set `shouldCompactOnLaunch` when `readOnly` is set."); } _config.should_compact_on_launch_function = [=](size_t totalBytes, size_t usedBytes) { return shouldCompactOnLaunch(totalBytes, usedBytes); }; } else { _config.should_compact_on_launch_function = nullptr; } _shouldCompactOnLaunch = shouldCompactOnLaunch; } - (void)setCustomSchemaWithoutCopying:(RLMSchema *)schema { _customSchema = schema; } @end