Object.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  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. `Object` is a class used to define Realm model objects.
  23. In Realm you define your model classes by subclassing `Object` and adding properties to be managed.
  24. You then instantiate and use your custom subclasses instead of using the `Object` class directly.
  25. ```swift
  26. class Dog: Object {
  27. @objc dynamic var name: String = ""
  28. @objc dynamic var adopted: Bool = false
  29. let siblings = List<Dog>()
  30. }
  31. ```
  32. ### Supported property types
  33. - `String`, `NSString`
  34. - `Int`
  35. - `Int8`, `Int16`, `Int32`, `Int64`
  36. - `Float`
  37. - `Double`
  38. - `Bool`
  39. - `Date`, `NSDate`
  40. - `Data`, `NSData`
  41. - `RealmOptional<Value>` for optional numeric properties
  42. - `Object` subclasses, to model many-to-one relationships
  43. - `List<Element>`, to model many-to-many relationships
  44. `String`, `NSString`, `Date`, `NSDate`, `Data`, `NSData` and `Object` subclass properties can be declared as optional.
  45. `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `Float`, `Double`, `Bool`, and `List` properties cannot. To store an optional
  46. number, use `RealmOptional<Int>`, `RealmOptional<Float>`, `RealmOptional<Double>`, or `RealmOptional<Bool>` instead,
  47. which wraps an optional numeric value.
  48. All property types except for `List` and `RealmOptional` *must* be declared as `@objc dynamic var`. `List` and
  49. `RealmOptional` properties must be declared as non-dynamic `let` properties. Swift `lazy` properties are not allowed.
  50. Note that none of the restrictions listed above apply to properties that are configured to be ignored by Realm.
  51. ### Querying
  52. You can retrieve all objects of a given type from a Realm by calling the `objects(_:)` instance method.
  53. ### Relationships
  54. See our [Cocoa guide](http://realm.io/docs/cocoa) for more details.
  55. */
  56. @objc(RealmSwiftObject)
  57. open class Object: RLMObjectBase, ThreadConfined, RealmCollectionValue {
  58. /// :nodoc:
  59. public static func _rlmArray() -> RLMArray<AnyObject> {
  60. return RLMArray(objectClassName: className())
  61. }
  62. // MARK: Initializers
  63. /**
  64. Creates an unmanaged instance of a Realm object.
  65. Call `add(_:)` on a `Realm` instance to add an unmanaged object into that Realm.
  66. - see: `Realm().add(_:)`
  67. */
  68. public override required init() {
  69. super.init()
  70. }
  71. /**
  72. Creates an unmanaged instance of a Realm object.
  73. The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or
  74. dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing one element for each
  75. managed property. An exception will be thrown if any required properties are not present and those properties were
  76. not defined with default values.
  77. When passing in an `Array` as the `value` argument, all properties must be present, valid and in the same order as
  78. the properties defined in the model.
  79. Call `add(_:)` on a `Realm` instance to add an unmanaged object into that Realm.
  80. - parameter value: The value used to populate the object.
  81. */
  82. public init(value: Any) {
  83. super.init(value: value, schema: .partialPrivateShared())
  84. }
  85. // MARK: Properties
  86. /// The Realm which manages the object, or `nil` if the object is unmanaged.
  87. public var realm: Realm? {
  88. if let rlmReam = RLMObjectBaseRealm(self) {
  89. return Realm(rlmReam)
  90. }
  91. return nil
  92. }
  93. /// The object schema which lists the managed properties for the object.
  94. public var objectSchema: ObjectSchema {
  95. return ObjectSchema(RLMObjectBaseObjectSchema(self)!)
  96. }
  97. /// Indicates if the object can no longer be accessed because it is now invalid.
  98. ///
  99. /// An object can no longer be accessed if the object has been deleted from the Realm that manages it, or if
  100. /// `invalidate()` is called on that Realm.
  101. public override final var isInvalidated: Bool { return super.isInvalidated }
  102. /// A human-readable description of the object.
  103. open override var description: String { return super.description }
  104. /**
  105. WARNING: This is an internal helper method not intended for public use.
  106. It is not considered part of the public API.
  107. :nodoc:
  108. */
  109. public override final class func objectUtilClass(_ isSwift: Bool) -> AnyClass {
  110. return ObjectUtil.self
  111. }
  112. // MARK: Object Customization
  113. /**
  114. Override this method to specify the name of a property to be used as the primary key.
  115. Only properties of types `String` and `Int` can be designated as the primary key. Primary key properties enforce
  116. uniqueness for each value whenever the property is set, which incurs minor overhead. Indexes are created
  117. automatically for primary key properties.
  118. - returns: The name of the property designated as the primary key, or `nil` if the model has no primary key.
  119. */
  120. @objc open class func primaryKey() -> String? { return nil }
  121. /**
  122. Override this method to specify the names of properties to ignore. These properties will not be managed by
  123. the Realm that manages the object.
  124. - returns: An array of property names to ignore.
  125. */
  126. @objc open class func ignoredProperties() -> [String] { return [] }
  127. /**
  128. Returns an array of property names for properties which should be indexed.
  129. Only string, integer, boolean, `Date`, and `NSDate` properties are supported.
  130. - returns: An array of property names.
  131. */
  132. @objc open class func indexedProperties() -> [String] { return [] }
  133. // MARK: Key-Value Coding & Subscripting
  134. /// Returns or sets the value of the property with the given name.
  135. @objc open subscript(key: String) -> Any? {
  136. get {
  137. if realm == nil {
  138. return value(forKey: key)
  139. }
  140. return RLMDynamicGetByName(self, key, true)
  141. }
  142. set(value) {
  143. if realm == nil {
  144. setValue(value, forKey: key)
  145. } else {
  146. RLMDynamicValidatedSet(self, key, value)
  147. }
  148. }
  149. }
  150. // MARK: Notifications
  151. /**
  152. Registers a block to be called each time the object changes.
  153. The block will be asynchronously called after each write transaction which
  154. deletes the object or modifies any of the managed properties of the object,
  155. including self-assignments that set a property to its existing value.
  156. For write transactions performed on different threads or in different
  157. processes, the block will be called when the managing Realm is
  158. (auto)refreshed to a version including the changes, while for local write
  159. transactions it will be called at some point in the future after the write
  160. transaction is committed.
  161. Notifications are delivered via the standard run loop, and so can't be
  162. delivered while the run loop is blocked by other activity. When
  163. notifications can't be delivered instantly, multiple notifications may be
  164. coalesced into a single notification.
  165. Unlike with `List` and `Results`, there is no "initial" callback made after
  166. you add a new notification block.
  167. Only objects which are managed by a Realm can be observed in this way. You
  168. must retain the returned token for as long as you want updates to be sent
  169. to the block. To stop receiving updates, call `invalidate()` on the token.
  170. It is safe to capture a strong reference to the observed object within the
  171. callback block. There is no retain cycle due to that the callback is
  172. retained by the returned token and not by the object itself.
  173. - warning: This method cannot be called during a write transaction, or when
  174. the containing Realm is read-only.
  175. - parameter block: The block to call with information about changes to the object.
  176. - returns: A token which must be held for as long as you want updates to be delivered.
  177. */
  178. public func observe(_ block: @escaping (ObjectChange) -> Void) -> NotificationToken {
  179. return RLMObjectAddNotificationBlock(self, { names, oldValues, newValues, error in
  180. if let error = error {
  181. block(.error(error as NSError))
  182. return
  183. }
  184. guard let names = names, let newValues = newValues else {
  185. block(.deleted)
  186. return
  187. }
  188. block(.change((0..<newValues.count).map { i in
  189. PropertyChange(name: names[i], oldValue: oldValues?[i], newValue: newValues[i])
  190. }))
  191. })
  192. }
  193. // MARK: Dynamic list
  194. /**
  195. Returns a list of `DynamicObject`s for a given property name.
  196. - warning: This method is useful only in specialized circumstances, for example, when building
  197. components that integrate with Realm. If you are simply building an app on Realm, it is
  198. recommended to use instance variables or cast the values returned from key-value coding.
  199. - parameter propertyName: The name of the property.
  200. - returns: A list of `DynamicObject`s.
  201. :nodoc:
  202. */
  203. public func dynamicList(_ propertyName: String) -> List<DynamicObject> {
  204. return noWarnUnsafeBitCast(RLMDynamicGetByName(self, propertyName, true) as! RLMListBase,
  205. to: List<DynamicObject>.self)
  206. }
  207. // MARK: Comparison
  208. /**
  209. Returns whether two Realm objects are the same.
  210. Objects are considered the same if and only if they are both managed by the same
  211. Realm and point to the same underlying object in the database.
  212. - note: Equality comparison is implemented by `isEqual(_:)`. If the object type
  213. is defined with a primary key, `isEqual(_:)` behaves identically to this
  214. method. If the object type is not defined with a primary key,
  215. `isEqual(_:)` uses the `NSObject` behavior of comparing object identity.
  216. This method can be used to compare two objects for database equality
  217. whether or not their object type defines a primary key.
  218. - parameter object: The object to compare the receiver to.
  219. */
  220. public func isSameObject(as object: Object?) -> Bool {
  221. return RLMObjectBaseAreEqual(self, object)
  222. }
  223. // MARK: Private functions
  224. // FIXME: None of these functions should be exposed in the public interface.
  225. /**
  226. WARNING: This is an internal initializer not intended for public use.
  227. :nodoc:
  228. */
  229. public override required init(realm: RLMRealm, schema: RLMObjectSchema) {
  230. super.init(realm: realm, schema: schema)
  231. }
  232. /**
  233. WARNING: This is an internal initializer not intended for public use.
  234. :nodoc:
  235. */
  236. public override required init(value: Any, schema: RLMSchema) {
  237. super.init(value: value, schema: schema)
  238. }
  239. }
  240. /**
  241. Information about a specific property which changed in an `Object` change notification.
  242. */
  243. public struct PropertyChange {
  244. /**
  245. The name of the property which changed.
  246. */
  247. public let name: String
  248. /**
  249. Value of the property before the change occurred. This is not supplied if
  250. the change happened on the same thread as the notification and for `List`
  251. properties.
  252. For object properties this will give the object which was previously
  253. linked to, but that object will have its new values and not the values it
  254. had before the changes. This means that `previousValue` may be a deleted
  255. object, and you will need to check `isInvalidated` before accessing any
  256. of its properties.
  257. */
  258. public let oldValue: Any?
  259. /**
  260. The value of the property after the change occurred. This is not supplied
  261. for `List` properties and will always be nil.
  262. */
  263. public let newValue: Any?
  264. }
  265. /**
  266. Information about the changes made to an object which is passed to `Object`'s
  267. notification blocks.
  268. */
  269. public enum ObjectChange {
  270. /**
  271. If an error occurs, notification blocks are called one time with a `.error`
  272. result and an `NSError` containing details about the error. Currently the
  273. only errors which can occur are when opening the Realm on a background
  274. worker thread to calculate the change set. The callback will never be
  275. called again after `.error` is delivered.
  276. */
  277. case error(_: NSError)
  278. /**
  279. One or more of the properties of the object have been changed.
  280. */
  281. case change(_: [PropertyChange])
  282. /// The object has been deleted from the Realm.
  283. case deleted
  284. }
  285. /// Object interface which allows untyped getters and setters for Objects.
  286. /// :nodoc:
  287. public final class DynamicObject: Object {
  288. public override subscript(key: String) -> Any? {
  289. get {
  290. let value = RLMDynamicGetByName(self, key, false)
  291. if let array = value as? RLMArray<AnyObject> {
  292. return List<DynamicObject>(rlmArray: array)
  293. }
  294. return value
  295. }
  296. set(value) {
  297. RLMDynamicValidatedSet(self, key, value)
  298. }
  299. }
  300. /// :nodoc:
  301. public override func dynamicList(_ propertyName: String) -> List<DynamicObject> {
  302. return self[propertyName] as! List<DynamicObject>
  303. }
  304. /// :nodoc:
  305. public override func value(forUndefinedKey key: String) -> Any? {
  306. return self[key]
  307. }
  308. /// :nodoc:
  309. public override func setValue(_ value: Any?, forUndefinedKey key: String) {
  310. self[key] = value
  311. }
  312. /// :nodoc:
  313. public override class func shouldIncludeInDefaultSchema() -> Bool {
  314. return false
  315. }
  316. }
  317. /// :nodoc:
  318. /// Internal class. Do not use directly.
  319. @objc(RealmSwiftObjectUtil)
  320. public class ObjectUtil: NSObject {
  321. @objc private class func swiftVersion() -> NSString {
  322. #if SWIFT_PACKAGE
  323. return "5.1"
  324. #else
  325. return swiftLanguageVersion as NSString
  326. #endif
  327. }
  328. @objc private class func ignoredPropertiesForClass(_ type: AnyClass) -> NSArray? {
  329. if let type = type as? Object.Type {
  330. return type.ignoredProperties() as NSArray?
  331. }
  332. if let type = type as? RLMObject.Type {
  333. return type.ignoredProperties() as NSArray?
  334. }
  335. return nil
  336. }
  337. @objc private class func indexedPropertiesForClass(_ type: AnyClass) -> NSArray? {
  338. if let type = type as? Object.Type {
  339. return type.indexedProperties() as NSArray?
  340. }
  341. if let type = type as? RLMObject.Type {
  342. return type.indexedProperties() as NSArray?
  343. }
  344. return nil
  345. }
  346. @objc private class func linkingObjectsPropertiesForClass(_ type: AnyClass) -> NSDictionary? {
  347. if let type = type as? RLMObject.Type {
  348. return type.linkingObjectsProperties() as NSDictionary?
  349. }
  350. // Not used for Swift. getLinkingObjectsProperties(_:) is used instead.
  351. return nil
  352. }
  353. // If the property is a storage property for a lazy Swift property, return
  354. // the base property name (e.g. `foo.storage` becomes `foo`). Otherwise, nil.
  355. private static func baseName(forLazySwiftProperty name: String) -> String? {
  356. // A Swift lazy var shows up as two separate children on the reflection tree:
  357. // one named 'x', and another that is optional and is named 'x.storage'. Note
  358. // that '.' is illegal in either a Swift or Objective-C property name.
  359. if let storageRange = name.range(of: ".storage", options: [.anchored, .backwards]) {
  360. #if swift(>=4.0)
  361. return String(name[..<storageRange.lowerBound])
  362. #else
  363. return name.substring(to: storageRange.lowerBound)
  364. #endif
  365. }
  366. // Xcode 11 changed the name of the storage property to "$__lazy_storage_$_propName"
  367. #if swift(>=4.0)
  368. if let storageRange = name.range(of: "$__lazy_storage_$_", options: [.anchored]) {
  369. return String(name[storageRange.upperBound...])
  370. }
  371. #endif
  372. return nil
  373. }
  374. // Reflect an object, returning only children representing managed Realm properties.
  375. private static func getNonIgnoredMirrorChildren(for object: Any) -> [Mirror.Child] {
  376. let ignoredPropNames: Set<String>
  377. if let realmObject = object as? Object {
  378. ignoredPropNames = Set(type(of: realmObject).ignoredProperties())
  379. } else {
  380. ignoredPropNames = Set()
  381. }
  382. // No HKT in Swift, unfortunately
  383. return Mirror(reflecting: object).children.filter { (prop: Mirror.Child) -> Bool in
  384. guard let label = prop.label else {
  385. return false
  386. }
  387. if ignoredPropNames.contains(label) {
  388. // Ignored property.
  389. return false
  390. }
  391. if let lazyBaseName = baseName(forLazySwiftProperty: label) {
  392. if ignoredPropNames.contains(lazyBaseName) {
  393. // Ignored lazy property.
  394. return false
  395. }
  396. if object is RLMObject {
  397. // Implicitly ignore lazy properties on RLMObject subclasses
  398. // FIXME: should align RLMObject/Object behavior in 4.0
  399. return false
  400. }
  401. // Managed lazy property; not currently supported.
  402. // FIXME: revisit this once Swift gets property behaviors/property macros.
  403. throwRealmException("Lazy managed property '\(lazyBaseName)' is not allowed on a Realm Swift object"
  404. + " class. Either add the property to the ignored properties list or make it non-lazy.")
  405. }
  406. return true
  407. }
  408. }
  409. // Build optional property metadata for a given property.
  410. // swiftlint:disable:next cyclomatic_complexity
  411. private static func getOptionalPropertyMetadata(for child: Mirror.Child, at index: Int) -> RLMSwiftPropertyMetadata? {
  412. guard let name = child.label else {
  413. return nil
  414. }
  415. let mirror = Mirror(reflecting: child.value)
  416. let type = mirror.subjectType
  417. let code: PropertyType
  418. if type is Optional<String>.Type || type is Optional<NSString>.Type {
  419. code = .string
  420. } else if type is Optional<Date>.Type {
  421. code = .date
  422. } else if type is Optional<Data>.Type {
  423. code = .data
  424. } else if type is Optional<Object>.Type {
  425. code = .object
  426. } else if type is RealmOptional<Int>.Type ||
  427. type is RealmOptional<Int8>.Type ||
  428. type is RealmOptional<Int16>.Type ||
  429. type is RealmOptional<Int32>.Type ||
  430. type is RealmOptional<Int64>.Type {
  431. code = .int
  432. } else if type is RealmOptional<Float>.Type {
  433. code = .float
  434. } else if type is RealmOptional<Double>.Type {
  435. code = .double
  436. } else if type is RealmOptional<Bool>.Type {
  437. code = .bool
  438. } else if child.value is RLMOptionalBase {
  439. throwRealmException("'\(type)' is not a valid RealmOptional type.")
  440. code = .int // ignored
  441. } else if mirror.displayStyle == .optional || type is ExpressibleByNilLiteral.Type {
  442. return RLMSwiftPropertyMetadata(forNilLiteralOptionalProperty: name)
  443. } else {
  444. return nil
  445. }
  446. return RLMSwiftPropertyMetadata(forOptionalProperty: name, type: code)
  447. }
  448. @objc private class func getSwiftProperties(_ object: Any) -> [RLMSwiftPropertyMetadata] {
  449. return getNonIgnoredMirrorChildren(for: object).enumerated().map { idx, prop in
  450. if let value = prop.value as? LinkingObjectsBase {
  451. return RLMSwiftPropertyMetadata(forLinkingObjectsProperty: prop.label!,
  452. className: value.objectClassName,
  453. linkedPropertyName: value.propertyName)
  454. } else if prop.value is RLMListBase {
  455. return RLMSwiftPropertyMetadata(forListProperty: prop.label!)
  456. } else if let optional = getOptionalPropertyMetadata(for: prop, at: idx) {
  457. return optional
  458. } else {
  459. return RLMSwiftPropertyMetadata(forOtherProperty: prop.label!)
  460. }
  461. }
  462. }
  463. @objc private class func requiredPropertiesForClass(_: Any) -> [String] {
  464. return []
  465. }
  466. }
  467. // MARK: AssistedObjectiveCBridgeable
  468. // FIXME: Remove when `as! Self` can be written
  469. private func forceCastToInferred<T, V>(_ x: T) -> V {
  470. return x as! V
  471. }
  472. extension Object: AssistedObjectiveCBridgeable {
  473. static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Self {
  474. return forceCastToInferred(objectiveCValue)
  475. }
  476. var bridged: (objectiveCValue: Any, metadata: Any?) {
  477. return (objectiveCValue: unsafeCastToRLMObject(), metadata: nil)
  478. }
  479. }
  480. // MARK: - Migration assistance
  481. extension Object {
  482. /// :nodoc:
  483. @available(*, unavailable, renamed: "observe()")
  484. public func addNotificationBlock(_ block: @escaping (ObjectChange) -> Void) -> NotificationToken {
  485. fatalError()
  486. }
  487. #if os(OSX)
  488. #else
  489. /// :nodoc:
  490. @available(*, unavailable, renamed: "isSameObject(as:)") public func isEqual(to object: Any?) -> Bool {
  491. fatalError()
  492. }
  493. #endif
  494. }