added iOS source code
[wl-app.git] / iOS / Pods / RealmSwift / RealmSwift / LinkingObjects.swift
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2016 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
19 import Foundation
20 import Realm
21
22 /// :nodoc:
23 /// Internal class. Do not use directly. Used for reflection and initialization
24 public class LinkingObjectsBase: NSObject, NSFastEnumeration {
25     internal let objectClassName: String
26     internal let propertyName: String
27
28     fileprivate var cachedRLMResults: RLMResults<AnyObject>?
29     @objc fileprivate var object: RLMWeakObjectHandle?
30     @objc fileprivate var property: RLMProperty?
31
32     internal var rlmResults: RLMResults<AnyObject> {
33         if cachedRLMResults == nil {
34             if let object = self.object, let property = self.property {
35                 cachedRLMResults = RLMDynamicGet(object.object, property)! as? RLMResults
36                 self.object = nil
37                 self.property = nil
38             } else {
39                 cachedRLMResults = RLMResults.emptyDetached()
40             }
41         }
42         return cachedRLMResults!
43     }
44
45     init(fromClassName objectClassName: String, property propertyName: String) {
46         self.objectClassName = objectClassName
47         self.propertyName = propertyName
48     }
49
50     // MARK: Fast Enumeration
51     public func countByEnumerating(with state: UnsafeMutablePointer<NSFastEnumerationState>,
52                                    objects buffer: AutoreleasingUnsafeMutablePointer<AnyObject?>,
53                                    count len: Int) -> Int {
54         return Int(rlmResults.countByEnumerating(with: state,
55                                                  objects: buffer,
56                                                  count: UInt(len)))
57     }
58 }
59
60 /**
61  `LinkingObjects` is an auto-updating container type. It represents zero or more objects that are linked to its owning
62  model object through a property relationship.
63
64  `LinkingObjects` can be queried with the same predicates as `List<Element>` and `Results<Element>`.
65
66  `LinkingObjects` always reflects the current state of the Realm on the current thread, including during write
67  transactions on the current thread. The one exception to this is when using `for...in` enumeration, which will always
68  enumerate over the linking objects that were present when the enumeration is begun, even if some of them are deleted or
69  modified to no longer link to the target object during the enumeration.
70
71  `LinkingObjects` can only be used as a property on `Object` models. Properties of this type must be declared as `let`
72  and cannot be `dynamic`.
73  */
74 public final class LinkingObjects<Element: Object>: LinkingObjectsBase {
75     /// The type of the objects represented by the linking objects.
76     public typealias ElementType = Element
77
78     // MARK: Properties
79
80     /// The Realm which manages the linking objects, or `nil` if the linking objects are unmanaged.
81     public var realm: Realm? { return rlmResults.isAttached ? Realm(rlmResults.realm) : nil }
82
83     /// Indicates if the linking objects are no longer valid.
84     ///
85     /// The linking objects become invalid if `invalidate()` is called on the containing `realm` instance.
86     ///
87     /// An invalidated linking objects can be accessed, but will always be empty.
88     public var isInvalidated: Bool { return rlmResults.isInvalidated }
89
90     /// The number of linking objects.
91     public var count: Int { return Int(rlmResults.count) }
92
93     // MARK: Initializers
94
95     /**
96      Creates an instance of a `LinkingObjects`. This initializer should only be called when declaring a property on a
97      Realm model.
98
99      - parameter type:         The type of the object owning the property the linking objects should refer to.
100      - parameter propertyName: The property name of the property the linking objects should refer to.
101      */
102     public init(fromType type: Element.Type, property propertyName: String) {
103         let className = (Element.self as Object.Type).className()
104         super.init(fromClassName: className, property: propertyName)
105     }
106
107     /// A human-readable description of the objects represented by the linking objects.
108     public override var description: String {
109         return RLMDescriptionWithMaxDepth("LinkingObjects", rlmResults, RLMDescriptionMaxDepth)
110     }
111
112     // MARK: Index Retrieval
113
114     /**
115      Returns the index of an object in the linking objects, or `nil` if the object is not present.
116
117      - parameter object: The object whose index is being queried.
118      */
119     public func index(of object: Element) -> Int? {
120         return notFoundToNil(index: rlmResults.index(of: object.unsafeCastToRLMObject()))
121     }
122
123     /**
124      Returns the index of the first object matching the given predicate, or `nil` if no objects match.
125
126      - parameter predicate: The predicate with which to filter the objects.
127      */
128     public func index(matching predicate: NSPredicate) -> Int? {
129         return notFoundToNil(index: rlmResults.indexOfObject(with: predicate))
130     }
131
132     /**
133      Returns the index of the first object matching the given predicate, or `nil` if no objects match.
134
135      - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
136      */
137     public func index(matching predicateFormat: String, _ args: Any...) -> Int? {
138         return notFoundToNil(index: rlmResults.indexOfObject(with: NSPredicate(format: predicateFormat,
139                                                                                argumentArray: unwrapOptionals(in: args))))
140     }
141
142     // MARK: Object Retrieval
143
144     /**
145      Returns the object at the given `index`.
146
147      - parameter index: The index.
148      */
149     public subscript(index: Int) -> Element {
150         get {
151             throwForNegativeIndex(index)
152             return unsafeBitCast(rlmResults[UInt(index)], to: Element.self)
153         }
154     }
155
156     /// Returns the first object in the linking objects, or `nil` if the linking objects are empty.
157     public var first: Element? { return unsafeBitCast(rlmResults.firstObject(), to: Optional<Element>.self) }
158
159     /// Returns the last object in the linking objects, or `nil` if the linking objects are empty.
160     public var last: Element? { return unsafeBitCast(rlmResults.lastObject(), to: Optional<Element>.self) }
161
162     // MARK: KVC
163
164     /**
165      Returns an `Array` containing the results of invoking `valueForKey(_:)` with `key` on each of the linking objects.
166
167      - parameter key: The name of the property whose values are desired.
168      */
169     public override func value(forKey key: String) -> Any? {
170         return value(forKeyPath: key)
171     }
172
173     /**
174      Returns an `Array` containing the results of invoking `valueForKeyPath(_:)` with `keyPath` on each of the linking
175      objects.
176
177      - parameter keyPath: The key path to the property whose values are desired.
178      */
179     public override func value(forKeyPath keyPath: String) -> Any? {
180         return rlmResults.value(forKeyPath: keyPath)
181     }
182
183     /**
184      Invokes `setValue(_:forKey:)` on each of the linking objects using the specified `value` and `key`.
185
186      - warning: This method may only be called during a write transaction.
187
188      - parameter value: The value to set the property to.
189      - parameter key:   The name of the property whose value should be set on each object.
190      */
191     public override func setValue(_ value: Any?, forKey key: String) {
192         return rlmResults.setValue(value, forKeyPath: key)
193     }
194
195     // MARK: Filtering
196
197     /**
198      Returns a `Results` containing all objects matching the given predicate in the linking objects.
199
200      - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
201      */
202     public func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element> {
203         return Results(rlmResults.objects(with: NSPredicate(format: predicateFormat,
204                                                             argumentArray: unwrapOptionals(in: args))))
205     }
206
207     /**
208      Returns a `Results` containing all objects matching the given predicate in the linking objects.
209
210      - parameter predicate: The predicate with which to filter the objects.
211      */
212     public func filter(_ predicate: NSPredicate) -> Results<Element> {
213         return Results(rlmResults.objects(with: predicate))
214     }
215
216     // MARK: Sorting
217
218     /**
219      Returns a `Results` containing all the linking objects, but sorted.
220
221      Objects are sorted based on the values of the given key path. For example, to sort a collection of `Student`s from
222      youngest to oldest based on their `age` property, you might call
223      `students.sorted(byKeyPath: "age", ascending: true)`.
224
225      - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
226                 floating point, integer, and string types.
227
228      - parameter keyPath:  The key path to sort by.
229      - parameter ascending: The direction to sort in.
230      */
231     public func sorted(byKeyPath keyPath: String, ascending: Bool = true) -> Results<Element> {
232         return sorted(by: [SortDescriptor(keyPath: keyPath, ascending: ascending)])
233     }
234
235     /**
236      Returns a `Results` containing all the linking objects, but sorted.
237
238      - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
239                 floating point, integer, and string types.
240
241      - see: `sorted(byKeyPath:ascending:)`
242
243      - parameter sortDescriptors: A sequence of `SortDescriptor`s to sort by.
244      */
245     public func sorted<S: Sequence>(by sortDescriptors: S) -> Results<Element>
246         where S.Iterator.Element == SortDescriptor {
247             return Results(rlmResults.sortedResults(using: sortDescriptors.map { $0.rlmSortDescriptorValue }))
248     }
249
250     // MARK: Aggregate Operations
251
252     /**
253      Returns the minimum (lowest) value of the given property among all the linking objects, or `nil` if the linking
254      objects are empty.
255
256      - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
257
258      - parameter property: The name of a property whose minimum value is desired.
259      */
260     public func min<T: MinMaxType>(ofProperty property: String) -> T? {
261         return rlmResults.min(ofProperty: property).map(dynamicBridgeCast)
262     }
263
264     /**
265      Returns the maximum (highest) value of the given property among all the linking objects, or `nil` if the linking
266      objects are empty.
267
268      - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
269
270      - parameter property: The name of a property whose minimum value is desired.
271      */
272     public func max<T: MinMaxType>(ofProperty property: String) -> T? {
273         return rlmResults.max(ofProperty: property).map(dynamicBridgeCast)
274     }
275
276     /**
277      Returns the sum of the values of a given property over all the linking objects.
278
279      - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
280
281      - parameter property: The name of a property whose values should be summed.
282      */
283     public func sum<T: AddableType>(ofProperty property: String) -> T {
284         return dynamicBridgeCast(fromObjectiveC: rlmResults.sum(ofProperty: property))
285     }
286
287     /**
288      Returns the average value of a given property over all the linking objects, or `nil` if the linking objects are
289      empty.
290
291      - warning: Only the name of a property whose type conforms to the `AddableType` protocol can be specified.
292
293      - parameter property: The name of a property whose average value should be calculated.
294      */
295     public func average<T: AddableType>(ofProperty property: String) -> T? {
296         return rlmResults.average(ofProperty: property).map(dynamicBridgeCast)
297     }
298
299     // MARK: Notifications
300
301     /**
302      Registers a block to be called each time the collection changes.
303
304      The block will be asynchronously called with the initial results, and then called again after each write
305      transaction which changes either any of the objects in the collection, or which objects are in the collection.
306
307      The `change` parameter that is passed to the block reports, in the form of indices within the collection, which of
308      the objects were added, removed, or modified during each write transaction. See the `RealmCollectionChange`
309      documentation for more information on the change information supplied and an example of how to use it to update a
310      `UITableView`.
311
312      At the time when the block is called, the collection will be fully evaluated and up-to-date, and as long as you do
313      not perform a write transaction on the same thread or explicitly call `realm.refresh()`, accessing it will never
314      perform blocking work.
315
316      Notifications are delivered via the standard run loop, and so can't be delivered while the run loop is blocked by
317      other activity. When notifications can't be delivered instantly, multiple notifications may be coalesced into a
318      single notification. This can include the notification with the initial collection.
319
320      For example, the following code performs a write transaction immediately after adding the notification block, so
321      there is no opportunity for the initial notification to be delivered first. As a result, the initial notification
322      will reflect the state of the Realm after the write transaction.
323
324      ```swift
325      let results = realm.objects(Dog.self)
326      print("dogs.count: \(dogs?.count)") // => 0
327      let token = dogs.observe { changes in
328          switch changes {
329          case .initial(let dogs):
330              // Will print "dogs.count: 1"
331              print("dogs.count: \(dogs.count)")
332              break
333          case .update:
334              // Will not be hit in this example
335              break
336          case .error:
337              break
338          }
339      }
340      try! realm.write {
341          let dog = Dog()
342          dog.name = "Rex"
343          person.dogs.append(dog)
344      }
345      // end of run loop execution context
346      ```
347
348      You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
349      updates, call `invalidate()` on the token.
350
351      - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only.
352
353      - parameter block: The block to be called whenever a change occurs.
354      - returns: A token which must be held for as long as you want updates to be delivered.
355      */
356     public func observe(_ block: @escaping (RealmCollectionChange<LinkingObjects>) -> Void) -> NotificationToken {
357         return rlmResults.addNotificationBlock { _, change, error in
358             block(RealmCollectionChange.fromObjc(value: self, change: change, error: error))
359         }
360     }
361 }
362
363 extension LinkingObjects: RealmCollection {
364     // MARK: Sequence Support
365
366     /// Returns an iterator that yields successive elements in the linking objects.
367     public func makeIterator() -> RLMIterator<Element> {
368         return RLMIterator(collection: rlmResults)
369     }
370
371     // MARK: Collection Support
372
373     /// The position of the first element in a non-empty collection.
374     /// Identical to endIndex in an empty collection.
375     public var startIndex: Int { return 0 }
376
377     /// The collection's "past the end" position.
378     /// endIndex is not a valid argument to subscript, and is always reachable from startIndex by
379     /// zero or more applications of successor().
380     public var endIndex: Int { return count }
381
382     public func index(after: Int) -> Int {
383       return after + 1
384     }
385
386     public func index(before: Int) -> Int {
387       return before - 1
388     }
389
390     /// :nodoc:
391     public func _observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection<Element>>) -> Void) ->
392         NotificationToken {
393             let anyCollection = AnyRealmCollection(self)
394             return rlmResults.addNotificationBlock { _, change, error in
395                 block(RealmCollectionChange.fromObjc(value: anyCollection, change: change, error: error))
396             }
397     }
398 }
399
400 // MARK: AssistedObjectiveCBridgeable
401
402 extension LinkingObjects: AssistedObjectiveCBridgeable {
403     internal static func bridging(from objectiveCValue: Any, with metadata: Any?) -> LinkingObjects {
404         guard let metadata = metadata as? LinkingObjectsBridgingMetadata else { preconditionFailure() }
405
406         let swiftValue = LinkingObjects(fromType: Element.self, property: metadata.propertyName)
407         switch (objectiveCValue, metadata) {
408         case (let object as RLMObjectBase, .uncached(let property)):
409             swiftValue.object = RLMWeakObjectHandle(object: object)
410             swiftValue.property = property
411         case (let results as RLMResults<AnyObject>, .cached):
412             swiftValue.cachedRLMResults = results
413         default:
414             preconditionFailure()
415         }
416         return swiftValue
417     }
418
419     internal var bridged: (objectiveCValue: Any, metadata: Any?) {
420         if let results = cachedRLMResults {
421             return (objectiveCValue: results,
422                     metadata: LinkingObjectsBridgingMetadata.cached(propertyName: propertyName))
423         } else {
424             return (objectiveCValue: (object!.copy() as! RLMWeakObjectHandle).object,
425                     metadata: LinkingObjectsBridgingMetadata.uncached(property: property!))
426         }
427     }
428 }
429
430 internal enum LinkingObjectsBridgingMetadata {
431     case uncached(property: RLMProperty)
432     case cached(propertyName: String)
433
434     fileprivate var propertyName: String {
435         switch self {
436         case .uncached(let property):   return property.name
437         case .cached(let propertyName): return propertyName
438         }
439     }
440 }