EmbeddedObject.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2020 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. `EmbeddedObject` is a base class used to define embedded Realm model objects.
  23. Embedded objects work similarly to normal objects, but are owned by a single
  24. parent Object (which itself may be embedded). Unlike normal top-level objects,
  25. embedded objects cannot be directly created in or added to a Realm. Instead,
  26. they can only be created as part of a parent object, or by assigning an
  27. unmanaged object to a parent object's property. Embedded objects are
  28. automatically deleted when the parent object is deleted or when the parent is
  29. modified to no longer point at the embedded object, either by reassigning an
  30. Object property or by removing the embedded object from the List containing it.
  31. Embedded objects can only ever have a single parent object which links to
  32. them, and attempting to link to an existing managed embedded object will throw
  33. an exception.
  34. The property types supported on `EmbeddedObject` are the same as for `Object`,
  35. except for that embedded objects cannot link to top-level objects, so `Object`
  36. and `List<Object>` properties are not supported (`EmbeddedObject` and
  37. `List<EmbeddedObject>` *are*).
  38. Embedded objects cannot have primary keys or indexed properties.
  39. ```swift
  40. class Owner: Object {
  41. @objc dynamic var name: String = ""
  42. let dogs = List<Dog>()
  43. }
  44. class Dog: EmbeddedObject {
  45. @objc dynamic var name: String = ""
  46. @objc dynamic var adopted: Bool = false
  47. let owner = LinkingObjects(fromType: Owner.self, property: "dogs")
  48. }
  49. ```
  50. */
  51. public typealias EmbeddedObject = RealmSwiftEmbeddedObject
  52. extension EmbeddedObject: RealmCollectionValue {
  53. /// :nodoc:
  54. public static func _rlmArray() -> RLMArray<AnyObject> {
  55. return RLMArray(objectClassName: className())
  56. }
  57. /// :nodoc:
  58. public class override final func isEmbedded() -> Bool {
  59. return true
  60. }
  61. // MARK: Initializers
  62. /**
  63. Creates an unmanaged instance of a Realm object.
  64. The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or
  65. dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing one element for each
  66. managed property. An exception will be thrown if any required properties are not present and those properties were
  67. not defined with default values.
  68. When passing in an `Array` as the `value` argument, all properties must be present, valid and in the same order as
  69. the properties defined in the model.
  70. An unmanaged embedded object can be added to a Realm by assigning it to a property of a managed object or by adding it to a managed List.
  71. - parameter value: The value used to populate the object.
  72. */
  73. public convenience init(value: Any) {
  74. self.init()
  75. RLMInitializeWithValue(self, value, .partialPrivateShared())
  76. }
  77. // MARK: Properties
  78. /// The Realm which manages the object, or `nil` if the object is unmanaged.
  79. public var realm: Realm? {
  80. if let rlmReam = RLMObjectBaseRealm(self) {
  81. return Realm(rlmReam)
  82. }
  83. return nil
  84. }
  85. /// The object schema which lists the managed properties for the object.
  86. public var objectSchema: ObjectSchema {
  87. return ObjectSchema(RLMObjectBaseObjectSchema(self)!)
  88. }
  89. /// Indicates if the object can no longer be accessed because it is now invalid.
  90. ///
  91. /// An object can no longer be accessed if the object has been deleted from the Realm that manages it, or if
  92. /// `invalidate()` is called on that Realm.
  93. public override final var isInvalidated: Bool { return super.isInvalidated }
  94. /// A human-readable description of the object.
  95. open override var description: String { return super.description }
  96. /**
  97. WARNING: This is an internal helper method not intended for public use.
  98. It is not considered part of the public API.
  99. :nodoc:
  100. */
  101. public override final class func _getProperties() -> [RLMProperty] {
  102. return ObjectUtil.getSwiftProperties(self)
  103. }
  104. // MARK: Object Customization
  105. /**
  106. Override this method to specify the names of properties to ignore. These properties will not be managed by
  107. the Realm that manages the object.
  108. - returns: An array of property names to ignore.
  109. */
  110. @objc open class func ignoredProperties() -> [String] { return [] }
  111. // MARK: Key-Value Coding & Subscripting
  112. /// Returns or sets the value of the property with the given name.
  113. @objc open subscript(key: String) -> Any? {
  114. get {
  115. if realm == nil {
  116. return value(forKey: key)
  117. }
  118. return dynamicGet(key: key)
  119. }
  120. set(value) {
  121. if realm == nil {
  122. setValue(value, forKey: key)
  123. } else {
  124. RLMDynamicValidatedSet(self, key, value)
  125. }
  126. }
  127. }
  128. private func dynamicGet(key: String) -> Any? {
  129. let objectSchema = RLMObjectBaseObjectSchema(self)!
  130. guard let prop = objectSchema[key] else {
  131. throwRealmException("Invalid property name '\(key) for class \(objectSchema.className)")
  132. }
  133. if let accessor = prop.swiftAccessor {
  134. return accessor.get(Unmanaged.passUnretained(self).toOpaque() + ivar_getOffset(prop.swiftIvar!))
  135. }
  136. if let ivar = prop.swiftIvar, prop.array {
  137. return object_getIvar(self, ivar)
  138. }
  139. return RLMDynamicGet(self, prop)
  140. }
  141. // MARK: Notifications
  142. /**
  143. Registers a block to be called each time the object changes.
  144. The block will be asynchronously called after each write transaction which
  145. deletes the object or modifies any of the managed properties of the object,
  146. including self-assignments that set a property to its existing value.
  147. For write transactions performed on different threads or in different
  148. processes, the block will be called when the managing Realm is
  149. (auto)refreshed to a version including the changes, while for local write
  150. transactions it will be called at some point in the future after the write
  151. transaction is committed.
  152. Notifications are delivered via the standard run loop, and so can't be
  153. delivered while the run loop is blocked by other activity. When
  154. notifications can't be delivered instantly, multiple notifications may be
  155. coalesced into a single notification.
  156. Unlike with `List` and `Results`, there is no "initial" callback made after
  157. you add a new notification block.
  158. Only objects which are managed by a Realm can be observed in this way. You
  159. must retain the returned token for as long as you want updates to be sent
  160. to the block. To stop receiving updates, call `invalidate()` on the token.
  161. It is safe to capture a strong reference to the observed object within the
  162. callback block. There is no retain cycle due to that the callback is
  163. retained by the returned token and not by the object itself.
  164. - warning: This method cannot be called during a write transaction, or when
  165. the containing Realm is read-only.
  166. - parameter queue: The serial dispatch queue to receive notification on. If
  167. `nil`, notifications are delivered to the current thread.
  168. - parameter block: The block to call with information about changes to the object.
  169. - returns: A token which must be held for as long as you want updates to be delivered.
  170. */
  171. public func observe<T: RLMObjectBase>(on queue: DispatchQueue? = nil,
  172. _ block: @escaping (ObjectChange<T>) -> Void) -> NotificationToken {
  173. return _observe(on: queue, block)
  174. }
  175. // MARK: Dynamic list
  176. /**
  177. Returns a list of `DynamicObject`s for a given property name.
  178. - warning: This method is useful only in specialized circumstances, for example, when building
  179. components that integrate with Realm. If you are simply building an app on Realm, it is
  180. recommended to use instance variables or cast the values returned from key-value coding.
  181. - parameter propertyName: The name of the property.
  182. - returns: A list of `DynamicObject`s.
  183. :nodoc:
  184. */
  185. public func dynamicList(_ propertyName: String) -> List<DynamicObject> {
  186. return noWarnUnsafeBitCast(dynamicGet(key: propertyName) as! RLMListBase,
  187. to: List<DynamicObject>.self)
  188. }
  189. // MARK: Comparison
  190. /**
  191. Returns whether two Realm objects are the same.
  192. Objects are considered the same if and only if they are both managed by the same
  193. Realm and point to the same underlying object in the database.
  194. - note: Equality comparison is implemented by `isEqual(_:)`. If the object type
  195. is defined with a primary key, `isEqual(_:)` behaves identically to this
  196. method. If the object type is not defined with a primary key,
  197. `isEqual(_:)` uses the `NSObject` behavior of comparing object identity.
  198. This method can be used to compare two objects for database equality
  199. whether or not their object type defines a primary key.
  200. - parameter object: The object to compare the receiver to.
  201. */
  202. public func isSameObject(as object: EmbeddedObject?) -> Bool {
  203. return RLMObjectBaseAreEqual(self, object)
  204. }
  205. }
  206. extension EmbeddedObject: ThreadConfined {
  207. /**
  208. Indicates if this object is frozen.
  209. - see: `Object.freeze()`
  210. */
  211. public var isFrozen: Bool { return realm?.isFrozen ?? false }
  212. /**
  213. Returns a frozen (immutable) snapshot of this object.
  214. The frozen copy is an immutable object which contains the same data as this
  215. object currently contains, but will not update when writes are made to the
  216. containing Realm. Unlike live objects, frozen objects can be accessed from any
  217. thread.
  218. - warning: Holding onto a frozen object for an extended period while performing write
  219. transaction on the Realm may result in the Realm file growing to large sizes. See
  220. `Realm.Configuration.maximumNumberOfActiveVersions` for more information.
  221. - warning: This method can only be called on a managed object.
  222. */
  223. public func freeze() -> Self {
  224. return realm!.freeze(self)
  225. }
  226. /**
  227. Returns a live (mutable) reference of this object.
  228. This method creates a managed accessor to a live copy of the same frozen object.
  229. Will return self if called on an already live object.
  230. */
  231. public func thaw() -> Self? {
  232. return realm?.thaw(self)
  233. }
  234. }
  235. // MARK: AssistedObjectiveCBridgeable
  236. // FIXME: Remove when `as! Self` can be written
  237. private func forceCastToInferred<T, V>(_ x: T) -> V {
  238. return x as! V
  239. }
  240. extension EmbeddedObject: AssistedObjectiveCBridgeable {
  241. static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Self {
  242. return forceCastToInferred(objectiveCValue)
  243. }
  244. var bridged: (objectiveCValue: Any, metadata: Any?) {
  245. return (objectiveCValue: unsafeBitCast(self, to: RLMObject.self), metadata: nil)
  246. }
  247. }