X-Git-Url: https://git.mdrn.pl/wl-app.git/blobdiff_plain/53b27422d140022594fc241cca91c3183be57bca..48b2fe9f7c2dc3d9aeaaa6dbfb27c7da4f3235ff:/iOS/Pods/RealmSwift/RealmSwift/Realm.swift diff --git a/iOS/Pods/RealmSwift/RealmSwift/Realm.swift b/iOS/Pods/RealmSwift/RealmSwift/Realm.swift new file mode 100644 index 0000000..def1432 --- /dev/null +++ b/iOS/Pods/RealmSwift/RealmSwift/Realm.swift @@ -0,0 +1,713 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +import Foundation +import Realm +import Realm.Private + +/** + A `Realm` instance (also referred to as "a Realm") represents a Realm database. + + Realms can either be stored on disk (see `init(path:)`) or in memory (see `Configuration`). + + `Realm` instances are cached internally, and constructing equivalent `Realm` objects (for example, by using the same + path or identifier) produces limited overhead. + + If you specifically want to ensure a `Realm` instance is destroyed (for example, if you wish to open a Realm, check + some property, and then possibly delete the Realm file and re-open it), place the code which uses the Realm within an + `autoreleasepool {}` and ensure you have no other strong references to it. + + - warning: `Realm` instances are not thread safe and cannot be shared across threads or dispatch queues. You must + construct a new instance for each thread in which a Realm will be accessed. For dispatch queues, this means + that you must construct a new instance in each block which is dispatched, as a queue is not guaranteed to + run all of its blocks on the same thread. + */ +public final class Realm { + + // MARK: Properties + + /// The `Schema` used by the Realm. + public var schema: Schema { return Schema(rlmRealm.schema) } + + /// The `Configuration` value that was used to create the `Realm` instance. + public var configuration: Configuration { return Configuration.fromRLMRealmConfiguration(rlmRealm.configuration) } + + /// Indicates if the Realm contains any objects. + public var isEmpty: Bool { return rlmRealm.isEmpty } + + // MARK: Initializers + + /** + Obtains an instance of the default Realm. + + The default Realm is persisted as *default.realm* under the *Documents* directory of your Application on iOS, and + in your application's *Application Support* directory on OS X. + + The default Realm is created using the default `Configuration`, which can be changed by setting the + `Realm.Configuration.defaultConfiguration` property to a new value. + + - throws: An `NSError` if the Realm could not be initialized. + */ + public convenience init() throws { + let rlmRealm = try RLMRealm(configuration: RLMRealmConfiguration.default()) + self.init(rlmRealm) + } + + /** + Obtains a `Realm` instance with the given configuration. + + - parameter configuration: A configuration value to use when creating the Realm. + + - throws: An `NSError` if the Realm could not be initialized. + */ + public convenience init(configuration: Configuration) throws { + let rlmRealm = try RLMRealm(configuration: configuration.rlmConfiguration) + self.init(rlmRealm) + } + + /** + Obtains a `Realm` instance persisted at a specified file URL. + + - parameter fileURL: The local URL of the file the Realm should be saved at. + + - throws: An `NSError` if the Realm could not be initialized. + */ + public convenience init(fileURL: URL) throws { + var configuration = Configuration.defaultConfiguration + configuration.fileURL = fileURL + try self.init(configuration: configuration) + } + + // MARK: Async + + /** + Asynchronously open a Realm and deliver it to a block on the given queue. + + Opening a Realm asynchronously will perform all work needed to get the Realm to + a usable state (such as running potentially time-consuming migrations) on a + background thread before dispatching to the given queue. In addition, + synchronized Realms wait for all remote content available at the time the + operation began to be downloaded and available locally. + + - parameter configuration: A configuration object to use when opening the Realm. + - parameter callbackQueue: The dispatch queue on which the callback should be run. + - parameter callback: A callback block. If the Realm was successfully opened, an + it will be passed in as an argument. + Otherwise, a `Swift.Error` describing what went wrong will be + passed to the block instead. + + - note: The returned Realm is confined to the thread on which it was created. + Because GCD does not guarantee that queues will always use the same + thread, accessing the returned Realm outside the callback block (even if + accessed from `callbackQueue`) is unsafe. + */ + public static func asyncOpen(configuration: Realm.Configuration = .defaultConfiguration, + callbackQueue: DispatchQueue = .main, + callback: @escaping (Realm?, Swift.Error?) -> Void) { + RLMRealm.asyncOpen(with: configuration.rlmConfiguration, callbackQueue: callbackQueue) { rlmRealm, error in + callback(rlmRealm.flatMap(Realm.init), error) + } + } + + // MARK: Transactions + + /** + Performs actions contained within the given block inside a write transaction. + + If the block throws an error, the transaction will be canceled and any + changes made before the error will be rolled back. + + Only one write transaction can be open at a time for each Realm file. Write + transactions cannot be nested, and trying to begin a write transaction on a + Realm which is already in a write transaction will throw an exception. + Calls to `write` from `Realm` instances for the same Realm file in other + threads or other processes will block until the current write transaction + completes or is cancelled. + + Before beginning the write transaction, `write` updates the `Realm` + instance to the latest Realm version, as if `refresh()` had been called, + and generates notifications if applicable. This has no effect if the Realm + was already up to date. + + - parameter block: The block containing actions to perform. + + - throws: An `NSError` if the transaction could not be completed successfully. + If `block` throws, the function throws the propagated `ErrorType` instead. + */ + public func write(_ block: (() throws -> Void)) throws { + beginWrite() + do { + try block() + } catch let error { + if isInWriteTransaction { cancelWrite() } + throw error + } + if isInWriteTransaction { try commitWrite() } + } + + /** + Begins a write transaction on the Realm. + + Only one write transaction can be open at a time for each Realm file. Write + transactions cannot be nested, and trying to begin a write transaction on a + Realm which is already in a write transaction will throw an exception. + Calls to `beginWrite` from `Realm` instances for the same Realm file in + other threads or other processes will block until the current write + transaction completes or is cancelled. + + Before beginning the write transaction, `beginWrite` updates the `Realm` + instance to the latest Realm version, as if `refresh()` had been called, + and generates notifications if applicable. This has no effect if the Realm + was already up to date. + + It is rarely a good idea to have write transactions span multiple cycles of + the run loop, but if you do wish to do so you will need to ensure that the + Realm participating in the write transaction is kept alive until the write + transaction is committed. + */ + public func beginWrite() { + rlmRealm.beginWriteTransaction() + } + + /** + Commits all write operations in the current write transaction, and ends + the transaction. + + After saving the changes and completing the write transaction, all + notification blocks registered on this specific `Realm` instance are called + synchronously. Notification blocks for `Realm` instances on other threads + and blocks registered for any Realm collection (including those on the + current thread) are scheduled to be called synchronously. + + You can skip notifiying specific notification blocks about the changes made + in this write transaction by passing in their associated notification + tokens. This is primarily useful when the write transaction is saving + changes already made in the UI and you do not want to have the notification + block attempt to re-apply the same changes. + + The tokens passed to this function must be for notifications for this Realm + which were added on the same thread as the write transaction is being + performed on. Notifications for different threads cannot be skipped using + this method. + + - warning: This method may only be called during a write transaction. + + - throws: An `NSError` if the transaction could not be written due to + running out of disk space or other i/o errors. + */ + public func commitWrite(withoutNotifying tokens: [NotificationToken] = []) throws { + try rlmRealm.commitWriteTransactionWithoutNotifying(tokens) + } + + /** + Reverts all writes made in the current write transaction and ends the transaction. + + This rolls back all objects in the Realm to the state they were in at the + beginning of the write transaction, and then ends the transaction. + + This restores the data for deleted objects, but does not revive invalidated + object instances. Any `Object`s which were added to the Realm will be + invalidated rather than becoming unmanaged. + + Given the following code: + + ```swift + let oldObject = objects(ObjectType).first! + let newObject = ObjectType() + + realm.beginWrite() + realm.add(newObject) + realm.delete(oldObject) + realm.cancelWrite() + ``` + + Both `oldObject` and `newObject` will return `true` for `isInvalidated`, + but re-running the query which provided `oldObject` will once again return + the valid object. + + KVO observers on any objects which were modified during the transaction + will be notified about the change back to their initial values, but no + other notifcations are produced by a cancelled write transaction. + + - warning: This method may only be called during a write transaction. + */ + public func cancelWrite() { + rlmRealm.cancelWriteTransaction() + } + + /** + Indicates whether the Realm is currently in a write transaction. + + - warning: Do not simply check this property and then start a write transaction whenever an object needs to be + created, updated, or removed. Doing so might cause a large number of write transactions to be created, + degrading performance. Instead, always prefer performing multiple updates during a single transaction. + */ + public var isInWriteTransaction: Bool { + return rlmRealm.inWriteTransaction + } + + // MARK: Adding and Creating objects + + /** + Adds or updates an existing object into the Realm. + + Only pass `true` to `update` if the object has a primary key. If no object exists in the Realm with the same + primary key value, the object is inserted. Otherwise, the existing object is updated with any changed values. + + When added, all child relationships referenced by this object will also be added to the Realm if they are not + already in it. If the object or any related objects are already being managed by a different Realm an error will be + thrown. Instead, use one of the `create` functions to insert a copy of a managed object into a different Realm. + + The object to be added must be valid and cannot have been previously deleted from a Realm (i.e. `isInvalidated` + must be `false`). + + - parameter object: The object to be added to this Realm. + - parameter update: If `true`, the Realm will try to find an existing copy of the object (with the same primary + key), and update it. Otherwise, the object will be added. + */ + public func add(_ object: Object, update: Bool = false) { + if update && object.objectSchema.primaryKeyProperty == nil { + throwRealmException("'\(object.objectSchema.className)' does not have a primary key and can not be updated") + } + RLMAddObjectToRealm(object, rlmRealm, update) + } + + /** + Adds or updates all the objects in a collection into the Realm. + + - see: `add(_:update:)` + + - warning: This method may only be called during a write transaction. + + - parameter objects: A sequence which contains objects to be added to the Realm. + - parameter update: If `true`, objects that are already in the Realm will be updated instead of added anew. + */ + public func add(_ objects: S, update: Bool = false) where S.Iterator.Element: Object { + for obj in objects { + add(obj, update: update) + } + } + + /** + Creates or updates a Realm object with a given value, adding it to the Realm and returning it. + + You may only pass `true` to `update` if the object has a primary key. If no object exists + in the Realm with the same primary key value, the object is inserted. Otherwise, the + existing object is updated with any changed values. + + The `value` argument can be a Realm object, a key-value coding compliant object, an array + or dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing + one element for each managed property. Do not pass in a `LinkingObjects` instance, either + by itself or as a member of a collection. + + If the object is being created, all required properties that were not defined with default + values must be given initial values through the `value` argument. Otherwise, an Objective-C + exception will be thrown. + + If the object is being updated, all properties defined in its schema will be set by copying + from `value` using key-value coding. If the `value` argument does not respond to `value(forKey:)` + for a given property name (or getter name, if defined), that value will remain untouched. + Nullable properties on the object can be set to nil by using `NSNull` as the updated value, + or (if you are passing in an instance of an `Object` subclass) setting the corresponding + property on `value` to nil. + + If the `value` argument is an array, all properties must be present, valid and in the same + order as the properties defined in the model. + + - warning: This method may only be called during a write transaction. + + - parameter type: The type of the object to create. + - parameter value: The value used to populate the object. + - parameter update: If `true`, the Realm will try to find an existing copy of the object (with the same primary + key), and update it. Otherwise, the object will be added. + + - returns: The newly created object. + */ + @discardableResult + public func create(_ type: T.Type, value: Any = [:], update: Bool = false) -> T { + let typeName = (type as Object.Type).className() + if update && schema[typeName]?.primaryKeyProperty == nil { + throwRealmException("'\(typeName)' does not have a primary key and can not be updated") + } + return unsafeDowncast(RLMCreateObjectInRealmWithValue(rlmRealm, typeName, value, update), to: T.self) + } + + /** + This method is useful only in specialized circumstances, for example, when building + components that integrate with Realm. If you are simply building an app on Realm, it is + recommended to use the typed method `create(_:value:update:)`. + + Creates or updates an object with the given class name and adds it to the `Realm`, populating + the object with the given value. + + You may only pass `true` to `update` if the object has a primary key. If no object exists + in the Realm with the same primary key value, the object is inserted. Otherwise, the + existing object is updated with any changed values. + + The `value` argument can be a Realm object, a key-value coding compliant object, an array + or dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing + one element for each managed property. Do not pass in a `LinkingObjects` instance, either + by itself or as a member of a collection. + + If the object is being created, all required properties that were not defined with default + values must be given initial values through the `value` argument. Otherwise, an Objective-C + exception will be thrown. + + If the object is being updated, all properties defined in its schema will be set by copying + from `value` using key-value coding. If the `value` argument does not respond to `value(forKey:)` + for a given property name (or getter name, if defined), that value will remain untouched. + Nullable properties on the object can be set to nil by using `NSNull` as the updated value. + + If the `value` argument is an array, all properties must be present, valid and in the same + order as the properties defined in the model. + + - warning: This method can only be called during a write transaction. + + - parameter className: The class name of the object to create. + - parameter value: The value used to populate the object. + - parameter update: If true will try to update existing objects with the same primary key. + + - returns: The created object. + + :nodoc: + */ + @discardableResult + public func dynamicCreate(_ typeName: String, value: Any = [:], update: Bool = false) -> DynamicObject { + if update && schema[typeName]?.primaryKeyProperty == nil { + throwRealmException("'\(typeName)' does not have a primary key and can not be updated") + } + return noWarnUnsafeBitCast(RLMCreateObjectInRealmWithValue(rlmRealm, typeName, value, update), + to: DynamicObject.self) + } + + // MARK: Deleting objects + + /** + Deletes an object from the Realm. Once the object is deleted it is considered invalidated. + + - warning: This method may only be called during a write transaction. + + - parameter object: The object to be deleted. + */ + public func delete(_ object: Object) { + RLMDeleteObjectFromRealm(object, rlmRealm) + } + + /** + Deletes zero or more objects from the Realm. + + Do not pass in a slice to a `Results` or any other auto-updating Realm collection + type (for example, the type returned by the Swift `suffix(_:)` standard library + method). Instead, make a copy of the objects to delete using `Array()`, and pass + that instead. Directly passing in a view into an auto-updating collection may + result in 'index out of bounds' exceptions being thrown. + + - warning: This method may only be called during a write transaction. + + - parameter objects: The objects to be deleted. This can be a `List`, + `Results`, or any other Swift `Sequence` whose + elements are `Object`s (subject to the caveats above). + */ + public func delete(_ objects: S) where S.Iterator.Element: Object { + for obj in objects { + delete(obj) + } + } + + /** + Deletes zero or more objects from the Realm. + + - warning: This method may only be called during a write transaction. + + - parameter objects: A list of objects to delete. + + :nodoc: + */ + public func delete(_ objects: List) { + rlmRealm.deleteObjects(objects._rlmArray) + } + + /** + Deletes zero or more objects from the Realm. + + - warning: This method may only be called during a write transaction. + + - parameter objects: A `Results` containing the objects to be deleted. + + :nodoc: + */ + public func delete(_ objects: Results) { + rlmRealm.deleteObjects(objects.rlmResults) + } + + /** + Deletes all objects from the Realm. + + - warning: This method may only be called during a write transaction. + */ + public func deleteAll() { + RLMDeleteAllObjectsFromRealm(rlmRealm) + } + + // MARK: Object Retrieval + + /** + Returns all objects of the given type stored in the Realm. + + - parameter type: The type of the objects to be returned. + + - returns: A `Results` containing the objects. + */ + public func objects(_ type: Element.Type) -> Results { + return Results(RLMGetObjects(rlmRealm, type.className(), nil)) + } + + /** + This method is useful only in specialized circumstances, for example, when building + components that integrate with Realm. If you are simply building an app on Realm, it is + recommended to use the typed method `objects(type:)`. + + Returns all objects for a given class name in the Realm. + + - parameter typeName: The class name of the objects to be returned. + - returns: All objects for the given class name as dynamic objects + + :nodoc: + */ + public func dynamicObjects(_ typeName: String) -> Results { + return Results(RLMGetObjects(rlmRealm, typeName, nil)) + } + + /** + Retrieves the single instance of a given object type with the given primary key from the Realm. + + This method requires that `primaryKey()` be overridden on the given object class. + + - see: `Object.primaryKey()` + + - parameter type: The type of the object to be returned. + - parameter key: The primary key of the desired object. + + - returns: An object of type `type`, or `nil` if no instance with the given primary key exists. + */ + public func object(ofType type: Element.Type, forPrimaryKey key: KeyType) -> Element? { + return unsafeBitCast(RLMGetObject(rlmRealm, (type as Object.Type).className(), + dynamicBridgeCast(fromSwift: key)) as! RLMObjectBase?, + to: Optional.self) + } + + /** + This method is useful only in specialized circumstances, for example, when building + components that integrate with Realm. If you are simply building an app on Realm, it is + recommended to use the typed method `objectForPrimaryKey(_:key:)`. + + Get a dynamic object with the given class name and primary key. + + Returns `nil` if no object exists with the given class name and primary key. + + This method requires that `primaryKey()` be overridden on the given subclass. + + - see: Object.primaryKey() + + - warning: This method is useful only in specialized circumstances. + + - parameter className: The class name of the object to be returned. + - parameter key: The primary key of the desired object. + + - returns: An object of type `DynamicObject` or `nil` if an object with the given primary key does not exist. + + :nodoc: + */ + public func dynamicObject(ofType typeName: String, forPrimaryKey key: Any) -> DynamicObject? { + return unsafeBitCast(RLMGetObject(rlmRealm, typeName, key) as! RLMObjectBase?, to: Optional.self) + } + + // MARK: Notifications + + /** + Adds a notification handler for changes made to this Realm, and returns a notification token. + + Notification handlers are called after each write transaction is committed, independent of the thread or process. + + Handler blocks are called on the same thread that they were added on, and may only be added on threads which are + currently within a run loop. Unless you are specifically creating and running a run loop on a background thread, + this will normally only be the main thread. + + Notifications can't be delivered as long as the run loop is blocked by other activity. When notifications can't be + delivered instantly, multiple notifications may be coalesced. + + You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving + updates, call `invalidate()` on the token. + + - parameter block: A block which is called to process Realm notifications. It receives the following parameters: + `notification`: the incoming notification; `realm`: the Realm for which the notification + occurred. + + - returns: A token which must be held for as long as you wish to continue receiving change notifications. + */ + public func observe(_ block: @escaping NotificationBlock) -> NotificationToken { + return rlmRealm.addNotificationBlock { rlmNotification, _ in + switch rlmNotification { + case RLMNotification.DidChange: + block(.didChange, self) + case RLMNotification.RefreshRequired: + block(.refreshRequired, self) + default: + fatalError("Unhandled notification type: \(rlmNotification)") + } + } + } + + // MARK: Autorefresh and Refresh + + /** + Set this property to `true` to automatically update this Realm when changes happen in other threads. + + If set to `true` (the default), changes made on other threads will be reflected in this Realm on the next cycle of + the run loop after the changes are committed. If set to `false`, you must manually call `refresh()` on the Realm + to update it to get the latest data. + + Note that by default, background threads do not have an active run loop and you will need to manually call + `refresh()` in order to update to the latest version, even if `autorefresh` is set to `true`. + + Even with this property enabled, you can still call `refresh()` at any time to update the Realm before the + automatic refresh would occur. + + Notifications are sent when a write transaction is committed whether or not automatic refreshing is enabled. + + Disabling `autorefresh` on a `Realm` without any strong references to it will not have any effect, and + `autorefresh` will revert back to `true` the next time the Realm is created. This is normally irrelevant as it + means that there is nothing to refresh (as managed `Object`s, `List`s, and `Results` have strong references to the + `Realm` that manages them), but it means that setting `autorefresh = false` in + `application(_:didFinishLaunchingWithOptions:)` and only later storing Realm objects will not work. + + Defaults to `true`. + */ + public var autorefresh: Bool { + get { + return rlmRealm.autorefresh + } + set { + rlmRealm.autorefresh = newValue + } + } + + /** + Updates the Realm and outstanding objects managed by the Realm to point to the most recent data. + + - returns: Whether there were any updates for the Realm. Note that `true` may be returned even if no data actually + changed. + */ + @discardableResult + public func refresh() -> Bool { + return rlmRealm.refresh() + } + + // MARK: Invalidation + + /** + Invalidates all `Object`s, `Results`, `LinkingObjects`, and `List`s managed by the Realm. + + A Realm holds a read lock on the version of the data accessed by it, so + that changes made to the Realm on different threads do not modify or delete the + data seen by this Realm. Calling this method releases the read lock, + allowing the space used on disk to be reused by later write transactions rather + than growing the file. This method should be called before performing long + blocking operations on a background thread on which you previously read data + from the Realm which you no longer need. + + All `Object`, `Results` and `List` instances obtained from this `Realm` instance on the current thread are + invalidated. `Object`s and `Array`s cannot be used. `Results` will become empty. The Realm itself remains valid, + and a new read transaction is implicitly begun the next time data is read from the Realm. + + Calling this method multiple times in a row without reading any data from the + Realm, or before ever reading any data from the Realm, is a no-op. This method + may not be called on a read-only Realm. + */ + public func invalidate() { + rlmRealm.invalidate() + } + + // MARK: Writing a Copy + + /** + Writes a compacted and optionally encrypted copy of the Realm to the given local URL. + + The destination file cannot already exist. + + Note that if this method is called from within a write transaction, the *current* data is written, not the data + from the point when the previous write transaction was committed. + + - parameter fileURL: Local URL to save the Realm to. + - parameter encryptionKey: Optional 64-byte encryption key to encrypt the new file with. + + - throws: An `NSError` if the copy could not be written. + */ + public func writeCopy(toFile fileURL: URL, encryptionKey: Data? = nil) throws { + try rlmRealm.writeCopy(to: fileURL, encryptionKey: encryptionKey) + } + + // MARK: Internal + + internal var rlmRealm: RLMRealm + + internal init(_ rlmRealm: RLMRealm) { + self.rlmRealm = rlmRealm + } +} + +// MARK: Equatable + +extension Realm: Equatable { + /// Returns whether two `Realm` instances are equal. + public static func == (lhs: Realm, rhs: Realm) -> Bool { + return lhs.rlmRealm == rhs.rlmRealm + } +} + +// MARK: Notifications + +extension Realm { + /// A notification indicating that changes were made to a Realm. + public enum Notification: String { + /** + This notification is posted when the data in a Realm has changed. + + `didChange` is posted after a Realm has been refreshed to reflect a write transaction, This can happen when an + autorefresh occurs, `refresh()` is called, after an implicit refresh from `write(_:)`/`beginWrite()`, or after + a local write transaction is committed. + */ + case didChange = "RLMRealmDidChangeNotification" + + /** + This notification is posted when a write transaction has been committed to a Realm on a different thread for + the same file. + + It is not posted if `autorefresh` is enabled, or if the Realm is refreshed before the notification has a chance + to run. + + Realms with autorefresh disabled should normally install a handler for this notification which calls + `refresh()` after doing some work. Refreshing the Realm is optional, but not refreshing the Realm may lead to + large Realm files. This is because an extra copy of the data must be kept for the stale Realm. + */ + case refreshRequired = "RLMRealmRefreshRequiredNotification" + } +} + +/// The type of a block to run for notification purposes when the data in a Realm is modified. +public typealias NotificationBlock = (_ notification: Realm.Notification, _ realm: Realm) -> Void