Object.swift 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  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. - `Decimal128`
  42. - `ObjectId`
  43. - `@objc enum` which has been delcared as conforming to `RealmEnum`.
  44. - `RealmOptional<Value>` for optional numeric properties
  45. - `Object` subclasses, to model many-to-one relationships
  46. - `EmbeddedObject` subclasses, to model owning one-to-one relationships
  47. - `List<Element>`, to model many-to-many relationships
  48. `String`, `NSString`, `Date`, `NSDate`, `Data`, `NSData`, `Decimal128`, and `ObjectId` properties
  49. can be declared as optional. `Object` and `EmbeddedObject` subclasses *must* be declared as optional.
  50. `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `Float`, `Double`, `Bool`, enum, and `List` properties cannot.
  51. To store an optional number, use `RealmOptional<Int>`, `RealmOptional<Float>`, `RealmOptional<Double>`, or
  52. `RealmOptional<Bool>` instead, which wraps an optional numeric value. Lists cannot be optional at all.
  53. All property types except for `List` and `RealmOptional` *must* be declared as `@objc dynamic var`. `List` and
  54. `RealmOptional` properties must be declared as non-dynamic `let` properties. Swift `lazy` properties are not allowed.
  55. Note that none of the restrictions listed above apply to properties that are configured to be ignored by Realm.
  56. ### Querying
  57. You can retrieve all objects of a given type from a Realm by calling the `objects(_:)` instance method.
  58. ### Relationships
  59. See our [Cocoa guide](http://realm.io/docs/cocoa) for more details.
  60. */
  61. public typealias Object = RealmSwiftObject
  62. extension Object: RealmCollectionValue {
  63. /// :nodoc:
  64. public static func _rlmArray() -> RLMArray<AnyObject> {
  65. return RLMArray(objectClassName: className())
  66. }
  67. // MARK: Initializers
  68. /**
  69. Creates an unmanaged instance of a Realm object.
  70. The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or
  71. dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing one element for each
  72. managed property. An exception will be thrown if any required properties are not present and those properties were
  73. not defined with default values.
  74. When passing in an `Array` as the `value` argument, all properties must be present, valid and in the same order as
  75. the properties defined in the model.
  76. Call `add(_:)` on a `Realm` instance to add an unmanaged object into that Realm.
  77. - parameter value: The value used to populate the object.
  78. */
  79. public convenience init(value: Any) {
  80. self.init()
  81. RLMInitializeWithValue(self, value, .partialPrivateShared())
  82. }
  83. // MARK: Properties
  84. /// The Realm which manages the object, or `nil` if the object is unmanaged.
  85. public var realm: Realm? {
  86. if let rlmReam = RLMObjectBaseRealm(self) {
  87. return Realm(rlmReam)
  88. }
  89. return nil
  90. }
  91. /// The object schema which lists the managed properties for the object.
  92. public var objectSchema: ObjectSchema {
  93. return ObjectSchema(RLMObjectBaseObjectSchema(self)!)
  94. }
  95. /// Indicates if the object can no longer be accessed because it is now invalid.
  96. ///
  97. /// An object can no longer be accessed if the object has been deleted from the Realm that manages it, or if
  98. /// `invalidate()` is called on that Realm. This property is key-value observable.
  99. @objc dynamic open override var isInvalidated: Bool { return super.isInvalidated }
  100. /// A human-readable description of the object.
  101. open override var description: String { return super.description }
  102. /**
  103. WARNING: This is an internal helper method not intended for public use.
  104. It is not considered part of the public API.
  105. :nodoc:
  106. */
  107. public override final class func _getProperties() -> [RLMProperty] {
  108. return ObjectUtil.getSwiftProperties(self)
  109. }
  110. // MARK: Object Customization
  111. /**
  112. Override this method to specify the name of a property to be used as the primary key.
  113. Only properties of types `String` and `Int` can be designated as the primary key. Primary key properties enforce
  114. uniqueness for each value whenever the property is set, which incurs minor overhead. Indexes are created
  115. automatically for primary key properties.
  116. - returns: The name of the property designated as the primary key, or `nil` if the model has no primary key.
  117. */
  118. @objc open class func primaryKey() -> String? { return nil }
  119. /**
  120. Override this method to specify the names of properties to ignore. These properties will not be managed by
  121. the Realm that manages the object.
  122. - returns: An array of property names to ignore.
  123. */
  124. @objc open class func ignoredProperties() -> [String] { return [] }
  125. /**
  126. Returns an array of property names for properties which should be indexed.
  127. Only string, integer, boolean, `Date`, and `NSDate` properties are supported.
  128. - returns: An array of property names.
  129. */
  130. @objc open class func indexedProperties() -> [String] { return [] }
  131. // MARK: Key-Value Coding & Subscripting
  132. /// Returns or sets the value of the property with the given name.
  133. @objc open subscript(key: String) -> Any? {
  134. get {
  135. if realm == nil {
  136. return value(forKey: key)
  137. }
  138. return dynamicGet(key: key)
  139. }
  140. set(value) {
  141. if realm == nil {
  142. setValue(value, forKey: key)
  143. } else {
  144. RLMDynamicValidatedSet(self, key, value)
  145. }
  146. }
  147. }
  148. private func dynamicGet(key: String) -> Any? {
  149. let objectSchema = RLMObjectBaseObjectSchema(self)!
  150. guard let prop = objectSchema[key] else {
  151. throwRealmException("Invalid property name '\(key) for class \(objectSchema.className)")
  152. }
  153. if let accessor = prop.swiftAccessor {
  154. return accessor.get(Unmanaged.passUnretained(self).toOpaque() + ivar_getOffset(prop.swiftIvar!))
  155. }
  156. if let ivar = prop.swiftIvar, prop.array {
  157. return object_getIvar(self, ivar)
  158. }
  159. return RLMDynamicGet(self, prop)
  160. }
  161. // MARK: Notifications
  162. /**
  163. Registers a block to be called each time the object changes.
  164. The block will be asynchronously called after each write transaction which
  165. deletes the object or modifies any of the managed properties of the object,
  166. including self-assignments that set a property to its existing value.
  167. For write transactions performed on different threads or in different
  168. processes, the block will be called when the managing Realm is
  169. (auto)refreshed to a version including the changes, while for local write
  170. transactions it will be called at some point in the future after the write
  171. transaction is committed.
  172. If no queue is given, notifications are delivered via the standard run
  173. loop, and so can't be delivered while the run loop is blocked by other
  174. activity. If a queue is given, notifications are delivered to that queue
  175. instead. When notifications can't be delivered instantly, multiple
  176. notifications may be coalesced into a single notification.
  177. Unlike with `List` and `Results`, there is no "initial" callback made after
  178. you add a new notification block.
  179. Only objects which are managed by a Realm can be observed in this way. You
  180. must retain the returned token for as long as you want updates to be sent
  181. to the block. To stop receiving updates, call `invalidate()` on the token.
  182. It is safe to capture a strong reference to the observed object within the
  183. callback block. There is no retain cycle due to that the callback is
  184. retained by the returned token and not by the object itself.
  185. - warning: This method cannot be called during a write transaction, or when
  186. the containing Realm is read-only.
  187. - parameter queue: The serial dispatch queue to receive notification on. If
  188. `nil`, notifications are delivered to the current thread.
  189. - parameter block: The block to call with information about changes to the object.
  190. - returns: A token which must be held for as long as you want updates to be delivered.
  191. */
  192. public func observe<T: RLMObjectBase>(on queue: DispatchQueue? = nil,
  193. _ block: @escaping (ObjectChange<T>) -> Void) -> NotificationToken {
  194. return _observe(on: queue, block)
  195. }
  196. // MARK: Dynamic list
  197. /**
  198. Returns a list of `DynamicObject`s for a given property name.
  199. - warning: This method is useful only in specialized circumstances, for example, when building
  200. components that integrate with Realm. If you are simply building an app on Realm, it is
  201. recommended to use instance variables or cast the values returned from key-value coding.
  202. - parameter propertyName: The name of the property.
  203. - returns: A list of `DynamicObject`s.
  204. :nodoc:
  205. */
  206. public func dynamicList(_ propertyName: String) -> List<DynamicObject> {
  207. if let dynamic = self as? DynamicObject {
  208. return dynamic[propertyName] as! List<DynamicObject>
  209. }
  210. return noWarnUnsafeBitCast(dynamicGet(key: propertyName) as! RLMListBase,
  211. to: List<DynamicObject>.self)
  212. }
  213. // MARK: Comparison
  214. /**
  215. Returns whether two Realm objects are the same.
  216. Objects are considered the same if and only if they are both managed by the same
  217. Realm and point to the same underlying object in the database.
  218. - note: Equality comparison is implemented by `isEqual(_:)`. If the object type
  219. is defined with a primary key, `isEqual(_:)` behaves identically to this
  220. method. If the object type is not defined with a primary key,
  221. `isEqual(_:)` uses the `NSObject` behavior of comparing object identity.
  222. This method can be used to compare two objects for database equality
  223. whether or not their object type defines a primary key.
  224. - parameter object: The object to compare the receiver to.
  225. */
  226. public func isSameObject(as object: Object?) -> Bool {
  227. return RLMObjectBaseAreEqual(self, object)
  228. }
  229. }
  230. extension Object: ThreadConfined {
  231. /**
  232. Indicates if this object is frozen.
  233. - see: `Object.freeze()`
  234. */
  235. public var isFrozen: Bool { return realm?.isFrozen ?? false }
  236. /**
  237. Returns a frozen (immutable) snapshot of this object.
  238. The frozen copy is an immutable object which contains the same data as this
  239. object currently contains, but will not update when writes are made to the
  240. containing Realm. Unlike live objects, frozen objects can be accessed from any
  241. thread.
  242. - warning: Holding onto a frozen object for an extended period while performing write
  243. transaction on the Realm may result in the Realm file growing to large sizes. See
  244. `Realm.Configuration.maximumNumberOfActiveVersions` for more information.
  245. - warning: This method can only be called on a managed object.
  246. */
  247. public func freeze() -> Self {
  248. guard let realm = realm else { throwRealmException("Unmanaged objects cannot be frozen.") }
  249. return realm.freeze(self)
  250. }
  251. /**
  252. Returns a live (mutable) reference of this object.
  253. This method creates a managed accessor to a live copy of the same frozen object.
  254. Will return self if called on an already live object.
  255. */
  256. public func thaw() -> Self? {
  257. guard let realm = realm else { throwRealmException("Unmanaged objects cannot be thawed.") }
  258. return realm.thaw(self)
  259. }
  260. }
  261. /**
  262. Information about a specific property which changed in an `Object` change notification.
  263. */
  264. @frozen public struct PropertyChange {
  265. /**
  266. The name of the property which changed.
  267. */
  268. public let name: String
  269. /**
  270. Value of the property before the change occurred. This is not supplied if
  271. the change happened on the same thread as the notification and for `List`
  272. properties.
  273. For object properties this will give the object which was previously
  274. linked to, but that object will have its new values and not the values it
  275. had before the changes. This means that `previousValue` may be a deleted
  276. object, and you will need to check `isInvalidated` before accessing any
  277. of its properties.
  278. */
  279. public let oldValue: Any?
  280. /**
  281. The value of the property after the change occurred. This is not supplied
  282. for `List` properties and will always be nil.
  283. */
  284. public let newValue: Any?
  285. }
  286. /**
  287. Information about the changes made to an object which is passed to `Object`'s
  288. notification blocks.
  289. */
  290. @frozen public enum ObjectChange<T: ObjectBase> {
  291. /**
  292. If an error occurs, notification blocks are called one time with a `.error`
  293. result and an `NSError` containing details about the error. Currently the
  294. only errors which can occur are when opening the Realm on a background
  295. worker thread to calculate the change set. The callback will never be
  296. called again after `.error` is delivered.
  297. */
  298. case error(_ error: NSError)
  299. /**
  300. One or more of the properties of the object have been changed.
  301. */
  302. case change(_: T, _: [PropertyChange])
  303. /// The object has been deleted from the Realm.
  304. case deleted
  305. }
  306. /// Object interface which allows untyped getters and setters for Objects.
  307. /// :nodoc:
  308. public final class DynamicObject: Object {
  309. public override subscript(key: String) -> Any? {
  310. get {
  311. let value = RLMDynamicGetByName(self, key)
  312. if let array = value as? RLMArray<AnyObject> {
  313. return List<DynamicObject>(objc: array)
  314. }
  315. return value
  316. }
  317. set(value) {
  318. RLMDynamicValidatedSet(self, key, value)
  319. }
  320. }
  321. /// :nodoc:
  322. public override func value(forUndefinedKey key: String) -> Any? {
  323. return self[key]
  324. }
  325. /// :nodoc:
  326. public override func setValue(_ value: Any?, forUndefinedKey key: String) {
  327. self[key] = value
  328. }
  329. /// :nodoc:
  330. public override class func shouldIncludeInDefaultSchema() -> Bool {
  331. return false
  332. }
  333. }
  334. /**
  335. An enum type which can be stored on a Realm Object.
  336. Only `@objc` enums backed by an Int can be stored on a Realm object, and the
  337. enum type must explicitly conform to this protocol. For example:
  338. ```
  339. @objc enum MyEnum: Int, RealmEnum {
  340. case first = 1
  341. case second = 2
  342. case third = 7
  343. }
  344. class MyModel: Object {
  345. @objc dynamic enumProperty = MyEnum.first
  346. let optionalEnumProperty = RealmOptional<MyEnum>()
  347. }
  348. ```
  349. */
  350. public protocol RealmEnum: RealmOptionalType, _ManagedPropertyType {
  351. /// :nodoc:
  352. // swiftlint:disable:next identifier_name
  353. static func _rlmToRawValue(_ value: Any) -> Any
  354. /// :nodoc:
  355. // swiftlint:disable:next identifier_name
  356. static func _rlmFromRawValue(_ value: Any) -> Any
  357. }
  358. // MARK: - Implementation
  359. /// :nodoc:
  360. public extension RealmEnum where Self: RawRepresentable, Self.RawValue: _ManagedPropertyType {
  361. // swiftlint:disable:next identifier_name
  362. static func _rlmToRawValue(_ value: Any) -> Any {
  363. return (value as! Self).rawValue
  364. }
  365. // swiftlint:disable:next identifier_name
  366. static func _rlmFromRawValue(_ value: Any) -> Any {
  367. return Self.init(rawValue: value as! RawValue)!
  368. }
  369. // swiftlint:disable:next identifier_name
  370. static func _rlmProperty(_ prop: RLMProperty) {
  371. RawValue._rlmProperty(prop)
  372. }
  373. }
  374. // A type which can be a managed property on a Realm object
  375. /// :nodoc:
  376. public protocol _ManagedPropertyType {
  377. // swiftlint:disable:next identifier_name
  378. func _rlmProperty(_ prop: RLMProperty)
  379. // swiftlint:disable:next identifier_name
  380. static func _rlmProperty(_ prop: RLMProperty)
  381. // swiftlint:disable:next identifier_name
  382. static func _rlmRequireObjc() -> Bool
  383. }
  384. /// :nodoc:
  385. extension _ManagedPropertyType {
  386. // swiftlint:disable:next identifier_name
  387. public func _rlmProperty(_ prop: RLMProperty) { }
  388. // swiftlint:disable:next identifier_name
  389. public static func _rlmRequireObjc() -> Bool { return true }
  390. }
  391. /// :nodoc:
  392. extension Int: _ManagedPropertyType {
  393. // swiftlint:disable:next identifier_name
  394. public static func _rlmProperty(_ prop: RLMProperty) {
  395. prop.type = .int
  396. }
  397. }
  398. /// :nodoc:
  399. extension Int8: _ManagedPropertyType {
  400. // swiftlint:disable:next identifier_name
  401. public static func _rlmProperty(_ prop: RLMProperty) {
  402. prop.type = .int
  403. }
  404. }
  405. /// :nodoc:
  406. extension Int16: _ManagedPropertyType {
  407. // swiftlint:disable:next identifier_name
  408. public static func _rlmProperty(_ prop: RLMProperty) {
  409. prop.type = .int
  410. }
  411. }
  412. /// :nodoc:
  413. extension Int32: _ManagedPropertyType {
  414. // swiftlint:disable:next identifier_name
  415. public static func _rlmProperty(_ prop: RLMProperty) {
  416. prop.type = .int
  417. }
  418. }
  419. /// :nodoc:
  420. extension Int64: _ManagedPropertyType {
  421. // swiftlint:disable:next identifier_name
  422. public static func _rlmProperty(_ prop: RLMProperty) {
  423. prop.type = .int
  424. }
  425. }
  426. /// :nodoc:
  427. extension Float: _ManagedPropertyType {
  428. // swiftlint:disable:next identifier_name
  429. public static func _rlmProperty(_ prop: RLMProperty) {
  430. prop.type = .float
  431. }
  432. }
  433. /// :nodoc:
  434. extension Double: _ManagedPropertyType {
  435. // swiftlint:disable:next identifier_name
  436. public static func _rlmProperty(_ prop: RLMProperty) {
  437. prop.type = .double
  438. }
  439. }
  440. /// :nodoc:
  441. extension Bool: _ManagedPropertyType {
  442. // swiftlint:disable:next identifier_name
  443. public static func _rlmProperty(_ prop: RLMProperty) {
  444. prop.type = .bool
  445. }
  446. }
  447. /// :nodoc:
  448. extension String: _ManagedPropertyType {
  449. // swiftlint:disable:next identifier_name
  450. public static func _rlmProperty(_ prop: RLMProperty) {
  451. prop.type = .string
  452. }
  453. }
  454. /// :nodoc:
  455. extension NSString: _ManagedPropertyType {
  456. // swiftlint:disable:next identifier_name
  457. public static func _rlmProperty(_ prop: RLMProperty) {
  458. prop.type = .string
  459. }
  460. }
  461. /// :nodoc:
  462. extension Data: _ManagedPropertyType {
  463. // swiftlint:disable:next identifier_name
  464. public static func _rlmProperty(_ prop: RLMProperty) {
  465. prop.type = .data
  466. }
  467. }
  468. /// :nodoc:
  469. extension NSData: _ManagedPropertyType {
  470. // swiftlint:disable:next identifier_name
  471. public static func _rlmProperty(_ prop: RLMProperty) {
  472. prop.type = .data
  473. }
  474. }
  475. /// :nodoc:
  476. extension Date: _ManagedPropertyType {
  477. // swiftlint:disable:next identifier_name
  478. public static func _rlmProperty(_ prop: RLMProperty) {
  479. prop.type = .date
  480. }
  481. }
  482. /// :nodoc:
  483. extension NSDate: _ManagedPropertyType {
  484. // swiftlint:disable:next identifier_name
  485. public static func _rlmProperty(_ prop: RLMProperty) {
  486. prop.type = .date
  487. }
  488. }
  489. /// :nodoc:
  490. extension Decimal128: _ManagedPropertyType {
  491. // swiftlint:disable:next identifier_name
  492. public static func _rlmProperty(_ prop: RLMProperty) {
  493. prop.type = .decimal128
  494. }
  495. }
  496. /// :nodoc:
  497. extension ObjectId: _ManagedPropertyType {
  498. // swiftlint:disable:next identifier_name
  499. public static func _rlmProperty(_ prop: RLMProperty) {
  500. prop.type = .objectId
  501. }
  502. }
  503. /// :nodoc:
  504. extension Object: _ManagedPropertyType {
  505. // swiftlint:disable:next identifier_name
  506. public static func _rlmProperty(_ prop: RLMProperty) {
  507. if !prop.optional && !prop.array {
  508. throwRealmException("Object property '\(prop.name)' must be marked as optional.")
  509. }
  510. if prop.optional && prop.array {
  511. throwRealmException("List<\(className())> property '\(prop.name)' must not be marked as optional.")
  512. }
  513. prop.type = .object
  514. prop.objectClassName = className()
  515. }
  516. }
  517. /// :nodoc:
  518. extension EmbeddedObject: _ManagedPropertyType {
  519. // swiftlint:disable:next identifier_name
  520. public static func _rlmProperty(_ prop: RLMProperty) {
  521. Object._rlmProperty(prop)
  522. prop.objectClassName = className()
  523. }
  524. }
  525. /// :nodoc:
  526. extension List: _ManagedPropertyType where Element: _ManagedPropertyType {
  527. // swiftlint:disable:next identifier_name
  528. public static func _rlmProperty(_ prop: RLMProperty) {
  529. prop.array = true
  530. Element._rlmProperty(prop)
  531. }
  532. // swiftlint:disable:next identifier_name
  533. public static func _rlmRequireObjc() -> Bool { return false }
  534. }
  535. /// :nodoc:
  536. class LinkingObjectsAccessor<Element: ObjectBase>: RLMManagedPropertyAccessor where Element: RealmCollectionValue {
  537. @objc override class func initializeObject(_ ptr: UnsafeMutableRawPointer,
  538. parent: RLMObjectBase, property: RLMProperty) {
  539. ptr.assumingMemoryBound(to: LinkingObjects<Element>.self).pointee.handle = RLMLinkingObjectsHandle(object: parent, property: property)
  540. }
  541. @objc override class func get(_ ptr: UnsafeMutableRawPointer) -> Any {
  542. return ptr.assumingMemoryBound(to: LinkingObjects<Element>.self).pointee
  543. }
  544. }
  545. /// :nodoc:
  546. extension LinkingObjects: _ManagedPropertyType {
  547. // swiftlint:disable:next identifier_name
  548. public static func _rlmProperty(_ prop: RLMProperty) {
  549. prop.array = true
  550. prop.type = .linkingObjects
  551. prop.objectClassName = Element.className()
  552. prop.swiftAccessor = LinkingObjectsAccessor<Element>.self
  553. }
  554. // swiftlint:disable:next identifier_name
  555. public func _rlmProperty(_ prop: RLMProperty) {
  556. prop.linkOriginPropertyName = self.propertyName
  557. }
  558. // swiftlint:disable:next identifier_name
  559. public static func _rlmRequireObjc() -> Bool { return false }
  560. }
  561. /// :nodoc:
  562. extension Optional: _ManagedPropertyType where Wrapped: _ManagedPropertyType {
  563. // swiftlint:disable:next identifier_name
  564. public static func _rlmProperty(_ prop: RLMProperty) {
  565. prop.optional = true
  566. Wrapped._rlmProperty(prop)
  567. }
  568. }
  569. /// :nodoc:
  570. extension RealmOptional: _ManagedPropertyType where Value: _ManagedPropertyType {
  571. // swiftlint:disable:next identifier_name
  572. public static func _rlmProperty(_ prop: RLMProperty) {
  573. prop.optional = true
  574. Value._rlmProperty(prop)
  575. }
  576. // swiftlint:disable:next identifier_name
  577. public static func _rlmRequireObjc() -> Bool { return false }
  578. }
  579. /// :nodoc:
  580. internal class ObjectUtil {
  581. private static let runOnce: Void = {
  582. RLMSwiftAsFastEnumeration = { (obj: Any) -> Any? in
  583. // Intermediate cast to AnyObject due to https://bugs.swift.org/browse/SR-8651
  584. if let collection = obj as AnyObject as? _RealmCollectionEnumerator {
  585. return collection._asNSFastEnumerator()
  586. }
  587. return nil
  588. }
  589. }()
  590. // If the property is a storage property for a lazy Swift property, return
  591. // the base property name (e.g. `foo.storage` becomes `foo`). Otherwise, nil.
  592. private static func baseName(forLazySwiftProperty name: String) -> String? {
  593. // A Swift lazy var shows up as two separate children on the reflection tree:
  594. // one named 'x', and another that is optional and is named 'x.storage'. Note
  595. // that '.' is illegal in either a Swift or Objective-C property name.
  596. if let storageRange = name.range(of: ".storage", options: [.anchored, .backwards]) {
  597. return String(name[..<storageRange.lowerBound])
  598. }
  599. // Xcode 11 changed the name of the storage property to "$__lazy_storage_$_propName"
  600. if let storageRange = name.range(of: "$__lazy_storage_$_", options: [.anchored]) {
  601. return String(name[storageRange.upperBound...])
  602. }
  603. return nil
  604. }
  605. // Reflect an object, returning only children representing managed Realm properties.
  606. private static func getNonIgnoredMirrorChildren(for object: Any) -> [Mirror.Child] {
  607. let ignoredPropNames: Set<String>
  608. if let realmObject = object as? Object {
  609. ignoredPropNames = Set(type(of: realmObject).ignoredProperties())
  610. } else {
  611. ignoredPropNames = Set()
  612. }
  613. return Mirror(reflecting: object).children.filter { (prop: Mirror.Child) -> Bool in
  614. guard let label = prop.label else {
  615. return false
  616. }
  617. if ignoredPropNames.contains(label) {
  618. return false
  619. }
  620. if let lazyBaseName = baseName(forLazySwiftProperty: label) {
  621. if ignoredPropNames.contains(lazyBaseName) {
  622. return false
  623. }
  624. // Managed lazy property; not currently supported.
  625. // FIXME: revisit this once Swift gets property behaviors/property macros.
  626. throwRealmException("Lazy managed property '\(lazyBaseName)' is not allowed on a Realm Swift object"
  627. + " class. Either add the property to the ignored properties list or make it non-lazy.")
  628. }
  629. return true
  630. }
  631. }
  632. internal class func getSwiftProperties(_ cls: RLMObjectBase.Type) -> [RLMProperty] {
  633. _ = ObjectUtil.runOnce
  634. let object = cls.init()
  635. var indexedProperties: Set<String>!
  636. let columnNames = cls._realmColumnNames()
  637. if let realmObject = object as? Object {
  638. indexedProperties = Set(type(of: realmObject).indexedProperties())
  639. } else {
  640. indexedProperties = Set()
  641. }
  642. return getNonIgnoredMirrorChildren(for: object).compactMap { prop in
  643. guard let label = prop.label else { return nil }
  644. var rawValue = prop.value
  645. if let value = rawValue as? RealmEnum {
  646. rawValue = type(of: value)._rlmToRawValue(value)
  647. }
  648. guard let value = rawValue as? _ManagedPropertyType else {
  649. if class_getProperty(cls, label) != nil {
  650. throwRealmException("Property \(cls).\(label) is declared as \(type(of: prop.value)), which is not a supported managed Object property type. If it is not supposed to be a managed property, either add it to `ignoredProperties()` or do not declare it as `@objc dynamic`. See https://realm.io/docs/swift/latest/api/Classes/Object.html for more information.")
  651. }
  652. if prop.value as? RealmOptionalProtocol != nil {
  653. throwRealmException("Property \(cls).\(label) has unsupported RealmOptional type \(type(of: prop.value)). Extending RealmOptionalType with custom types is not currently supported. ")
  654. }
  655. return nil
  656. }
  657. RLMValidateSwiftPropertyName(label)
  658. let valueType = type(of: value)
  659. let property = RLMProperty()
  660. property.name = label
  661. property.indexed = indexedProperties.contains(label)
  662. property.columnName = columnNames?[label]
  663. valueType._rlmProperty(property)
  664. value._rlmProperty(property)
  665. if let objcProp = class_getProperty(cls, label) {
  666. var count: UInt32 = 0
  667. let attrs = property_copyAttributeList(objcProp, &count)!
  668. defer {
  669. free(attrs)
  670. }
  671. var computed = true
  672. for i in 0..<Int(count) {
  673. let attr = attrs[i]
  674. switch attr.name[0] {
  675. case Int8(UInt8(ascii: "R")): // Read only
  676. return nil
  677. case Int8(UInt8(ascii: "V")): // Ivar name
  678. computed = false
  679. case Int8(UInt8(ascii: "G")): // Getter name
  680. property.getterName = String(cString: attr.value)
  681. case Int8(UInt8(ascii: "S")): // Setter name
  682. property.setterName = String(cString: attr.value)
  683. default:
  684. break
  685. }
  686. }
  687. // If there's no ivar name and no ivar with the same name as
  688. // the property then this is a computed property and we should
  689. // implicitly ignore it
  690. if computed && class_getInstanceVariable(cls, label) == nil {
  691. return nil
  692. }
  693. } else if valueType._rlmRequireObjc() {
  694. // Implicitly ignore non-@objc dynamic properties
  695. return nil
  696. } else {
  697. property.swiftIvar = class_getInstanceVariable(cls, label)
  698. }
  699. property.updateAccessors()
  700. return property
  701. }
  702. }
  703. }
  704. // MARK: AssistedObjectiveCBridgeable
  705. // FIXME: Remove when `as! Self` can be written
  706. private func forceCastToInferred<T, V>(_ x: T) -> V {
  707. return x as! V
  708. }
  709. extension Object: AssistedObjectiveCBridgeable {
  710. internal static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Self {
  711. return forceCastToInferred(objectiveCValue)
  712. }
  713. internal var bridged: (objectiveCValue: Any, metadata: Any?) {
  714. return (objectiveCValue: unsafeCastToRLMObject(), metadata: nil)
  715. }
  716. }