RLMRealmConfiguration.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2015 Realm Inc.
  4. //
  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
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  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.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMRealmConfiguration_Private.h"
  19. #import "RLMEvent.h"
  20. #import "RLMObjectSchema_Private.hpp"
  21. #import "RLMRealm_Private.h"
  22. #import "RLMSchema_Private.hpp"
  23. #import "RLMUtil.hpp"
  24. #import <realm/object-store/schema.hpp>
  25. #import <realm/object-store/shared_realm.hpp>
  26. #if REALM_ENABLE_SYNC
  27. #import "RLMSyncConfiguration_Private.hpp"
  28. #import "RLMUser_Private.hpp"
  29. #import <realm/object-store/sync/sync_manager.hpp>
  30. #import <realm/object-store/util/bson/bson.hpp>
  31. #import <realm/sync/config.hpp>
  32. #else
  33. @class RLMSyncConfiguration;
  34. #endif
  35. static NSString *const c_RLMRealmConfigurationProperties[] = {
  36. @"fileURL",
  37. @"inMemoryIdentifier",
  38. @"encryptionKey",
  39. @"readOnly",
  40. @"schemaVersion",
  41. @"migrationBlock",
  42. @"deleteRealmIfMigrationNeeded",
  43. @"shouldCompactOnLaunch",
  44. @"dynamic",
  45. @"customSchema",
  46. };
  47. static NSString *const c_defaultRealmFileName = @"default.realm";
  48. RLMRealmConfiguration *s_defaultConfiguration;
  49. NSString *RLMRealmPathForFileAndBundleIdentifier(NSString *fileName, NSString *bundleIdentifier) {
  50. return [RLMDefaultDirectoryForBundleIdentifier(bundleIdentifier)
  51. stringByAppendingPathComponent:fileName];
  52. }
  53. NSString *RLMRealmPathForFile(NSString *fileName) {
  54. static NSString *directory = RLMDefaultDirectoryForBundleIdentifier(nil);
  55. return [directory stringByAppendingPathComponent:fileName];
  56. }
  57. @implementation RLMRealmConfiguration {
  58. realm::Realm::Config _config;
  59. RLMSyncErrorReportingBlock _manualClientResetHandler;
  60. }
  61. - (realm::Realm::Config&)configRef {
  62. return _config;
  63. }
  64. - (std::string const&)path {
  65. return _config.path;
  66. }
  67. + (instancetype)defaultConfiguration {
  68. return [[self rawDefaultConfiguration] copy];
  69. }
  70. + (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration {
  71. if (!configuration) {
  72. @throw RLMException(@"Cannot set the default configuration to nil.");
  73. }
  74. @synchronized(c_defaultRealmFileName) {
  75. s_defaultConfiguration = [configuration copy];
  76. }
  77. }
  78. + (RLMRealmConfiguration *)rawDefaultConfiguration {
  79. RLMRealmConfiguration *configuration;
  80. @synchronized(c_defaultRealmFileName) {
  81. if (!s_defaultConfiguration) {
  82. s_defaultConfiguration = [[RLMRealmConfiguration alloc] init];
  83. }
  84. configuration = s_defaultConfiguration;
  85. }
  86. return configuration;
  87. }
  88. + (void)resetRealmConfigurationState {
  89. @synchronized(c_defaultRealmFileName) {
  90. s_defaultConfiguration = nil;
  91. }
  92. }
  93. - (instancetype)init {
  94. self = [super init];
  95. if (self) {
  96. static NSURL *defaultRealmURL = [NSURL fileURLWithPath:RLMRealmPathForFile(c_defaultRealmFileName)];
  97. self.fileURL = defaultRealmURL;
  98. self.schemaVersion = 0;
  99. self.cache = YES;
  100. _config.automatically_handle_backlinks_in_migrations = true;
  101. }
  102. return self;
  103. }
  104. - (instancetype)copyWithZone:(NSZone *)zone {
  105. RLMRealmConfiguration *configuration = [[[self class] allocWithZone:zone] init];
  106. configuration->_config = _config;
  107. configuration->_cache = _cache;
  108. configuration->_dynamic = _dynamic;
  109. configuration->_migrationBlock = _migrationBlock;
  110. configuration->_shouldCompactOnLaunch = _shouldCompactOnLaunch;
  111. configuration->_customSchema = _customSchema;
  112. configuration->_eventConfiguration = _eventConfiguration;
  113. configuration->_migrationObjectClass = _migrationObjectClass;
  114. configuration->_initialSubscriptions = _initialSubscriptions;
  115. configuration->_rerunOnOpen = _rerunOnOpen;
  116. return configuration;
  117. }
  118. - (NSString *)description {
  119. NSMutableString *string = [NSMutableString stringWithFormat:@"%@ {\n", self.class];
  120. for (NSString *key : c_RLMRealmConfigurationProperties) {
  121. NSString *description = [[self valueForKey:key] description];
  122. description = [description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"];
  123. [string appendFormat:@"\t%@ = %@;\n", key, description];
  124. }
  125. return [string stringByAppendingString:@"}"];
  126. }
  127. - (NSURL *)fileURL {
  128. if (_config.in_memory) {
  129. return nil;
  130. }
  131. return [NSURL fileURLWithPath:@(_config.path.c_str())];
  132. }
  133. - (void)setFileURL:(NSURL *)fileURL {
  134. NSString *path = fileURL.path;
  135. if (path.length == 0) {
  136. @throw RLMException(@"Realm path must not be empty");
  137. }
  138. RLMNSStringToStdString(_config.path, path);
  139. _config.in_memory = false;
  140. }
  141. - (NSString *)inMemoryIdentifier {
  142. if (!_config.in_memory) {
  143. return nil;
  144. }
  145. return [@(_config.path.c_str()) lastPathComponent];
  146. }
  147. - (void)setInMemoryIdentifier:(NSString *)inMemoryIdentifier {
  148. if (inMemoryIdentifier.length == 0) {
  149. @throw RLMException(@"In-memory identifier must not be empty");
  150. }
  151. _config.sync_config = nullptr;
  152. _seedFilePath = nil;
  153. RLMNSStringToStdString(_config.path, [NSTemporaryDirectory() stringByAppendingPathComponent:inMemoryIdentifier]);
  154. _config.in_memory = true;
  155. }
  156. - (void)setSeedFilePath:(NSURL *)seedFilePath {
  157. _seedFilePath = seedFilePath;
  158. if (_seedFilePath) {
  159. _config.in_memory = false;
  160. }
  161. }
  162. - (NSData *)encryptionKey {
  163. return _config.encryption_key.empty() ? nil : [NSData dataWithBytes:_config.encryption_key.data() length:_config.encryption_key.size()];
  164. }
  165. - (void)setEncryptionKey:(NSData * __nullable)encryptionKey {
  166. if (NSData *key = RLMRealmValidatedEncryptionKey(encryptionKey)) {
  167. auto bytes = static_cast<const char *>(key.bytes);
  168. _config.encryption_key.assign(bytes, bytes + key.length);
  169. }
  170. else {
  171. _config.encryption_key.clear();
  172. }
  173. }
  174. - (BOOL)readOnly {
  175. return _config.immutable() || _config.read_only();
  176. }
  177. static bool isSync(realm::Realm::Config const& config) {
  178. #if REALM_ENABLE_SYNC
  179. return !!config.sync_config;
  180. #endif
  181. return false;
  182. }
  183. - (void)updateSchemaMode {
  184. if (self.deleteRealmIfMigrationNeeded) {
  185. if (isSync(_config)) {
  186. @throw RLMException(@"Cannot set 'deleteRealmIfMigrationNeeded' when sync is enabled ('syncConfig' is set).");
  187. }
  188. }
  189. else if (self.readOnly) {
  190. _config.schema_mode = isSync(_config) ? realm::SchemaMode::ReadOnly : realm::SchemaMode::Immutable;
  191. }
  192. else if (isSync(_config)) {
  193. if (_customSchema) {
  194. _config.schema_mode = realm::SchemaMode::AdditiveExplicit;
  195. }
  196. else {
  197. _config.schema_mode = realm::SchemaMode::AdditiveDiscovered;
  198. }
  199. }
  200. else {
  201. _config.schema_mode = realm::SchemaMode::Automatic;
  202. }
  203. }
  204. - (void)setReadOnly:(BOOL)readOnly {
  205. if (readOnly) {
  206. if (self.deleteRealmIfMigrationNeeded) {
  207. @throw RLMException(@"Cannot set `readOnly` when `deleteRealmIfMigrationNeeded` is set.");
  208. } else if (self.shouldCompactOnLaunch) {
  209. @throw RLMException(@"Cannot set `readOnly` when `shouldCompactOnLaunch` is set.");
  210. }
  211. _config.schema_mode = isSync(_config) ? realm::SchemaMode::ReadOnly : realm::SchemaMode::Immutable;
  212. }
  213. else if (self.readOnly) {
  214. _config.schema_mode = realm::SchemaMode::Automatic;
  215. [self updateSchemaMode];
  216. }
  217. }
  218. - (uint64_t)schemaVersion {
  219. return _config.schema_version;
  220. }
  221. - (void)setSchemaVersion:(uint64_t)schemaVersion {
  222. if (schemaVersion == RLMNotVersioned) {
  223. @throw RLMException(@"Cannot set schema version to %llu (RLMNotVersioned)", RLMNotVersioned);
  224. }
  225. _config.schema_version = schemaVersion;
  226. }
  227. - (BOOL)deleteRealmIfMigrationNeeded {
  228. return _config.schema_mode == realm::SchemaMode::SoftResetFile;
  229. }
  230. - (void)setDeleteRealmIfMigrationNeeded:(BOOL)deleteRealmIfMigrationNeeded {
  231. if (deleteRealmIfMigrationNeeded) {
  232. if (self.readOnly) {
  233. @throw RLMException(@"Cannot set `deleteRealmIfMigrationNeeded` when `readOnly` is set.");
  234. }
  235. if (isSync(_config)) {
  236. @throw RLMException(@"Cannot set 'deleteRealmIfMigrationNeeded' when sync is enabled ('syncConfig' is set).");
  237. }
  238. _config.schema_mode = realm::SchemaMode::SoftResetFile;
  239. }
  240. else if (self.deleteRealmIfMigrationNeeded) {
  241. _config.schema_mode = realm::SchemaMode::Automatic;
  242. }
  243. }
  244. - (NSArray *)objectClasses {
  245. return [_customSchema.objectSchema valueForKeyPath:@"objectClass"];
  246. }
  247. - (void)setObjectClasses:(NSArray *)objectClasses {
  248. _customSchema = objectClasses ? [RLMSchema schemaWithObjectClasses:objectClasses] : nil;
  249. [self updateSchemaMode];
  250. }
  251. - (NSUInteger)maximumNumberOfActiveVersions {
  252. if (_config.max_number_of_active_versions > std::numeric_limits<NSUInteger>::max()) {
  253. return std::numeric_limits<NSUInteger>::max();
  254. }
  255. return static_cast<NSUInteger>(_config.max_number_of_active_versions);
  256. }
  257. - (void)setMaximumNumberOfActiveVersions:(NSUInteger)maximumNumberOfActiveVersions {
  258. if (maximumNumberOfActiveVersions == 0) {
  259. _config.max_number_of_active_versions = std::numeric_limits<uint_fast64_t>::max();
  260. }
  261. else {
  262. _config.max_number_of_active_versions = maximumNumberOfActiveVersions;
  263. }
  264. }
  265. - (void)setDynamic:(bool)dynamic {
  266. _dynamic = dynamic;
  267. self.cache = !dynamic;
  268. }
  269. - (bool)disableFormatUpgrade {
  270. return _config.disable_format_upgrade;
  271. }
  272. - (void)setDisableFormatUpgrade:(bool)disableFormatUpgrade {
  273. _config.disable_format_upgrade = disableFormatUpgrade;
  274. }
  275. - (realm::SchemaMode)schemaMode {
  276. return _config.schema_mode;
  277. }
  278. - (void)setSchemaMode:(realm::SchemaMode)mode {
  279. _config.schema_mode = mode;
  280. }
  281. - (NSString *)pathOnDisk {
  282. return @(_config.path.c_str());
  283. }
  284. - (void)setShouldCompactOnLaunch:(RLMShouldCompactOnLaunchBlock)shouldCompactOnLaunch {
  285. if (shouldCompactOnLaunch) {
  286. if (_config.immutable()) {
  287. @throw RLMException(@"Cannot set `shouldCompactOnLaunch` when `readOnly` is set.");
  288. }
  289. _config.should_compact_on_launch_function = shouldCompactOnLaunch;
  290. }
  291. else {
  292. _config.should_compact_on_launch_function = nullptr;
  293. }
  294. _shouldCompactOnLaunch = shouldCompactOnLaunch;
  295. }
  296. - (void)setCustomSchemaWithoutCopying:(RLMSchema *)schema {
  297. _customSchema = schema;
  298. }
  299. - (bool)disableAutomaticChangeNotifications {
  300. return !_config.automatic_change_notifications;
  301. }
  302. - (void)setDisableAutomaticChangeNotifications:(bool)disableAutomaticChangeNotifications {
  303. _config.automatic_change_notifications = !disableAutomaticChangeNotifications;
  304. }
  305. #if REALM_ENABLE_SYNC
  306. - (void)setSyncConfiguration:(RLMSyncConfiguration *)syncConfiguration {
  307. if (syncConfiguration == nil) {
  308. _config.sync_config = nullptr;
  309. return;
  310. }
  311. RLMUser *user = syncConfiguration.user;
  312. if (user.state == RLMUserStateRemoved) {
  313. @throw RLMException(@"Cannot set a sync configuration which has an errored-out user.");
  314. }
  315. NSAssert(user.identifier, @"Cannot call this method on a user that doesn't have an identifier.");
  316. _config.in_memory = false;
  317. _config.sync_config = std::make_shared<realm::SyncConfig>(syncConfiguration.rawConfiguration);
  318. _config.path = syncConfiguration.path;
  319. // The manual client reset handler doesn't exist on the raw config,
  320. // so assign it here.
  321. _manualClientResetHandler = syncConfiguration.manualClientResetHandler;
  322. [self updateSchemaMode];
  323. }
  324. - (RLMSyncConfiguration *)syncConfiguration {
  325. if (!_config.sync_config) {
  326. return nil;
  327. }
  328. RLMSyncConfiguration* syncConfig = [[RLMSyncConfiguration alloc] initWithRawConfig:*_config.sync_config path:_config.path];
  329. syncConfig.manualClientResetHandler = _manualClientResetHandler;
  330. return syncConfig;
  331. }
  332. #else // REALM_ENABLE_SYNC
  333. - (RLMSyncConfiguration *)syncConfiguration {
  334. return nil;
  335. }
  336. #endif // REALM_ENABLE_SYNC
  337. - (realm::Realm::Config)config {
  338. auto config = _config;
  339. if (config.sync_config) {
  340. config.sync_config = std::make_shared<realm::SyncConfig>(*config.sync_config);
  341. }
  342. #if REALM_ENABLE_SYNC
  343. if (config.sync_config) {
  344. RLMSetConfigInfoForClientResetCallbacks(*config.sync_config, self);
  345. }
  346. if (_eventConfiguration) {
  347. config.audit_config = [_eventConfiguration auditConfigWithRealmConfiguration:self];
  348. }
  349. #endif
  350. return config;
  351. }
  352. @end