1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2014 Realm Inc.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 ////////////////////////////////////////////////////////////////////////////
24 A `Realm` instance (also referred to as "a Realm") represents a Realm database.
26 Realms can either be stored on disk (see `init(path:)`) or in memory (see `Configuration`).
28 `Realm` instances are cached internally, and constructing equivalent `Realm` objects (for example, by using the same
29 path or identifier) produces limited overhead.
31 If you specifically want to ensure a `Realm` instance is destroyed (for example, if you wish to open a Realm, check
32 some property, and then possibly delete the Realm file and re-open it), place the code which uses the Realm within an
33 `autoreleasepool {}` and ensure you have no other strong references to it.
35 - warning: `Realm` instances are not thread safe and cannot be shared across threads or dispatch queues. You must
36 construct a new instance for each thread in which a Realm will be accessed. For dispatch queues, this means
37 that you must construct a new instance in each block which is dispatched, as a queue is not guaranteed to
38 run all of its blocks on the same thread.
40 public final class Realm {
44 /// The `Schema` used by the Realm.
45 public var schema: Schema { return Schema(rlmRealm.schema) }
47 /// The `Configuration` value that was used to create the `Realm` instance.
48 public var configuration: Configuration { return Configuration.fromRLMRealmConfiguration(rlmRealm.configuration) }
50 /// Indicates if the Realm contains any objects.
51 public var isEmpty: Bool { return rlmRealm.isEmpty }
56 Obtains an instance of the default Realm.
58 The default Realm is persisted as *default.realm* under the *Documents* directory of your Application on iOS, and
59 in your application's *Application Support* directory on OS X.
61 The default Realm is created using the default `Configuration`, which can be changed by setting the
62 `Realm.Configuration.defaultConfiguration` property to a new value.
64 - throws: An `NSError` if the Realm could not be initialized.
66 public convenience init() throws {
67 let rlmRealm = try RLMRealm(configuration: RLMRealmConfiguration.default())
72 Obtains a `Realm` instance with the given configuration.
74 - parameter configuration: A configuration value to use when creating the Realm.
76 - throws: An `NSError` if the Realm could not be initialized.
78 public convenience init(configuration: Configuration) throws {
79 let rlmRealm = try RLMRealm(configuration: configuration.rlmConfiguration)
84 Obtains a `Realm` instance persisted at a specified file URL.
86 - parameter fileURL: The local URL of the file the Realm should be saved at.
88 - throws: An `NSError` if the Realm could not be initialized.
90 public convenience init(fileURL: URL) throws {
91 var configuration = Configuration.defaultConfiguration
92 configuration.fileURL = fileURL
93 try self.init(configuration: configuration)
99 Asynchronously open a Realm and deliver it to a block on the given queue.
101 Opening a Realm asynchronously will perform all work needed to get the Realm to
102 a usable state (such as running potentially time-consuming migrations) on a
103 background thread before dispatching to the given queue. In addition,
104 synchronized Realms wait for all remote content available at the time the
105 operation began to be downloaded and available locally.
107 - parameter configuration: A configuration object to use when opening the Realm.
108 - parameter callbackQueue: The dispatch queue on which the callback should be run.
109 - parameter callback: A callback block. If the Realm was successfully opened, an
110 it will be passed in as an argument.
111 Otherwise, a `Swift.Error` describing what went wrong will be
112 passed to the block instead.
114 - note: The returned Realm is confined to the thread on which it was created.
115 Because GCD does not guarantee that queues will always use the same
116 thread, accessing the returned Realm outside the callback block (even if
117 accessed from `callbackQueue`) is unsafe.
119 public static func asyncOpen(configuration: Realm.Configuration = .defaultConfiguration,
120 callbackQueue: DispatchQueue = .main,
121 callback: @escaping (Realm?, Swift.Error?) -> Void) {
122 RLMRealm.asyncOpen(with: configuration.rlmConfiguration, callbackQueue: callbackQueue) { rlmRealm, error in
123 callback(rlmRealm.flatMap(Realm.init), error)
127 // MARK: Transactions
130 Performs actions contained within the given block inside a write transaction.
132 If the block throws an error, the transaction will be canceled and any
133 changes made before the error will be rolled back.
135 Only one write transaction can be open at a time for each Realm file. Write
136 transactions cannot be nested, and trying to begin a write transaction on a
137 Realm which is already in a write transaction will throw an exception.
138 Calls to `write` from `Realm` instances for the same Realm file in other
139 threads or other processes will block until the current write transaction
140 completes or is cancelled.
142 Before beginning the write transaction, `write` updates the `Realm`
143 instance to the latest Realm version, as if `refresh()` had been called,
144 and generates notifications if applicable. This has no effect if the Realm
145 was already up to date.
147 - parameter block: The block containing actions to perform.
149 - throws: An `NSError` if the transaction could not be completed successfully.
150 If `block` throws, the function throws the propagated `ErrorType` instead.
152 public func write(_ block: (() throws -> Void)) throws {
157 if isInWriteTransaction { cancelWrite() }
160 if isInWriteTransaction { try commitWrite() }
164 Begins a write transaction on the Realm.
166 Only one write transaction can be open at a time for each Realm file. Write
167 transactions cannot be nested, and trying to begin a write transaction on a
168 Realm which is already in a write transaction will throw an exception.
169 Calls to `beginWrite` from `Realm` instances for the same Realm file in
170 other threads or other processes will block until the current write
171 transaction completes or is cancelled.
173 Before beginning the write transaction, `beginWrite` updates the `Realm`
174 instance to the latest Realm version, as if `refresh()` had been called,
175 and generates notifications if applicable. This has no effect if the Realm
176 was already up to date.
178 It is rarely a good idea to have write transactions span multiple cycles of
179 the run loop, but if you do wish to do so you will need to ensure that the
180 Realm participating in the write transaction is kept alive until the write
181 transaction is committed.
183 public func beginWrite() {
184 rlmRealm.beginWriteTransaction()
188 Commits all write operations in the current write transaction, and ends
191 After saving the changes and completing the write transaction, all
192 notification blocks registered on this specific `Realm` instance are called
193 synchronously. Notification blocks for `Realm` instances on other threads
194 and blocks registered for any Realm collection (including those on the
195 current thread) are scheduled to be called synchronously.
197 You can skip notifiying specific notification blocks about the changes made
198 in this write transaction by passing in their associated notification
199 tokens. This is primarily useful when the write transaction is saving
200 changes already made in the UI and you do not want to have the notification
201 block attempt to re-apply the same changes.
203 The tokens passed to this function must be for notifications for this Realm
204 which were added on the same thread as the write transaction is being
205 performed on. Notifications for different threads cannot be skipped using
208 - warning: This method may only be called during a write transaction.
210 - throws: An `NSError` if the transaction could not be written due to
211 running out of disk space or other i/o errors.
213 public func commitWrite(withoutNotifying tokens: [NotificationToken] = []) throws {
214 try rlmRealm.commitWriteTransactionWithoutNotifying(tokens)
218 Reverts all writes made in the current write transaction and ends the transaction.
220 This rolls back all objects in the Realm to the state they were in at the
221 beginning of the write transaction, and then ends the transaction.
223 This restores the data for deleted objects, but does not revive invalidated
224 object instances. Any `Object`s which were added to the Realm will be
225 invalidated rather than becoming unmanaged.
227 Given the following code:
230 let oldObject = objects(ObjectType).first!
231 let newObject = ObjectType()
235 realm.delete(oldObject)
239 Both `oldObject` and `newObject` will return `true` for `isInvalidated`,
240 but re-running the query which provided `oldObject` will once again return
243 KVO observers on any objects which were modified during the transaction
244 will be notified about the change back to their initial values, but no
245 other notifcations are produced by a cancelled write transaction.
247 - warning: This method may only be called during a write transaction.
249 public func cancelWrite() {
250 rlmRealm.cancelWriteTransaction()
254 Indicates whether the Realm is currently in a write transaction.
256 - warning: Do not simply check this property and then start a write transaction whenever an object needs to be
257 created, updated, or removed. Doing so might cause a large number of write transactions to be created,
258 degrading performance. Instead, always prefer performing multiple updates during a single transaction.
260 public var isInWriteTransaction: Bool {
261 return rlmRealm.inWriteTransaction
264 // MARK: Adding and Creating objects
267 Adds or updates an existing object into the Realm.
269 Only pass `true` to `update` if the object has a primary key. If no object exists in the Realm with the same
270 primary key value, the object is inserted. Otherwise, the existing object is updated with any changed values.
272 When added, all child relationships referenced by this object will also be added to the Realm if they are not
273 already in it. If the object or any related objects are already being managed by a different Realm an error will be
274 thrown. Instead, use one of the `create` functions to insert a copy of a managed object into a different Realm.
276 The object to be added must be valid and cannot have been previously deleted from a Realm (i.e. `isInvalidated`
279 - parameter object: The object to be added to this Realm.
280 - parameter update: If `true`, the Realm will try to find an existing copy of the object (with the same primary
281 key), and update it. Otherwise, the object will be added.
283 public func add(_ object: Object, update: Bool = false) {
284 if update && object.objectSchema.primaryKeyProperty == nil {
285 throwRealmException("'\(object.objectSchema.className)' does not have a primary key and can not be updated")
287 RLMAddObjectToRealm(object, rlmRealm, update)
291 Adds or updates all the objects in a collection into the Realm.
293 - see: `add(_:update:)`
295 - warning: This method may only be called during a write transaction.
297 - parameter objects: A sequence which contains objects to be added to the Realm.
298 - parameter update: If `true`, objects that are already in the Realm will be updated instead of added anew.
300 public func add<S: Sequence>(_ objects: S, update: Bool = false) where S.Iterator.Element: Object {
302 add(obj, update: update)
307 Creates or updates a Realm object with a given value, adding it to the Realm and returning it.
309 You may only pass `true` to `update` if the object has a primary key. If no object exists
310 in the Realm with the same primary key value, the object is inserted. Otherwise, the
311 existing object is updated with any changed values.
313 The `value` argument can be a Realm object, a key-value coding compliant object, an array
314 or dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing
315 one element for each managed property. Do not pass in a `LinkingObjects` instance, either
316 by itself or as a member of a collection.
318 If the object is being created, all required properties that were not defined with default
319 values must be given initial values through the `value` argument. Otherwise, an Objective-C
320 exception will be thrown.
322 If the object is being updated, all properties defined in its schema will be set by copying
323 from `value` using key-value coding. If the `value` argument does not respond to `value(forKey:)`
324 for a given property name (or getter name, if defined), that value will remain untouched.
325 Nullable properties on the object can be set to nil by using `NSNull` as the updated value,
326 or (if you are passing in an instance of an `Object` subclass) setting the corresponding
327 property on `value` to nil.
329 If the `value` argument is an array, all properties must be present, valid and in the same
330 order as the properties defined in the model.
332 - warning: This method may only be called during a write transaction.
334 - parameter type: The type of the object to create.
335 - parameter value: The value used to populate the object.
336 - parameter update: If `true`, the Realm will try to find an existing copy of the object (with the same primary
337 key), and update it. Otherwise, the object will be added.
339 - returns: The newly created object.
342 public func create<T: Object>(_ type: T.Type, value: Any = [:], update: Bool = false) -> T {
343 let typeName = (type as Object.Type).className()
344 if update && schema[typeName]?.primaryKeyProperty == nil {
345 throwRealmException("'\(typeName)' does not have a primary key and can not be updated")
347 return unsafeDowncast(RLMCreateObjectInRealmWithValue(rlmRealm, typeName, value, update), to: T.self)
351 This method is useful only in specialized circumstances, for example, when building
352 components that integrate with Realm. If you are simply building an app on Realm, it is
353 recommended to use the typed method `create(_:value:update:)`.
355 Creates or updates an object with the given class name and adds it to the `Realm`, populating
356 the object with the given value.
358 You may only pass `true` to `update` if the object has a primary key. If no object exists
359 in the Realm with the same primary key value, the object is inserted. Otherwise, the
360 existing object is updated with any changed values.
362 The `value` argument can be a Realm object, a key-value coding compliant object, an array
363 or dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing
364 one element for each managed property. Do not pass in a `LinkingObjects` instance, either
365 by itself or as a member of a collection.
367 If the object is being created, all required properties that were not defined with default
368 values must be given initial values through the `value` argument. Otherwise, an Objective-C
369 exception will be thrown.
371 If the object is being updated, all properties defined in its schema will be set by copying
372 from `value` using key-value coding. If the `value` argument does not respond to `value(forKey:)`
373 for a given property name (or getter name, if defined), that value will remain untouched.
374 Nullable properties on the object can be set to nil by using `NSNull` as the updated value.
376 If the `value` argument is an array, all properties must be present, valid and in the same
377 order as the properties defined in the model.
379 - warning: This method can only be called during a write transaction.
381 - parameter className: The class name of the object to create.
382 - parameter value: The value used to populate the object.
383 - parameter update: If true will try to update existing objects with the same primary key.
385 - returns: The created object.
390 public func dynamicCreate(_ typeName: String, value: Any = [:], update: Bool = false) -> DynamicObject {
391 if update && schema[typeName]?.primaryKeyProperty == nil {
392 throwRealmException("'\(typeName)' does not have a primary key and can not be updated")
394 return noWarnUnsafeBitCast(RLMCreateObjectInRealmWithValue(rlmRealm, typeName, value, update),
395 to: DynamicObject.self)
398 // MARK: Deleting objects
401 Deletes an object from the Realm. Once the object is deleted it is considered invalidated.
403 - warning: This method may only be called during a write transaction.
405 - parameter object: The object to be deleted.
407 public func delete(_ object: Object) {
408 RLMDeleteObjectFromRealm(object, rlmRealm)
412 Deletes zero or more objects from the Realm.
414 Do not pass in a slice to a `Results` or any other auto-updating Realm collection
415 type (for example, the type returned by the Swift `suffix(_:)` standard library
416 method). Instead, make a copy of the objects to delete using `Array()`, and pass
417 that instead. Directly passing in a view into an auto-updating collection may
418 result in 'index out of bounds' exceptions being thrown.
420 - warning: This method may only be called during a write transaction.
422 - parameter objects: The objects to be deleted. This can be a `List<Object>`,
423 `Results<Object>`, or any other Swift `Sequence` whose
424 elements are `Object`s (subject to the caveats above).
426 public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: Object {
433 Deletes zero or more objects from the Realm.
435 - warning: This method may only be called during a write transaction.
437 - parameter objects: A list of objects to delete.
441 public func delete<Element: Object>(_ objects: List<Element>) {
442 rlmRealm.deleteObjects(objects._rlmArray)
446 Deletes zero or more objects from the Realm.
448 - warning: This method may only be called during a write transaction.
450 - parameter objects: A `Results` containing the objects to be deleted.
454 public func delete<Element: Object>(_ objects: Results<Element>) {
455 rlmRealm.deleteObjects(objects.rlmResults)
459 Deletes all objects from the Realm.
461 - warning: This method may only be called during a write transaction.
463 public func deleteAll() {
464 RLMDeleteAllObjectsFromRealm(rlmRealm)
467 // MARK: Object Retrieval
470 Returns all objects of the given type stored in the Realm.
472 - parameter type: The type of the objects to be returned.
474 - returns: A `Results` containing the objects.
476 public func objects<Element: Object>(_ type: Element.Type) -> Results<Element> {
477 return Results(RLMGetObjects(rlmRealm, type.className(), nil))
481 This method is useful only in specialized circumstances, for example, when building
482 components that integrate with Realm. If you are simply building an app on Realm, it is
483 recommended to use the typed method `objects(type:)`.
485 Returns all objects for a given class name in the Realm.
487 - parameter typeName: The class name of the objects to be returned.
488 - returns: All objects for the given class name as dynamic objects
492 public func dynamicObjects(_ typeName: String) -> Results<DynamicObject> {
493 return Results<DynamicObject>(RLMGetObjects(rlmRealm, typeName, nil))
497 Retrieves the single instance of a given object type with the given primary key from the Realm.
499 This method requires that `primaryKey()` be overridden on the given object class.
501 - see: `Object.primaryKey()`
503 - parameter type: The type of the object to be returned.
504 - parameter key: The primary key of the desired object.
506 - returns: An object of type `type`, or `nil` if no instance with the given primary key exists.
508 public func object<Element: Object, KeyType>(ofType type: Element.Type, forPrimaryKey key: KeyType) -> Element? {
509 return unsafeBitCast(RLMGetObject(rlmRealm, (type as Object.Type).className(),
510 dynamicBridgeCast(fromSwift: key)) as! RLMObjectBase?,
511 to: Optional<Element>.self)
515 This method is useful only in specialized circumstances, for example, when building
516 components that integrate with Realm. If you are simply building an app on Realm, it is
517 recommended to use the typed method `objectForPrimaryKey(_:key:)`.
519 Get a dynamic object with the given class name and primary key.
521 Returns `nil` if no object exists with the given class name and primary key.
523 This method requires that `primaryKey()` be overridden on the given subclass.
525 - see: Object.primaryKey()
527 - warning: This method is useful only in specialized circumstances.
529 - parameter className: The class name of the object to be returned.
530 - parameter key: The primary key of the desired object.
532 - returns: An object of type `DynamicObject` or `nil` if an object with the given primary key does not exist.
536 public func dynamicObject(ofType typeName: String, forPrimaryKey key: Any) -> DynamicObject? {
537 return unsafeBitCast(RLMGetObject(rlmRealm, typeName, key) as! RLMObjectBase?, to: Optional<DynamicObject>.self)
540 // MARK: Notifications
543 Adds a notification handler for changes made to this Realm, and returns a notification token.
545 Notification handlers are called after each write transaction is committed, independent of the thread or process.
547 Handler blocks are called on the same thread that they were added on, and may only be added on threads which are
548 currently within a run loop. Unless you are specifically creating and running a run loop on a background thread,
549 this will normally only be the main thread.
551 Notifications can't be delivered as long as the run loop is blocked by other activity. When notifications can't be
552 delivered instantly, multiple notifications may be coalesced.
554 You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
555 updates, call `invalidate()` on the token.
557 - parameter block: A block which is called to process Realm notifications. It receives the following parameters:
558 `notification`: the incoming notification; `realm`: the Realm for which the notification
561 - returns: A token which must be held for as long as you wish to continue receiving change notifications.
563 public func observe(_ block: @escaping NotificationBlock) -> NotificationToken {
564 return rlmRealm.addNotificationBlock { rlmNotification, _ in
565 switch rlmNotification {
566 case RLMNotification.DidChange:
567 block(.didChange, self)
568 case RLMNotification.RefreshRequired:
569 block(.refreshRequired, self)
571 fatalError("Unhandled notification type: \(rlmNotification)")
576 // MARK: Autorefresh and Refresh
579 Set this property to `true` to automatically update this Realm when changes happen in other threads.
581 If set to `true` (the default), changes made on other threads will be reflected in this Realm on the next cycle of
582 the run loop after the changes are committed. If set to `false`, you must manually call `refresh()` on the Realm
583 to update it to get the latest data.
585 Note that by default, background threads do not have an active run loop and you will need to manually call
586 `refresh()` in order to update to the latest version, even if `autorefresh` is set to `true`.
588 Even with this property enabled, you can still call `refresh()` at any time to update the Realm before the
589 automatic refresh would occur.
591 Notifications are sent when a write transaction is committed whether or not automatic refreshing is enabled.
593 Disabling `autorefresh` on a `Realm` without any strong references to it will not have any effect, and
594 `autorefresh` will revert back to `true` the next time the Realm is created. This is normally irrelevant as it
595 means that there is nothing to refresh (as managed `Object`s, `List`s, and `Results` have strong references to the
596 `Realm` that manages them), but it means that setting `autorefresh = false` in
597 `application(_:didFinishLaunchingWithOptions:)` and only later storing Realm objects will not work.
601 public var autorefresh: Bool {
603 return rlmRealm.autorefresh
606 rlmRealm.autorefresh = newValue
611 Updates the Realm and outstanding objects managed by the Realm to point to the most recent data.
613 - returns: Whether there were any updates for the Realm. Note that `true` may be returned even if no data actually
617 public func refresh() -> Bool {
618 return rlmRealm.refresh()
621 // MARK: Invalidation
624 Invalidates all `Object`s, `Results`, `LinkingObjects`, and `List`s managed by the Realm.
626 A Realm holds a read lock on the version of the data accessed by it, so
627 that changes made to the Realm on different threads do not modify or delete the
628 data seen by this Realm. Calling this method releases the read lock,
629 allowing the space used on disk to be reused by later write transactions rather
630 than growing the file. This method should be called before performing long
631 blocking operations on a background thread on which you previously read data
632 from the Realm which you no longer need.
634 All `Object`, `Results` and `List` instances obtained from this `Realm` instance on the current thread are
635 invalidated. `Object`s and `Array`s cannot be used. `Results` will become empty. The Realm itself remains valid,
636 and a new read transaction is implicitly begun the next time data is read from the Realm.
638 Calling this method multiple times in a row without reading any data from the
639 Realm, or before ever reading any data from the Realm, is a no-op. This method
640 may not be called on a read-only Realm.
642 public func invalidate() {
643 rlmRealm.invalidate()
646 // MARK: Writing a Copy
649 Writes a compacted and optionally encrypted copy of the Realm to the given local URL.
651 The destination file cannot already exist.
653 Note that if this method is called from within a write transaction, the *current* data is written, not the data
654 from the point when the previous write transaction was committed.
656 - parameter fileURL: Local URL to save the Realm to.
657 - parameter encryptionKey: Optional 64-byte encryption key to encrypt the new file with.
659 - throws: An `NSError` if the copy could not be written.
661 public func writeCopy(toFile fileURL: URL, encryptionKey: Data? = nil) throws {
662 try rlmRealm.writeCopy(to: fileURL, encryptionKey: encryptionKey)
667 internal var rlmRealm: RLMRealm
669 internal init(_ rlmRealm: RLMRealm) {
670 self.rlmRealm = rlmRealm
676 extension Realm: Equatable {
677 /// Returns whether two `Realm` instances are equal.
678 public static func == (lhs: Realm, rhs: Realm) -> Bool {
679 return lhs.rlmRealm == rhs.rlmRealm
683 // MARK: Notifications
686 /// A notification indicating that changes were made to a Realm.
687 public enum Notification: String {
689 This notification is posted when the data in a Realm has changed.
691 `didChange` is posted after a Realm has been refreshed to reflect a write transaction, This can happen when an
692 autorefresh occurs, `refresh()` is called, after an implicit refresh from `write(_:)`/`beginWrite()`, or after
693 a local write transaction is committed.
695 case didChange = "RLMRealmDidChangeNotification"
698 This notification is posted when a write transaction has been committed to a Realm on a different thread for
701 It is not posted if `autorefresh` is enabled, or if the Realm is refreshed before the notification has a chance
704 Realms with autorefresh disabled should normally install a handler for this notification which calls
705 `refresh()` after doing some work. Refreshing the Realm is optional, but not refreshing the Realm may lead to
706 large Realm files. This is because an extra copy of the data must be kept for the stale Realm.
708 case refreshRequired = "RLMRealmRefreshRequiredNotification"
712 /// The type of a block to run for notification purposes when the data in a Realm is modified.
713 public typealias NotificationBlock = (_ notification: Realm.Notification, _ realm: Realm) -> Void