RealmConfiguration.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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 Foundation
  19. import Realm
  20. import Realm.Private
  21. extension Realm {
  22. /**
  23. A `Configuration` instance describes the different options used to create an instance of a Realm.
  24. `Configuration` instances are just plain Swift structs. Unlike `Realm`s and `Object`s, they can be freely shared
  25. between threads as long as you do not mutate them.
  26. Creating configuration values for class subsets (by setting the `objectClasses` property) can be expensive. Because
  27. of this, you will normally want to cache and reuse a single configuration value for each distinct configuration
  28. rather than creating a new value each time you open a Realm.
  29. */
  30. public struct Configuration {
  31. // MARK: Default Configuration
  32. /**
  33. The default `Configuration` used to create Realms when no configuration is explicitly specified (i.e.
  34. `Realm()`)
  35. */
  36. public static var defaultConfiguration: Configuration {
  37. get {
  38. return fromRLMRealmConfiguration(RLMRealmConfiguration.default())
  39. }
  40. set {
  41. RLMRealmConfiguration.setDefault(newValue.rlmConfiguration)
  42. }
  43. }
  44. // MARK: Initialization
  45. /**
  46. Creates a `Configuration` which can be used to create new `Realm` instances.
  47. - note: The `fileURL`, `inMemoryIdentifier`, and `syncConfiguration` parameters are mutually exclusive. Only
  48. set one of them, or none if you wish to use the default file URL.
  49. - parameter fileURL: The local URL to the Realm file.
  50. - parameter inMemoryIdentifier: A string used to identify a particular in-memory Realm.
  51. - parameter syncConfiguration: For Realms intended to sync with the Realm Object Server, a sync configuration.
  52. - parameter encryptionKey: An optional 64-byte key to use to encrypt the data.
  53. - parameter readOnly: Whether the Realm is read-only (must be true for read-only files).
  54. - parameter schemaVersion: The current schema version.
  55. - parameter migrationBlock: The block which migrates the Realm to the current version.
  56. - parameter deleteRealmIfMigrationNeeded: If `true`, recreate the Realm file with the provided
  57. schema if a migration is required.
  58. - parameter shouldCompactOnLaunch: A block called when opening a Realm for the first time during the
  59. life of a process to determine if it should be compacted before being
  60. returned to the user. It is passed the total file size (data + free space)
  61. and the total bytes used by data in the file.
  62. Return `true ` to indicate that an attempt to compact the file should be made.
  63. The compaction will be skipped if another process is accessing it.
  64. - parameter objectTypes: The subset of `Object` subclasses persisted in the Realm.
  65. */
  66. public init(fileURL: URL? = URL(fileURLWithPath: RLMRealmPathForFile("default.realm"), isDirectory: false),
  67. inMemoryIdentifier: String? = nil,
  68. syncConfiguration: SyncConfiguration? = nil,
  69. encryptionKey: Data? = nil,
  70. readOnly: Bool = false,
  71. schemaVersion: UInt64 = 0,
  72. migrationBlock: MigrationBlock? = nil,
  73. deleteRealmIfMigrationNeeded: Bool = false,
  74. shouldCompactOnLaunch: ((Int, Int) -> Bool)? = nil,
  75. objectTypes: [Object.Type]? = nil) {
  76. self.fileURL = fileURL
  77. if let inMemoryIdentifier = inMemoryIdentifier {
  78. self.inMemoryIdentifier = inMemoryIdentifier
  79. }
  80. if let syncConfiguration = syncConfiguration {
  81. self.syncConfiguration = syncConfiguration
  82. }
  83. self.encryptionKey = encryptionKey
  84. self.readOnly = readOnly
  85. self.schemaVersion = schemaVersion
  86. self.migrationBlock = migrationBlock
  87. self.deleteRealmIfMigrationNeeded = deleteRealmIfMigrationNeeded
  88. self.shouldCompactOnLaunch = shouldCompactOnLaunch
  89. self.objectTypes = objectTypes
  90. }
  91. // MARK: Configuration Properties
  92. /**
  93. A configuration value used to configure a Realm for synchronization with the Realm Object Server. Mutually
  94. exclusive with `inMemoryIdentifier`.
  95. */
  96. public var syncConfiguration: SyncConfiguration? {
  97. set {
  98. _inMemoryIdentifier = nil
  99. _syncConfiguration = newValue
  100. }
  101. get {
  102. return _syncConfiguration
  103. }
  104. }
  105. private var _syncConfiguration: SyncConfiguration?
  106. /// The local URL of the Realm file. Mutually exclusive with `inMemoryIdentifier`.
  107. public var fileURL: URL? {
  108. set {
  109. _inMemoryIdentifier = nil
  110. _path = newValue?.path
  111. }
  112. get {
  113. return _path.map { URL(fileURLWithPath: $0) }
  114. }
  115. }
  116. private var _path: String?
  117. /// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL` and
  118. /// `syncConfiguration`.
  119. public var inMemoryIdentifier: String? {
  120. set {
  121. _path = nil
  122. _syncConfiguration = nil
  123. _inMemoryIdentifier = newValue
  124. }
  125. get {
  126. return _inMemoryIdentifier
  127. }
  128. }
  129. private var _inMemoryIdentifier: String?
  130. /// A 64-byte key to use to encrypt the data, or `nil` if encryption is not enabled.
  131. public var encryptionKey: Data?
  132. /**
  133. Whether to open the Realm in read-only mode.
  134. For non-synchronized Realms, this is required to be able to open Realm files which are not
  135. writeable or are in a directory which is not writeable. This should only be used on files
  136. which will not be modified by anyone while they are open, and not just to get a read-only
  137. view of a file which may be written to by another thread or process. Opening in read-only
  138. mode requires disabling Realm's reader/writer coordination, so committing a write
  139. transaction from another process will result in crashes.
  140. Syncronized Realms must always be writeable (as otherwise no synchronization could happen),
  141. and this instead merely disallows performing write transactions on the Realm. In addition,
  142. it will skip some automatic writes made to the Realm, such as to initialize the Realm's
  143. schema. Setting `readOnly = YES` is not strictly required for Realms which the sync user
  144. does not have write access to, but is highly recommended as it will improve error reporting
  145. and catch some errors earlier.
  146. Realms using query-based sync cannot be opened in read-only mode.
  147. */
  148. public var readOnly: Bool = false
  149. /// The current schema version.
  150. public var schemaVersion: UInt64 = 0
  151. /// The block which migrates the Realm to the current version.
  152. public var migrationBlock: MigrationBlock?
  153. /**
  154. Whether to recreate the Realm file with the provided schema if a migration is required. This is the case when
  155. the stored schema differs from the provided schema or the stored schema version differs from the version on
  156. this configuration. Setting this property to `true` deletes the file if a migration would otherwise be required
  157. or executed.
  158. - note: Setting this property to `true` doesn't disable file format migrations.
  159. */
  160. public var deleteRealmIfMigrationNeeded: Bool = false
  161. /**
  162. A block called when opening a Realm for the first time during the
  163. life of a process to determine if it should be compacted before being
  164. returned to the user. It is passed the total file size (data + free space)
  165. and the total bytes used by data in the file.
  166. Return `true ` to indicate that an attempt to compact the file should be made.
  167. The compaction will be skipped if another process is accessing it.
  168. */
  169. public var shouldCompactOnLaunch: ((Int, Int) -> Bool)?
  170. /// The classes managed by the Realm.
  171. public var objectTypes: [Object.Type]? {
  172. set {
  173. self.customSchema = newValue.map { RLMSchema(objectClasses: $0) }
  174. }
  175. get {
  176. return self.customSchema.map { $0.objectSchema.compactMap { $0.objectClass as? Object.Type } }
  177. }
  178. }
  179. /**
  180. The maximum number of live versions in the Realm file before an exception will
  181. be thrown when attempting to start a write transaction.
  182. Realm provides MVCC snapshot isolation, meaning that writes on one thread do
  183. not overwrite data being read on another thread, and instead write a new copy
  184. of that data. When a Realm refreshes it updates to the latest version of the
  185. data and releases the old versions, allowing them to be overwritten by
  186. subsequent write transactions.
  187. Under normal circumstances this is not a problem, but if the number of active
  188. versions grow too large, it will have a negative effect on the filesize on
  189. disk. This can happen when performing writes on many different threads at
  190. once, when holding on to frozen objects for an extended time, or when
  191. performing long operations on background threads which do not allow the Realm
  192. to refresh.
  193. Setting this property to a non-zero value makes it so that exceeding the set
  194. number of versions will instead throw an exception. This can be used with a
  195. low value during development to help identify places that may be problematic,
  196. or in production use to cause the app to crash rather than produce a Realm
  197. file which is too large to be oened.
  198. */
  199. public var maximumNumberOfActiveVersions: UInt?
  200. /// A custom schema to use for the Realm.
  201. private var customSchema: RLMSchema?
  202. /// If `true`, disables automatic format upgrades when accessing the Realm.
  203. internal var disableFormatUpgrade: Bool = false
  204. // MARK: Private Methods
  205. internal var rlmConfiguration: RLMRealmConfiguration {
  206. let configuration = RLMRealmConfiguration()
  207. if let syncConfiguration = syncConfiguration {
  208. configuration.syncConfiguration = syncConfiguration.asConfig()
  209. }
  210. if let fileURL = fileURL {
  211. configuration.fileURL = fileURL
  212. } else if let inMemoryIdentifier = inMemoryIdentifier {
  213. configuration.inMemoryIdentifier = inMemoryIdentifier
  214. } else if syncConfiguration == nil {
  215. fatalError("A Realm Configuration must specify a path or an in-memory identifier.")
  216. }
  217. configuration.encryptionKey = self.encryptionKey
  218. configuration.readOnly = self.readOnly
  219. configuration.schemaVersion = self.schemaVersion
  220. configuration.migrationBlock = self.migrationBlock.map { accessorMigrationBlock($0) }
  221. configuration.deleteRealmIfMigrationNeeded = self.deleteRealmIfMigrationNeeded
  222. if let shouldCompactOnLaunch = self.shouldCompactOnLaunch {
  223. configuration.shouldCompactOnLaunch = ObjectiveCSupport.convert(object: shouldCompactOnLaunch)
  224. } else {
  225. configuration.shouldCompactOnLaunch = nil
  226. }
  227. configuration.setCustomSchemaWithoutCopying(self.customSchema)
  228. configuration.disableFormatUpgrade = self.disableFormatUpgrade
  229. configuration.maximumNumberOfActiveVersions = self.maximumNumberOfActiveVersions ?? 0
  230. return configuration
  231. }
  232. internal static func fromRLMRealmConfiguration(_ rlmConfiguration: RLMRealmConfiguration) -> Configuration {
  233. var configuration = Configuration()
  234. configuration._path = rlmConfiguration.fileURL?.path
  235. configuration._inMemoryIdentifier = rlmConfiguration.inMemoryIdentifier
  236. if let objcSyncConfig = rlmConfiguration.syncConfiguration {
  237. configuration._syncConfiguration = SyncConfiguration(config: objcSyncConfig)
  238. } else {
  239. configuration._syncConfiguration = nil
  240. }
  241. configuration.encryptionKey = rlmConfiguration.encryptionKey
  242. configuration.readOnly = rlmConfiguration.readOnly
  243. configuration.schemaVersion = rlmConfiguration.schemaVersion
  244. configuration.migrationBlock = rlmConfiguration.migrationBlock.map { rlmMigration in
  245. return { migration, schemaVersion in
  246. rlmMigration(migration.rlmMigration, schemaVersion)
  247. }
  248. }
  249. configuration.deleteRealmIfMigrationNeeded = rlmConfiguration.deleteRealmIfMigrationNeeded
  250. configuration.shouldCompactOnLaunch = rlmConfiguration.shouldCompactOnLaunch.map(ObjectiveCSupport.convert)
  251. configuration.customSchema = rlmConfiguration.customSchema
  252. configuration.disableFormatUpgrade = rlmConfiguration.disableFormatUpgrade
  253. configuration.maximumNumberOfActiveVersions = rlmConfiguration.maximumNumberOfActiveVersions
  254. return configuration
  255. }
  256. }
  257. }
  258. // MARK: CustomStringConvertible
  259. extension Realm.Configuration: CustomStringConvertible {
  260. /// A human-readable description of the configuration value.
  261. public var description: String {
  262. return gsub(pattern: "\\ARLMRealmConfiguration",
  263. template: "Realm.Configuration",
  264. string: rlmConfiguration.description) ?? ""
  265. }
  266. }