RealmConfiguration.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. #if !swift(>=4.1)
  22. fileprivate extension Sequence {
  23. func compactMap<T>(_ fn: (Self.Iterator.Element) throws -> T?) rethrows -> [T] {
  24. return try flatMap(fn)
  25. }
  26. }
  27. #endif
  28. extension Realm {
  29. /**
  30. A `Configuration` instance describes the different options used to create an instance of a Realm.
  31. `Configuration` instances are just plain Swift structs. Unlike `Realm`s and `Object`s, they can be freely shared
  32. between threads as long as you do not mutate them.
  33. Creating configuration values for class subsets (by setting the `objectClasses` property) can be expensive. Because
  34. of this, you will normally want to cache and reuse a single configuration value for each distinct configuration
  35. rather than creating a new value each time you open a Realm.
  36. */
  37. public struct Configuration {
  38. // MARK: Default Configuration
  39. /**
  40. The default `Configuration` used to create Realms when no configuration is explicitly specified (i.e.
  41. `Realm()`)
  42. */
  43. public static var defaultConfiguration: Configuration {
  44. get {
  45. return fromRLMRealmConfiguration(RLMRealmConfiguration.default())
  46. }
  47. set {
  48. RLMRealmConfiguration.setDefault(newValue.rlmConfiguration)
  49. }
  50. }
  51. // MARK: Initialization
  52. /**
  53. Creates a `Configuration` which can be used to create new `Realm` instances.
  54. - note: The `fileURL`, `inMemoryIdentifier`, and `syncConfiguration` parameters are mutually exclusive. Only
  55. set one of them, or none if you wish to use the default file URL.
  56. - parameter fileURL: The local URL to the Realm file.
  57. - parameter inMemoryIdentifier: A string used to identify a particular in-memory Realm.
  58. - parameter syncConfiguration: For Realms intended to sync with the Realm Object Server, a sync configuration.
  59. - parameter encryptionKey: An optional 64-byte key to use to encrypt the data.
  60. - parameter readOnly: Whether the Realm is read-only (must be true for read-only files).
  61. - parameter schemaVersion: The current schema version.
  62. - parameter migrationBlock: The block which migrates the Realm to the current version.
  63. - parameter deleteRealmIfMigrationNeeded: If `true`, recreate the Realm file with the provided
  64. schema if a migration is required.
  65. - parameter shouldCompactOnLaunch: A block called when opening a Realm for the first time during the
  66. life of a process to determine if it should be compacted before being
  67. returned to the user. It is passed the total file size (data + free space)
  68. and the total bytes used by data in the file.
  69. Return `true ` to indicate that an attempt to compact the file should be made.
  70. The compaction will be skipped if another process is accessing it.
  71. - parameter objectTypes: The subset of `Object` subclasses persisted in the Realm.
  72. */
  73. public init(fileURL: URL? = URL(fileURLWithPath: RLMRealmPathForFile("default.realm"), isDirectory: false),
  74. inMemoryIdentifier: String? = nil,
  75. syncConfiguration: SyncConfiguration? = nil,
  76. encryptionKey: Data? = nil,
  77. readOnly: Bool = false,
  78. schemaVersion: UInt64 = 0,
  79. migrationBlock: MigrationBlock? = nil,
  80. deleteRealmIfMigrationNeeded: Bool = false,
  81. shouldCompactOnLaunch: ((Int, Int) -> Bool)? = nil,
  82. objectTypes: [Object.Type]? = nil) {
  83. self.fileURL = fileURL
  84. if let inMemoryIdentifier = inMemoryIdentifier {
  85. self.inMemoryIdentifier = inMemoryIdentifier
  86. }
  87. if let syncConfiguration = syncConfiguration {
  88. self.syncConfiguration = syncConfiguration
  89. }
  90. self.encryptionKey = encryptionKey
  91. self.readOnly = readOnly
  92. self.schemaVersion = schemaVersion
  93. self.migrationBlock = migrationBlock
  94. self.deleteRealmIfMigrationNeeded = deleteRealmIfMigrationNeeded
  95. self.shouldCompactOnLaunch = shouldCompactOnLaunch
  96. self.objectTypes = objectTypes
  97. }
  98. // MARK: Configuration Properties
  99. /**
  100. A configuration value used to configure a Realm for synchronization with the Realm Object Server. Mutually
  101. exclusive with `inMemoryIdentifier` and `fileURL`.
  102. */
  103. public var syncConfiguration: SyncConfiguration? {
  104. set {
  105. _path = nil
  106. _inMemoryIdentifier = nil
  107. _syncConfiguration = newValue
  108. }
  109. get {
  110. return _syncConfiguration
  111. }
  112. }
  113. private var _syncConfiguration: SyncConfiguration?
  114. /// The local URL of the Realm file. Mutually exclusive with `inMemoryIdentifier` and `syncConfiguration`.
  115. public var fileURL: URL? {
  116. set {
  117. _inMemoryIdentifier = nil
  118. _syncConfiguration = nil
  119. _path = newValue?.path
  120. }
  121. get {
  122. return _path.map { URL(fileURLWithPath: $0) }
  123. }
  124. }
  125. private var _path: String?
  126. /// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL` and
  127. /// `syncConfiguration`.
  128. public var inMemoryIdentifier: String? {
  129. set {
  130. _path = nil
  131. _syncConfiguration = nil
  132. _inMemoryIdentifier = newValue
  133. }
  134. get {
  135. return _inMemoryIdentifier
  136. }
  137. }
  138. private var _inMemoryIdentifier: String?
  139. /// A 64-byte key to use to encrypt the data, or `nil` if encryption is not enabled.
  140. public var encryptionKey: Data?
  141. /**
  142. Whether to open the Realm in read-only mode.
  143. This is required to be able to open Realm files which are not writeable or are in a directory which is not
  144. writeable. This should only be used on files which will not be modified by anyone while they are open, and not
  145. just to get a read-only view of a file which may be written to by another thread or process. Opening in
  146. read-only mode requires disabling Realm's reader/writer coordination, so committing a write transaction from
  147. another process will result in crashes.
  148. */
  149. public var readOnly: Bool = false
  150. /// The current schema version.
  151. public var schemaVersion: UInt64 = 0
  152. /// The block which migrates the Realm to the current version.
  153. public var migrationBlock: MigrationBlock?
  154. /**
  155. Whether to recreate the Realm file with the provided schema if a migration is required. This is the case when
  156. the stored schema differs from the provided schema or the stored schema version differs from the version on
  157. this configuration. Setting this property to `true` deletes the file if a migration would otherwise be required
  158. or executed.
  159. - note: Setting this property to `true` doesn't disable file format migrations.
  160. */
  161. public var deleteRealmIfMigrationNeeded: Bool = false
  162. /**
  163. A block called when opening a Realm for the first time during the
  164. life of a process to determine if it should be compacted before being
  165. returned to the user. It is passed the total file size (data + free space)
  166. and the total bytes used by data in the file.
  167. Return `true ` to indicate that an attempt to compact the file should be made.
  168. The compaction will be skipped if another process is accessing it.
  169. */
  170. public var shouldCompactOnLaunch: ((Int, Int) -> Bool)?
  171. /// The classes managed by the Realm.
  172. public var objectTypes: [Object.Type]? {
  173. set {
  174. self.customSchema = newValue.map { RLMSchema(objectClasses: $0) }
  175. }
  176. get {
  177. return self.customSchema.map { $0.objectSchema.compactMap { $0.objectClass as? Object.Type } }
  178. }
  179. }
  180. /// A custom schema to use for the Realm.
  181. private var customSchema: RLMSchema?
  182. /// If `true`, disables automatic format upgrades when accessing the Realm.
  183. internal var disableFormatUpgrade: Bool = false
  184. // MARK: Private Methods
  185. internal var rlmConfiguration: RLMRealmConfiguration {
  186. let configuration = RLMRealmConfiguration()
  187. if let fileURL = fileURL {
  188. configuration.fileURL = fileURL
  189. } else if let inMemoryIdentifier = inMemoryIdentifier {
  190. configuration.inMemoryIdentifier = inMemoryIdentifier
  191. } else if let syncConfiguration = syncConfiguration {
  192. configuration.syncConfiguration = syncConfiguration.asConfig()
  193. } else {
  194. fatalError("A Realm Configuration must specify a path or an in-memory identifier.")
  195. }
  196. configuration.encryptionKey = self.encryptionKey
  197. configuration.readOnly = self.readOnly
  198. configuration.schemaVersion = self.schemaVersion
  199. configuration.migrationBlock = self.migrationBlock.map { accessorMigrationBlock($0) }
  200. configuration.deleteRealmIfMigrationNeeded = self.deleteRealmIfMigrationNeeded
  201. if let shouldCompactOnLaunch = self.shouldCompactOnLaunch {
  202. configuration.shouldCompactOnLaunch = ObjectiveCSupport.convert(object: shouldCompactOnLaunch)
  203. } else {
  204. configuration.shouldCompactOnLaunch = nil
  205. }
  206. configuration.setCustomSchemaWithoutCopying(self.customSchema)
  207. configuration.disableFormatUpgrade = self.disableFormatUpgrade
  208. return configuration
  209. }
  210. internal static func fromRLMRealmConfiguration(_ rlmConfiguration: RLMRealmConfiguration) -> Configuration {
  211. var configuration = Configuration()
  212. configuration._path = rlmConfiguration.fileURL?.path
  213. configuration._inMemoryIdentifier = rlmConfiguration.inMemoryIdentifier
  214. if let objcSyncConfig = rlmConfiguration.syncConfiguration {
  215. configuration._syncConfiguration = SyncConfiguration(config: objcSyncConfig)
  216. } else {
  217. configuration._syncConfiguration = nil
  218. }
  219. configuration.encryptionKey = rlmConfiguration.encryptionKey
  220. configuration.readOnly = rlmConfiguration.readOnly
  221. configuration.schemaVersion = rlmConfiguration.schemaVersion
  222. configuration.migrationBlock = rlmConfiguration.migrationBlock.map { rlmMigration in
  223. return { migration, schemaVersion in
  224. rlmMigration(migration.rlmMigration, schemaVersion)
  225. }
  226. }
  227. configuration.deleteRealmIfMigrationNeeded = rlmConfiguration.deleteRealmIfMigrationNeeded
  228. configuration.shouldCompactOnLaunch = rlmConfiguration.shouldCompactOnLaunch.map(ObjectiveCSupport.convert)
  229. configuration.customSchema = rlmConfiguration.customSchema
  230. configuration.disableFormatUpgrade = rlmConfiguration.disableFormatUpgrade
  231. return configuration
  232. }
  233. }
  234. }
  235. // MARK: CustomStringConvertible
  236. extension Realm.Configuration: CustomStringConvertible {
  237. /// A human-readable description of the configuration value.
  238. public var description: String {
  239. return gsub(pattern: "\\ARLMRealmConfiguration",
  240. template: "Realm.Configuration",
  241. string: rlmConfiguration.description) ?? ""
  242. }
  243. }