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 ////////////////////////////////////////////////////////////////////////////
25 Types of properties which can be used with the minimum and maximum value APIs.
27 - see: `min(ofProperty:)`, `max(ofProperty:)`
29 public protocol MinMaxType {}
30 extension NSNumber: MinMaxType {}
31 extension Double: MinMaxType {}
32 extension Float: MinMaxType {}
33 extension Int: MinMaxType {}
34 extension Int8: MinMaxType {}
35 extension Int16: MinMaxType {}
36 extension Int32: MinMaxType {}
37 extension Int64: MinMaxType {}
38 extension Date: MinMaxType {}
39 extension NSDate: MinMaxType {}
44 Types of properties which can be used with the sum and average value APIs.
46 - see: `sum(ofProperty:)`, `average(ofProperty:)`
48 public protocol AddableType {
52 extension NSNumber: AddableType {}
53 extension Double: AddableType {}
54 extension Float: AddableType {}
55 extension Int: AddableType {}
56 extension Int8: AddableType {}
57 extension Int16: AddableType {}
58 extension Int32: AddableType {}
59 extension Int64: AddableType {}
62 `Results` is an auto-updating container type in Realm returned from object queries.
64 `Results` can be queried with the same predicates as `List<Element>`, and you can
65 chain queries to further filter query results.
67 `Results` always reflect the current state of the Realm on the current thread, including during write transactions on
68 the current thread. The one exception to this is when using `for...in` enumeration, which will always enumerate over
69 the objects which matched the query when the enumeration is begun, even if some of them are deleted or modified to be
70 excluded by the filter during the enumeration.
72 `Results` are lazily evaluated the first time they are accessed; they only run queries when the result of the query is
73 requested. This means that chaining several temporary `Results` to sort and filter your data does not perform any
74 unnecessary work processing the intermediate state.
76 Once the results have been evaluated or a notification block has been added, the results are eagerly kept up-to-date,
77 with the work done to keep them up-to-date done on a background thread whenever possible.
79 Results instances cannot be directly instantiated.
81 public final class Results<Element: RealmCollectionValue>: NSObject, NSFastEnumeration {
83 internal let rlmResults: RLMResults<AnyObject>
85 /// A human-readable description of the objects represented by the results.
86 public override var description: String {
87 return RLMDescriptionWithMaxDepth("Results", rlmResults, RLMDescriptionMaxDepth)
90 // MARK: Fast Enumeration
93 public func countByEnumerating(with state: UnsafeMutablePointer<NSFastEnumerationState>,
94 objects buffer: AutoreleasingUnsafeMutablePointer<AnyObject?>,
95 count len: Int) -> Int {
96 return Int(rlmResults.countByEnumerating(with: state, objects: buffer, count: UInt(len)))
99 /// The type of the objects described by the results.
100 public typealias ElementType = Element
104 /// The Realm which manages this results. Note that this property will never return `nil`.
105 public var realm: Realm? { return Realm(rlmResults.realm) }
108 Indicates if the results are no longer valid.
110 The results becomes invalid if `invalidate()` is called on the containing `realm`. An invalidated results can be
111 accessed, but will always be empty.
113 public var isInvalidated: Bool { return rlmResults.isInvalidated }
115 /// The number of objects in the results.
116 public var count: Int { return Int(rlmResults.count) }
118 // MARK: Initializers
120 internal init(_ rlmResults: RLMResults<AnyObject>) {
121 self.rlmResults = rlmResults
124 // MARK: Index Retrieval
127 Returns the index of the given object in the results, or `nil` if the object is not present.
129 public func index(of object: Element) -> Int? {
130 return notFoundToNil(index: rlmResults.index(of: object as AnyObject))
134 Returns the index of the first object matching the predicate, or `nil` if no objects match.
136 - parameter predicate: The predicate with which to filter the objects.
138 public func index(matching predicate: NSPredicate) -> Int? {
139 return notFoundToNil(index: rlmResults.indexOfObject(with: predicate))
143 Returns the index of the first object matching the predicate, or `nil` if no objects match.
145 - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
147 public func index(matching predicateFormat: String, _ args: Any...) -> Int? {
148 return notFoundToNil(index: rlmResults.indexOfObject(with: NSPredicate(format: predicateFormat,
149 argumentArray: unwrapOptionals(in: args))))
152 // MARK: Object Retrieval
155 Returns the object at the given `index`.
157 - parameter index: The index.
159 public subscript(position: Int) -> Element {
160 throwForNegativeIndex(position)
161 return dynamicBridgeCast(fromObjectiveC: rlmResults.object(at: UInt(position)))
164 /// Returns the first object in the results, or `nil` if the results are empty.
165 public var first: Element? { return rlmResults.firstObject().map(dynamicBridgeCast) }
167 /// Returns the last object in the results, or `nil` if the results are empty.
168 public var last: Element? { return rlmResults.lastObject().map(dynamicBridgeCast) }
173 Returns an `Array` containing the results of invoking `valueForKey(_:)` with `key` on each of the results.
175 - parameter key: The name of the property whose values are desired.
177 public override func value(forKey key: String) -> Any? {
178 return value(forKeyPath: key)
182 Returns an `Array` containing the results of invoking `valueForKeyPath(_:)` with `keyPath` on each of the results.
184 - parameter keyPath: The key path to the property whose values are desired.
186 public override func value(forKeyPath keyPath: String) -> Any? {
187 return rlmResults.value(forKeyPath: keyPath)
191 Invokes `setValue(_:forKey:)` on each of the objects represented by the results using the specified `value` and
194 - warning: This method may only be called during a write transaction.
196 - parameter value: The object value.
197 - parameter key: The name of the property whose value should be set on each object.
199 public override func setValue(_ value: Any?, forKey key: String) {
200 return rlmResults.setValue(value, forKeyPath: key)
206 Returns a `Results` containing all objects matching the given predicate in the collection.
208 - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
210 public func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element> {
211 return Results<Element>(rlmResults.objects(with: NSPredicate(format: predicateFormat,
212 argumentArray: unwrapOptionals(in: args))))
216 Returns a `Results` containing all objects matching the given predicate in the collection.
218 - parameter predicate: The predicate with which to filter the objects.
220 public func filter(_ predicate: NSPredicate) -> Results<Element> {
221 return Results<Element>(rlmResults.objects(with: predicate))
227 Returns a `Results` containing the objects represented by the results, but sorted.
229 Objects are sorted based on the values of the given key path. For example, to sort a collection of `Student`s from
230 youngest to oldest based on their `age` property, you might call
231 `students.sorted(byKeyPath: "age", ascending: true)`.
233 - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
234 floating point, integer, and string types.
236 - parameter keyPath: The key path to sort by.
237 - parameter ascending: The direction to sort in.
239 public func sorted(byKeyPath keyPath: String, ascending: Bool = true) -> Results<Element> {
240 return sorted(by: [SortDescriptor(keyPath: keyPath, ascending: ascending)])
244 Returns a `Results` containing the objects represented by the results, but sorted.
246 - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
247 floating point, integer, and string types.
249 - see: `sorted(byKeyPath:ascending:)`
251 - parameter sortDescriptors: A sequence of `SortDescriptor`s to sort by.
253 public func sorted<S: Sequence>(by sortDescriptors: S) -> Results<Element>
254 where S.Iterator.Element == SortDescriptor {
255 return Results<Element>(rlmResults.sortedResults(using: sortDescriptors.map { $0.rlmSortDescriptorValue }))
259 Returns a `Results` containing distinct objects based on the specified key paths
261 - parameter keyPaths: The key paths used produce distinct results
263 public func distinct<S: Sequence>(by keyPaths: S) -> Results<Element>
264 where S.Iterator.Element == String {
265 return Results<Element>(rlmResults.distinctResults(usingKeyPaths: Array(keyPaths)))
268 // MARK: Aggregate Operations
271 Returns the minimum (lowest) value of the given property among all the results, or `nil` if the results are empty.
273 - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
275 - parameter property: The name of a property whose minimum value is desired.
277 public func min<T: MinMaxType>(ofProperty property: String) -> T? {
278 return rlmResults.min(ofProperty: property).map(dynamicBridgeCast)
282 Returns the maximum (highest) value of the given property among all the results, or `nil` if the results are empty.
284 - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
286 - parameter property: The name of a property whose minimum value is desired.
288 public func max<T: MinMaxType>(ofProperty property: String) -> T? {
289 return rlmResults.max(ofProperty: property).map(dynamicBridgeCast)
293 Returns the sum of the values of a given property over all the results.
295 - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
297 - parameter property: The name of a property whose values should be summed.
299 public func sum<T: AddableType>(ofProperty property: String) -> T {
300 return dynamicBridgeCast(fromObjectiveC: rlmResults.sum(ofProperty: property))
304 Returns the average value of a given property over all the results, or `nil` if the results are empty.
306 - warning: Only the name of a property whose type conforms to the `AddableType` protocol can be specified.
308 - parameter property: The name of a property whose average value should be calculated.
310 public func average<T: AddableType>(ofProperty property: String) -> T? {
311 return rlmResults.average(ofProperty: property).map(dynamicBridgeCast)
314 // MARK: Notifications
317 Registers a block to be called each time the collection changes.
319 The block will be asynchronously called with the initial results, and then called again after each write
320 transaction which changes either any of the objects in the collection, or which objects are in the collection.
322 The `change` parameter that is passed to the block reports, in the form of indices within the collection, which of
323 the objects were added, removed, or modified during each write transaction. See the `RealmCollectionChange`
324 documentation for more information on the change information supplied and an example of how to use it to update a
327 At the time when the block is called, the collection will be fully evaluated and up-to-date, and as long as you do
328 not perform a write transaction on the same thread or explicitly call `realm.refresh()`, accessing it will never
329 perform blocking work.
331 Notifications are delivered via the standard run loop, and so can't be delivered while the run loop is blocked by
332 other activity. When notifications can't be delivered instantly, multiple notifications may be coalesced into a
333 single notification. This can include the notification with the initial collection.
335 For example, the following code performs a write transaction immediately after adding the notification block, so
336 there is no opportunity for the initial notification to be delivered first. As a result, the initial notification
337 will reflect the state of the Realm after the write transaction.
340 let results = realm.objects(Dog.self)
341 print("dogs.count: \(dogs?.count)") // => 0
342 let token = dogs.observe { changes in
344 case .initial(let dogs):
345 // Will print "dogs.count: 1"
346 print("dogs.count: \(dogs.count)")
349 // Will not be hit in this example
358 person.dogs.append(dog)
360 // end of run loop execution context
363 You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
364 updates, call `invalidate()` on the token.
366 - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only.
368 - parameter block: The block to be called whenever a change occurs.
369 - returns: A token which must be held for as long as you want updates to be delivered.
371 public func observe(_ block: @escaping (RealmCollectionChange<Results>) -> Void) -> NotificationToken {
372 return rlmResults.addNotificationBlock { _, change, error in
373 block(RealmCollectionChange.fromObjc(value: self, change: change, error: error))
378 extension Results: RealmCollection {
379 // MARK: Sequence Support
381 /// Returns a `RLMIterator` that yields successive elements in the results.
382 public func makeIterator() -> RLMIterator<Element> {
383 return RLMIterator(collection: rlmResults)
386 // MARK: Collection Support
388 /// The position of the first element in a non-empty collection.
389 /// Identical to endIndex in an empty collection.
390 public var startIndex: Int { return 0 }
392 /// The collection's "past the end" position.
393 /// endIndex is not a valid argument to subscript, and is always reachable from startIndex by
394 /// zero or more applications of successor().
395 public var endIndex: Int { return count }
397 public func index(after i: Int) -> Int { return i + 1 }
398 public func index(before i: Int) -> Int { return i - 1 }
401 public func _observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection<Element>>) -> Void) ->
403 let anyCollection = AnyRealmCollection(self)
404 return rlmResults.addNotificationBlock { _, change, error in
405 block(RealmCollectionChange.fromObjc(value: anyCollection, change: change, error: error))
410 // MARK: AssistedObjectiveCBridgeable
412 extension Results: AssistedObjectiveCBridgeable {
413 static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Results {
414 return Results(objectiveCValue as! RLMResults)
417 var bridged: (objectiveCValue: Any, metadata: Any?) {
418 return (objectiveCValue: rlmResults, metadata: nil)