1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2014 Realm Inc.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 ////////////////////////////////////////////////////////////////////////////
24 `Object` is a class used to define Realm model objects.
26 In Realm you define your model classes by subclassing `Object` and adding properties to be managed.
27 You then instantiate and use your custom subclasses instead of using the `Object` class directly.
31 @objc dynamic var name: String = ""
32 @objc dynamic var adopted: Bool = false
33 let siblings = List<Dog>()
37 ### Supported property types
39 - `String`, `NSString`
41 - `Int8`, `Int16`, `Int32`, `Int64`
47 - `RealmOptional<Value>` for optional numeric properties
48 - `Object` subclasses, to model many-to-one relationships
49 - `List<Element>`, to model many-to-many relationships
51 `String`, `NSString`, `Date`, `NSDate`, `Data`, `NSData` and `Object` subclass properties can be declared as optional.
52 `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `Float`, `Double`, `Bool`, and `List` properties cannot. To store an optional
53 number, use `RealmOptional<Int>`, `RealmOptional<Float>`, `RealmOptional<Double>`, or `RealmOptional<Bool>` instead,
54 which wraps an optional numeric value.
56 All property types except for `List` and `RealmOptional` *must* be declared as `@objc dynamic var`. `List` and
57 `RealmOptional` properties must be declared as non-dynamic `let` properties. Swift `lazy` properties are not allowed.
59 Note that none of the restrictions listed above apply to properties that are configured to be ignored by Realm.
63 You can retrieve all objects of a given type from a Realm by calling the `objects(_:)` instance method.
67 See our [Cocoa guide](http://realm.io/docs/cocoa) for more details.
69 @objc(RealmSwiftObject)
70 open class Object: RLMObjectBase, ThreadConfined, RealmCollectionValue {
72 // swiftlint:disable:next identifier_name
73 public static func _rlmArray() -> RLMArray<AnyObject> {
74 return RLMArray(objectClassName: className())
80 Creates an unmanaged instance of a Realm object.
82 Call `add(_:)` on a `Realm` instance to add an unmanaged object into that Realm.
84 - see: `Realm().add(_:)`
86 public override required init() {
91 Creates an unmanaged instance of a Realm object.
93 The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or
94 dictionary returned from the methods in `NSJSONSerialization`, or an `Array` containing one element for each
95 managed property. An exception will be thrown if any required properties are not present and those properties were
96 not defined with default values.
98 When passing in an `Array` as the `value` argument, all properties must be present, valid and in the same order as
99 the properties defined in the model.
101 Call `add(_:)` on a `Realm` instance to add an unmanaged object into that Realm.
103 - parameter value: The value used to populate the object.
105 public init(value: Any) {
106 super.init(value: value, schema: .partialPrivateShared())
112 /// The Realm which manages the object, or `nil` if the object is unmanaged.
113 public var realm: Realm? {
114 if let rlmReam = RLMObjectBaseRealm(self) {
115 return Realm(rlmReam)
120 /// The object schema which lists the managed properties for the object.
121 public var objectSchema: ObjectSchema {
122 return ObjectSchema(RLMObjectBaseObjectSchema(self)!)
125 /// Indicates if the object can no longer be accessed because it is now invalid.
127 /// An object can no longer be accessed if the object has been deleted from the Realm that manages it, or if
128 /// `invalidate()` is called on that Realm.
129 public override final var isInvalidated: Bool { return super.isInvalidated }
131 /// A human-readable description of the object.
132 open override var description: String { return super.description }
135 WARNING: This is an internal helper method not intended for public use.
136 It is not considered part of the public API.
139 public override final class func objectUtilClass(_ isSwift: Bool) -> AnyClass {
140 return ObjectUtil.self
144 // MARK: Object Customization
147 Override this method to specify the name of a property to be used as the primary key.
149 Only properties of types `String` and `Int` can be designated as the primary key. Primary key properties enforce
150 uniqueness for each value whenever the property is set, which incurs minor overhead. Indexes are created
151 automatically for primary key properties.
153 - returns: The name of the property designated as the primary key, or `nil` if the model has no primary key.
155 @objc open class func primaryKey() -> String? { return nil }
158 Override this method to specify the names of properties to ignore. These properties will not be managed by
159 the Realm that manages the object.
161 - returns: An array of property names to ignore.
163 @objc open class func ignoredProperties() -> [String] { return [] }
166 Returns an array of property names for properties which should be indexed.
168 Only string, integer, boolean, `Date`, and `NSDate` properties are supported.
170 - returns: An array of property names.
172 @objc open class func indexedProperties() -> [String] { return [] }
174 // MARK: Key-Value Coding & Subscripting
176 /// Returns or sets the value of the property with the given name.
177 @objc open subscript(key: String) -> Any? {
180 return value(forKey: key)
182 return RLMDynamicGetByName(self, key, true)
186 setValue(value, forKey: key)
188 RLMDynamicValidatedSet(self, key, value)
193 // MARK: Notifications
196 Registers a block to be called each time the object changes.
198 The block will be asynchronously called after each write transaction which
199 deletes the object or modifies any of the managed properties of the object,
200 including self-assignments that set a property to its existing value.
202 For write transactions performed on different threads or in different
203 processes, the block will be called when the managing Realm is
204 (auto)refreshed to a version including the changes, while for local write
205 transactions it will be called at some point in the future after the write
206 transaction is committed.
208 Notifications are delivered via the standard run loop, and so can't be
209 delivered while the run loop is blocked by other activity. When
210 notifications can't be delivered instantly, multiple notifications may be
211 coalesced into a single notification.
213 Unlike with `List` and `Results`, there is no "initial" callback made after
214 you add a new notification block.
216 Only objects which are managed by a Realm can be observed in this way. You
217 must retain the returned token for as long as you want updates to be sent
218 to the block. To stop receiving updates, call `invalidate()` on the token.
220 It is safe to capture a strong reference to the observed object within the
221 callback block. There is no retain cycle due to that the callback is
222 retained by the returned token and not by the object itself.
224 - warning: This method cannot be called during a write transaction, or when
225 the containing Realm is read-only.
227 - parameter block: The block to call with information about changes to the object.
228 - returns: A token which must be held for as long as you want updates to be delivered.
230 public func observe(_ block: @escaping (ObjectChange) -> Void) -> NotificationToken {
231 return RLMObjectAddNotificationBlock(self, { names, oldValues, newValues, error in
232 if let error = error {
233 block(.error(error as NSError))
236 guard let names = names, let newValues = newValues else {
241 block(.change((0..<newValues.count).map { i in
242 PropertyChange(name: names[i], oldValue: oldValues?[i], newValue: newValues[i])
247 // MARK: Dynamic list
250 Returns a list of `DynamicObject`s for a given property name.
252 - warning: This method is useful only in specialized circumstances, for example, when building
253 components that integrate with Realm. If you are simply building an app on Realm, it is
254 recommended to use instance variables or cast the values returned from key-value coding.
256 - parameter propertyName: The name of the property.
258 - returns: A list of `DynamicObject`s.
262 public func dynamicList(_ propertyName: String) -> List<DynamicObject> {
263 return noWarnUnsafeBitCast(RLMDynamicGetByName(self, propertyName, true) as! RLMListBase,
264 to: List<DynamicObject>.self)
269 Returns whether two Realm objects are the same.
271 Objects are considered the same if and only if they are both managed by the same
272 Realm and point to the same underlying object in the database.
274 - note: Equality comparison is implemented by `isEqual(_:)`. If the object type
275 is defined with a primary key, `isEqual(_:)` behaves identically to this
276 method. If the object type is not defined with a primary key,
277 `isEqual(_:)` uses the `NSObject` behavior of comparing object identity.
278 This method can be used to compare two objects for database equality
279 whether or not their object type defines a primary key.
281 - parameter object: The object to compare the receiver to.
283 public func isSameObject(as object: Object?) -> Bool {
284 return RLMObjectBaseAreEqual(self, object)
287 // MARK: Private functions
289 // FIXME: None of these functions should be exposed in the public interface.
292 WARNING: This is an internal initializer not intended for public use.
295 public override required init(realm: RLMRealm, schema: RLMObjectSchema) {
296 super.init(realm: realm, schema: schema)
300 WARNING: This is an internal initializer not intended for public use.
303 public override required init(value: Any, schema: RLMSchema) {
304 super.init(value: value, schema: schema)
309 Information about a specific property which changed in an `Object` change notification.
311 public struct PropertyChange {
313 The name of the property which changed.
315 public let name: String
318 Value of the property before the change occurred. This is not supplied if
319 the change happened on the same thread as the notification and for `List`
322 For object properties this will give the object which was previously
323 linked to, but that object will have its new values and not the values it
324 had before the changes. This means that `previousValue` may be a deleted
325 object, and you will need to check `isInvalidated` before accessing any
328 public let oldValue: Any?
331 The value of the property after the change occurred. This is not supplied
332 for `List` properties and will always be nil.
334 public let newValue: Any?
338 Information about the changes made to an object which is passed to `Object`'s
341 public enum ObjectChange {
343 If an error occurs, notification blocks are called one time with a `.error`
344 result and an `NSError` containing details about the error. Currently the
345 only errors which can occur are when opening the Realm on a background
346 worker thread to calculate the change set. The callback will never be
347 called again after `.error` is delivered.
349 case error(_: NSError)
351 One or more of the properties of the object have been changed.
353 case change(_: [PropertyChange])
354 /// The object has been deleted from the Realm.
358 /// Object interface which allows untyped getters and setters for Objects.
360 public final class DynamicObject: Object {
361 public override subscript(key: String) -> Any? {
363 let value = RLMDynamicGetByName(self, key, false)
364 if let array = value as? RLMArray<AnyObject> {
365 return List<DynamicObject>(rlmArray: array)
370 RLMDynamicValidatedSet(self, key, value)
375 public override func value(forUndefinedKey key: String) -> Any? {
380 public override func setValue(_ value: Any?, forUndefinedKey key: String) {
385 public override class func shouldIncludeInDefaultSchema() -> Bool {
391 /// Internal class. Do not use directly.
392 @objc(RealmSwiftObjectUtil)
393 public class ObjectUtil: NSObject {
394 @objc private class func swiftVersion() -> NSString {
395 return swiftLanguageVersion as NSString
398 @objc private class func ignoredPropertiesForClass(_ type: AnyClass) -> NSArray? {
399 if let type = type as? Object.Type {
400 return type.ignoredProperties() as NSArray?
405 @objc private class func indexedPropertiesForClass(_ type: AnyClass) -> NSArray? {
406 if let type = type as? Object.Type {
407 return type.indexedProperties() as NSArray?
412 @objc private class func linkingObjectsPropertiesForClass(_ type: AnyClass) -> NSDictionary? {
413 // Not used for Swift. getLinkingObjectsProperties(_:) is used instead.
417 // If the property is a storage property for a lazy Swift property, return
418 // the base property name (e.g. `foo.storage` becomes `foo`). Otherwise, nil.
419 private static func baseName(forLazySwiftProperty name: String) -> String? {
420 // A Swift lazy var shows up as two separate children on the reflection tree:
421 // one named 'x', and another that is optional and is named 'x.storage'. Note
422 // that '.' is illegal in either a Swift or Objective-C property name.
423 if let storageRange = name.range(of: ".storage", options: [.anchored, .backwards]) {
425 return String(name[..<storageRange.lowerBound])
427 return name.substring(to: storageRange.lowerBound)
433 // Reflect an object, returning only children representing managed Realm properties.
434 private static func getNonIgnoredMirrorChildren(for object: Any) -> [Mirror.Child] {
435 let ignoredPropNames: Set<String>
436 if let realmObject = object as? Object {
437 ignoredPropNames = Set(type(of: realmObject).ignoredProperties())
439 ignoredPropNames = Set()
441 // No HKT in Swift, unfortunately
442 return Mirror(reflecting: object).children.filter { (prop: Mirror.Child) -> Bool in
443 guard let label = prop.label else {
446 if ignoredPropNames.contains(label) {
450 if let lazyBaseName = baseName(forLazySwiftProperty: label) {
451 if ignoredPropNames.contains(lazyBaseName) {
452 // Ignored lazy property.
455 // Managed lazy property; not currently supported.
456 // FIXME: revisit this once Swift gets property behaviors/property macros.
457 throwRealmException("Lazy managed property '\(lazyBaseName)' is not allowed on a Realm Swift object"
458 + " class. Either add the property to the ignored properties list or make it non-lazy.")
464 // Build optional property metadata for a given property.
465 // swiftlint:disable:next cyclomatic_complexity
466 private static func getOptionalPropertyMetadata(for child: Mirror.Child, at index: Int) -> RLMSwiftPropertyMetadata? {
467 guard let name = child.label else {
470 let mirror = Mirror(reflecting: child.value)
471 let type = mirror.subjectType
472 let code: PropertyType
473 if type is Optional<String>.Type || type is Optional<NSString>.Type {
475 } else if type is Optional<Date>.Type {
477 } else if type is Optional<Data>.Type {
479 } else if type is Optional<Object>.Type {
481 } else if type is RealmOptional<Int>.Type ||
482 type is RealmOptional<Int8>.Type ||
483 type is RealmOptional<Int16>.Type ||
484 type is RealmOptional<Int32>.Type ||
485 type is RealmOptional<Int64>.Type {
487 } else if type is RealmOptional<Float>.Type {
489 } else if type is RealmOptional<Double>.Type {
491 } else if type is RealmOptional<Bool>.Type {
493 } else if child.value is RLMOptionalBase {
494 throwRealmException("'\(type)' is not a valid RealmOptional type.")
495 code = .int // ignored
496 } else if mirror.displayStyle == .optional || type is ExpressibleByNilLiteral.Type {
497 return RLMSwiftPropertyMetadata(forNilLiteralOptionalProperty: name)
501 return RLMSwiftPropertyMetadata(forOptionalProperty: name, type: code)
504 @objc private class func getSwiftProperties(_ object: Any) -> [RLMSwiftPropertyMetadata] {
505 return getNonIgnoredMirrorChildren(for: object).enumerated().flatMap { idx, prop in
506 if let value = prop.value as? LinkingObjectsBase {
507 return RLMSwiftPropertyMetadata(forLinkingObjectsProperty: prop.label!,
508 className: value.objectClassName,
509 linkedPropertyName: value.propertyName)
510 } else if prop.value is RLMListBase {
511 return RLMSwiftPropertyMetadata(forListProperty: prop.label!)
512 } else if let optional = getOptionalPropertyMetadata(for: prop, at: idx) {
515 return RLMSwiftPropertyMetadata(forOtherProperty: prop.label!)
520 @objc private class func requiredPropertiesForClass(_: Any) -> [String] {
525 // MARK: AssistedObjectiveCBridgeable
527 // FIXME: Remove when `as! Self` can be written
528 private func forceCastToInferred<T, V>(_ x: T) -> V {
532 extension Object: AssistedObjectiveCBridgeable {
533 static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Self {
534 return forceCastToInferred(objectiveCValue)
537 var bridged: (objectiveCValue: Any, metadata: Any?) {
538 return (objectiveCValue: unsafeCastToRLMObject(), metadata: nil)
542 // MARK: - Migration assistance
546 @available(*, unavailable, renamed: "observe()")
547 public func addNotificationBlock(_ block: @escaping (ObjectChange) -> Void) -> NotificationToken {
554 @available(*, unavailable, renamed: "isSameObject(as:)") public func isEqual(to object: Any?) -> Bool {