Migration.swift 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 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. /**
  22. The type of a migration block used to migrate a Realm.
  23. - parameter migration: A `Migration` object used to perform the migration. The migration object allows you to
  24. enumerate and alter any existing objects which require migration.
  25. - parameter oldSchemaVersion: The schema version of the Realm being migrated.
  26. */
  27. public typealias MigrationBlock = (_ migration: Migration, _ oldSchemaVersion: UInt64) -> Void
  28. /// An object class used during migrations.
  29. public typealias MigrationObject = DynamicObject
  30. /**
  31. A block type which provides both the old and new versions of an object in the Realm. Object
  32. properties can only be accessed using subscripting.
  33. - parameter oldObject: The object from the original Realm (read-only).
  34. - parameter newObject: The object from the migrated Realm (read-write).
  35. */
  36. public typealias MigrationObjectEnumerateBlock = (_ oldObject: MigrationObject?, _ newObject: MigrationObject?) -> Void
  37. /**
  38. Returns the schema version for a Realm at a given local URL.
  39. - parameter fileURL: Local URL to a Realm file.
  40. - parameter encryptionKey: 64-byte key used to encrypt the file, or `nil` if it is unencrypted.
  41. - throws: An `NSError` that describes the problem.
  42. */
  43. public func schemaVersionAtURL(_ fileURL: URL, encryptionKey: Data? = nil) throws -> UInt64 {
  44. var error: NSError?
  45. let version = RLMRealm.__schemaVersion(at: fileURL, encryptionKey: encryptionKey, error: &error)
  46. guard version != RLMNotVersioned else {
  47. throw error!
  48. }
  49. return version
  50. }
  51. extension Realm {
  52. /**
  53. Performs the given Realm configuration's migration block on a Realm at the given path.
  54. This method is called automatically when opening a Realm for the first time and does not need to be called
  55. explicitly. You can choose to call this method to control exactly when and how migrations are performed.
  56. - parameter configuration: The Realm configuration used to open and migrate the Realm.
  57. */
  58. public static func performMigration(for configuration: Realm.Configuration = Realm.Configuration.defaultConfiguration) throws {
  59. try RLMRealm.performMigration(for: configuration.rlmConfiguration)
  60. }
  61. }
  62. /**
  63. `Migration` instances encapsulate information intended to facilitate a schema migration.
  64. A `Migration` instance is passed into a user-defined `MigrationBlock` block when updating the version of a Realm. This
  65. instance provides access to the old and new database schemas, the objects in the Realm, and provides functionality for
  66. modifying the Realm during the migration.
  67. */
  68. public struct Migration {
  69. // MARK: Properties
  70. /// The old schema, describing the Realm before applying a migration.
  71. public var oldSchema: Schema { return Schema(rlmMigration.oldSchema) }
  72. /// The new schema, describing the Realm after applying a migration.
  73. public var newSchema: Schema { return Schema(rlmMigration.newSchema) }
  74. internal var rlmMigration: RLMMigration
  75. // MARK: Altering Objects During a Migration
  76. /**
  77. Enumerates all the objects of a given type in this Realm, providing both the old and new versions of each object.
  78. Properties on an object can be accessed using subscripting.
  79. - parameter objectClassName: The name of the `Object` class to enumerate.
  80. - parameter block: The block providing both the old and new versions of an object in this Realm.
  81. */
  82. public func enumerateObjects(ofType typeName: String, _ block: MigrationObjectEnumerateBlock) {
  83. rlmMigration.enumerateObjects(typeName) { oldObject, newObject in
  84. block(unsafeBitCast(oldObject, to: MigrationObject.self),
  85. unsafeBitCast(newObject, to: MigrationObject.self))
  86. }
  87. }
  88. /**
  89. Creates and returns an `Object` of type `className` in the Realm being migrated.
  90. The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or
  91. dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing one element for each
  92. managed property. An exception will be thrown if any required properties are not present and those properties were
  93. not defined with default values.
  94. When passing in an `Array` as the `value` argument, all properties must be present, valid and in the same order as
  95. the properties defined in the model.
  96. - parameter className: The name of the `Object` class to create.
  97. - parameter value: The value used to populate the created object.
  98. - returns: The newly created object.
  99. */
  100. @discardableResult
  101. public func create(_ typeName: String, value: Any = [:]) -> MigrationObject {
  102. return unsafeBitCast(rlmMigration.createObject(typeName, withValue: value), to: MigrationObject.self)
  103. }
  104. /**
  105. Deletes an object from a Realm during a migration.
  106. It is permitted to call this method from within the block passed to `enumerate(_:block:)`.
  107. - parameter object: An object to be deleted from the Realm being migrated.
  108. */
  109. public func delete(_ object: MigrationObject) {
  110. rlmMigration.delete(object.unsafeCastToRLMObject())
  111. }
  112. /**
  113. Deletes the data for the class with the given name.
  114. All objects of the given class will be deleted. If the `Object` subclass no longer exists in your program, any
  115. remaining metadata for the class will be removed from the Realm file.
  116. - parameter objectClassName: The name of the `Object` class to delete.
  117. - returns: A Boolean value indicating whether there was any data to delete.
  118. */
  119. @discardableResult
  120. public func deleteData(forType typeName: String) -> Bool {
  121. return rlmMigration.deleteData(forClassName: typeName)
  122. }
  123. /**
  124. Renames a property of the given class from `oldName` to `newName`.
  125. - parameter className: The name of the class whose property should be renamed. This class must be present
  126. in both the old and new Realm schemas.
  127. - parameter oldName: The old name for the property to be renamed. There must not be a property with this name in
  128. the class as defined by the new Realm schema.
  129. - parameter newName: The new name for the property to be renamed. There must not be a property with this name in
  130. the class as defined by the old Realm schema.
  131. */
  132. public func renameProperty(onType typeName: String, from oldName: String, to newName: String) {
  133. rlmMigration.renameProperty(forClass: typeName, oldName: oldName, newName: newName)
  134. }
  135. internal init(_ rlmMigration: RLMMigration) {
  136. self.rlmMigration = rlmMigration
  137. }
  138. }
  139. // MARK: Private Helpers
  140. internal func accessorMigrationBlock(_ migrationBlock: @escaping MigrationBlock) -> RLMMigrationBlock {
  141. return { migration, oldVersion in
  142. // set all accessor classes to MigrationObject
  143. for objectSchema in migration.oldSchema.objectSchema {
  144. objectSchema.accessorClass = MigrationObject.self
  145. // isSwiftClass is always `false` for object schema generated
  146. // from the table, but we need to pretend it's from a swift class
  147. // (even if it isn't) for the accessors to be initialized correctly.
  148. objectSchema.isSwiftClass = true
  149. }
  150. for objectSchema in migration.newSchema.objectSchema {
  151. objectSchema.accessorClass = MigrationObject.self
  152. }
  153. // run migration
  154. migrationBlock(Migration(migration), oldVersion)
  155. }
  156. }