added iOS source code
[wl-app.git] / iOS / Pods / RealmSwift / RealmSwift / Sync.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 Realm
20 import Foundation
21
22 /**
23  An object representing a Realm Object Server user.
24
25  - see: `RLMSyncUser`
26  */
27 public typealias SyncUser = RLMSyncUser
28
29 /**
30  An immutable data object representing information retrieved from the Realm Object
31  Server about a particular user.
32
33  - see: `RLMSyncUserInfo`
34  */
35 public typealias SyncUserInfo = RLMSyncUserInfo
36
37 /**
38  An immutable data object representing an account belonging to a particular user.
39
40  - see: `SyncUserInfo`, `RLMSyncUserAccountInfo`
41  */
42 public typealias SyncUserAccountInfo = RLMSyncUserAccountInfo
43
44 /**
45  A singleton which configures and manages the Realm Object Server synchronization-related
46  functionality.
47
48  - see: `RLMSyncManager`
49  */
50 public typealias SyncManager = RLMSyncManager
51
52 extension SyncManager {
53     /// The sole instance of the singleton.
54     public static var shared: SyncManager {
55         return __shared()
56     }
57 }
58
59 /**
60  A session object which represents communication between the client and server for a specific
61  Realm.
62
63  - see: `RLMSyncSession`
64  */
65 public typealias SyncSession = RLMSyncSession
66
67 /**
68  A closure type for a closure which can be set on the `SyncManager` to allow errors to be reported
69  to the application.
70
71  - see: `RLMSyncErrorReportingBlock`
72  */
73 public typealias ErrorReportingBlock = RLMSyncErrorReportingBlock
74
75 /**
76  A closure type for a closure which is used by certain APIs to asynchronously return a `SyncUser`
77  object to the application.
78
79  - see: `RLMUserCompletionBlock`
80  */
81 public typealias UserCompletionBlock = RLMUserCompletionBlock
82
83 /**
84  An error associated with the SDK's synchronization functionality. All errors reported by
85  an error handler registered on the `SyncManager` are of this type.
86
87  - see: `RLMSyncError`
88  */
89 public typealias SyncError = RLMSyncError
90
91 extension SyncError {
92     /**
93      An opaque token allowing the user to take action after certain types of
94      errors have been reported.
95
96      - see: `RLMSyncErrorActionToken`
97      */
98     public typealias ActionToken = RLMSyncErrorActionToken
99
100     /**
101      Given a client reset error, extract and return the recovery file path
102      and the action token.
103
104      The action token can be passed into `SyncSession.immediatelyHandleError(_:)`
105      to immediately delete the local copy of the Realm which experienced the
106      client reset error. The local copy of the Realm must be deleted before
107      your application attempts to open the Realm again.
108
109      The recovery file path is the path to which the current copy of the Realm
110      on disk will be saved once the client reset occurs.
111
112      - warning: Do not call `SyncSession.immediatelyHandleError(_:)` until you are
113                 sure that all references to the Realm and managed objects belonging
114                 to the Realm have been nil'ed out, and that all autorelease pools
115                 containing these references have been drained.
116
117      - see: `SyncError.ActionToken`, `SyncSession.immediatelyHandleError(_:)`
118      */
119     public func clientResetInfo() -> (String, SyncError.ActionToken)? {
120         if code == SyncError.clientResetError,
121             let recoveryPath = userInfo[kRLMSyncPathOfRealmBackupCopyKey] as? String,
122             let token = _nsError.__rlmSync_errorActionToken() {
123             return (recoveryPath, token)
124         }
125         return nil
126     }
127
128     /**
129      Given a permission denied error, extract and return the action token.
130
131      This action token can be passed into `SyncSession.immediatelyHandleError(_:)`
132      to immediately delete the local copy of the Realm which experienced the
133      permission denied error. The local copy of the Realm must be deleted before
134      your application attempts to open the Realm again.
135
136      - warning: Do not call `SyncSession.immediatelyHandleError(_:)` until you are
137                 sure that all references to the Realm and managed objects belonging
138                 to the Realm have been nil'ed out, and that all autorelease pools
139                 containing these references have been drained.
140
141      - see: `SyncError.ActionToken`, `SyncSession.immediatelyHandleError(_:)`
142      */
143     public func deleteRealmUserInfo() -> SyncError.ActionToken? {
144         return _nsError.__rlmSync_errorActionToken()
145     }
146 }
147
148 /**
149  An error associated with network requests made to the authentication server. This type of error
150  may be returned in the callback block to `SyncUser.logIn()` upon certain types of failed login
151  attempts (for example, if the request is malformed or if the server is experiencing an issue).
152
153  - see: `RLMSyncAuthError`
154  */
155 public typealias SyncAuthError = RLMSyncAuthError
156
157 /**
158  An error associated with retrieving or modifying user permissions to access a synchronized Realm.
159
160  - see: `RLMSyncPermissionError`
161  */
162 public typealias SyncPermissionError = RLMSyncPermissionError
163
164 /**
165  An enum which can be used to specify the level of logging.
166
167  - see: `RLMSyncLogLevel`
168  */
169 public typealias SyncLogLevel = RLMSyncLogLevel
170
171 /**
172  A data type whose values represent different authentication providers that can be used with
173  the Realm Object Server.
174
175  - see: `RLMIdentityProvider`
176  */
177 public typealias Provider = RLMIdentityProvider
178
179 /**
180  A `SyncConfiguration` represents configuration parameters for Realms intended to sync with
181  a Realm Object Server.
182  */
183 public struct SyncConfiguration {
184     /// The `SyncUser` who owns the Realm that this configuration should open.
185     public let user: SyncUser
186
187     /**
188      The URL of the Realm on the Realm Object Server that this configuration should open.
189
190      - warning: The URL must be absolute (e.g. `realms://example.com/~/foo`), and cannot end with
191                 `.realm`, `.realm.lock` or `.realm.management`.
192      */
193     public let realmURL: URL
194
195     /**
196      A policy that determines what should happen when all references to Realms opened by this
197      configuration go out of scope.
198      */
199     internal let stopPolicy: RLMSyncStopPolicy
200
201     /**
202      Whether the SSL certificate of the Realm Object Server should be validated.
203      */
204     public let enableSSLValidation: Bool
205
206     /**
207      Whether this Realm should be opened in 'partial synchronization' mode.
208      Partial synchronization mode means that no objects are synchronized from the remote Realm
209      except those matching queries that the user explicitly specifies.
210
211      -warning: Partial synchronization is a tech preview. Its APIs are subject to change.
212      */
213     public let isPartial: Bool
214
215     internal init(config: RLMSyncConfiguration) {
216         self.user = config.user
217         self.realmURL = config.realmURL
218         self.stopPolicy = config.stopPolicy
219         self.enableSSLValidation = config.enableSSLValidation
220         self.isPartial = config.isPartial
221     }
222
223     func asConfig() -> RLMSyncConfiguration {
224         let config = RLMSyncConfiguration(user: user, realmURL: realmURL)
225         config.stopPolicy = stopPolicy
226         config.enableSSLValidation = enableSSLValidation
227         config.isPartial = isPartial
228         return config
229     }
230
231     /**
232      Initialize a sync configuration with a user and a Realm URL.
233
234      Additional settings can be optionally specified. Descriptions of these
235      settings follow.
236
237      `enableSSLValidation` is true by default. It can be disabled for debugging
238      purposes.
239
240      - warning: The URL must be absolute (e.g. `realms://example.com/~/foo`), and cannot end with
241                 `.realm`, `.realm.lock` or `.realm.management`.
242
243      - warning: NEVER disable SSL validation for a system running in production.
244      */
245     public init(user: SyncUser, realmURL: URL, enableSSLValidation: Bool = true, isPartial: Bool = false) {
246         self.user = user
247         self.realmURL = realmURL
248         self.stopPolicy = .afterChangesUploaded
249         self.enableSSLValidation = enableSSLValidation
250         self.isPartial = isPartial
251     }
252 }
253
254 /// A `SyncCredentials` represents data that uniquely identifies a Realm Object Server user.
255 public struct SyncCredentials {
256     public typealias Token = String
257
258     internal var token: Token
259     internal var provider: Provider
260     internal var userInfo: [String: Any]
261
262     /**
263      Initialize new credentials using a custom token, authentication provider, and user information
264      dictionary. In most cases, the convenience initializers should be used instead.
265      */
266     public init(customToken token: Token, provider: Provider, userInfo: [String: Any] = [:]) {
267         self.token = token
268         self.provider = provider
269         self.userInfo = userInfo
270     }
271
272     internal init(_ credentials: RLMSyncCredentials) {
273         self.token = credentials.token
274         self.provider = credentials.provider
275         self.userInfo = credentials.userInfo
276     }
277
278     /// Initialize new credentials using a Facebook account token.
279     public static func facebook(token: Token) -> SyncCredentials {
280         return SyncCredentials(RLMSyncCredentials(facebookToken: token))
281     }
282
283     /// Initialize new credentials using a Google account token.
284     public static func google(token: Token) -> SyncCredentials {
285         return SyncCredentials(RLMSyncCredentials(googleToken: token))
286     }
287
288     /// Initialize new credentials using a CloudKit account token.
289     public static func cloudKit(token: Token) -> SyncCredentials {
290         return SyncCredentials(RLMSyncCredentials(cloudKitToken: token))
291     }
292
293     /// Initialize new credentials using a Realm Object Server username and password.
294     public static func usernamePassword(username: String,
295                                         password: String,
296                                         register: Bool = false) -> SyncCredentials {
297         return SyncCredentials(RLMSyncCredentials(username: username, password: password, register: register))
298     }
299
300     /// Initialize new credentials using a Realm Object Server access token.
301     public static func accessToken(_ accessToken: String, identity: String) -> SyncCredentials {
302         return SyncCredentials(RLMSyncCredentials(accessToken: accessToken, identity: identity))
303     }
304
305     /// Initialize new credentials using a JSON Web Token.
306     public static func jwt(_ token: Token) -> SyncCredentials {
307         return SyncCredentials(RLMSyncCredentials(jwt: token))
308     }
309
310     /// Initialize new credentials using a nickname.
311     public static func nickname(_ nickname: String, isAdmin: Bool = false) -> SyncCredentials {
312         return SyncCredentials(RLMSyncCredentials(nickname: nickname, isAdmin: isAdmin))
313     }
314
315     /// Initialize new credentials anonymously
316     public static func anonymous() -> SyncCredentials {
317         return SyncCredentials(RLMSyncCredentials.anonymous())
318     }
319 }
320
321 extension RLMSyncCredentials {
322     internal convenience init(_ credentials: SyncCredentials) {
323         self.init(customToken: credentials.token, provider: credentials.provider, userInfo: credentials.userInfo)
324     }
325 }
326
327 extension SyncUser {
328     /**
329      Log in a user and asynchronously retrieve a user object.
330
331      If the log in completes successfully, the completion block will be called, and a
332      `SyncUser` representing the logged-in user will be passed to it. This user object
333      can be used to open `Realm`s and retrieve `SyncSession`s. Otherwise, the
334      completion block will be called with an error.
335
336      - parameter credentials: A `SyncCredentials` object representing the user to log in.
337      - parameter authServerURL: The URL of the authentication server (e.g. "http://realm.example.org:9080").
338      - parameter timeout: How long the network client should wait, in seconds, before timing out.
339      - parameter callbackQueue: The dispatch queue upon which the callback should run. Defaults to the main queue.
340      - parameter completion: A callback block to be invoked once the log in completes.
341      */
342     public static func logIn(with credentials: SyncCredentials,
343                              server authServerURL: URL,
344                              timeout: TimeInterval = 30,
345                              callbackQueue queue: DispatchQueue = DispatchQueue.main,
346                              onCompletion completion: @escaping UserCompletionBlock) {
347         return SyncUser.__logIn(with: RLMSyncCredentials(credentials),
348                                 authServerURL: authServerURL,
349                                 timeout: timeout,
350                                 callbackQueue: queue,
351                                 onCompletion: completion)
352     }
353
354     /// A dictionary of all valid, logged-in user identities corresponding to their `SyncUser` objects.
355     public static var all: [String: SyncUser] {
356         return __allUsers()
357     }
358
359     /**
360      The logged-in user. `nil` if none exists. Only use this property if your application expects
361      no more than one logged-in user at any given time.
362
363      - warning: Throws an Objective-C exception if more than one logged-in user exists.
364      */
365     public static var current: SyncUser? {
366         return __current()
367     }
368
369     /**
370      An optional error handler which can be set to notify the host application when
371      the user encounters an error.
372      
373      - note: Check for `.invalidAccessToken` to see if the user has been remotely logged
374              out because its refresh token expired, or because the third party authentication
375              service providing the user's identity has logged the user out.
376
377      - warning: Regardless of whether an error handler is defined, certain user errors
378                 will automatically cause the user to enter the logged out state.
379      */
380     @nonobjc public var errorHandler: ((SyncUser, SyncAuthError) -> Void)? {
381         get {
382             return __errorHandler
383         }
384         set {
385             if let newValue = newValue {
386                 __errorHandler = { (user, error) in
387                     newValue(user, error as! SyncAuthError)
388                 }
389             } else {
390                 __errorHandler = nil
391             }
392         }
393     }
394
395     /**
396      Retrieve permissions for this user. Permissions describe which synchronized
397      Realms this user has access to and what they are allowed to do with them.
398
399      Permissions are retrieved asynchronously and returned via the callback. The
400      callback is run on the same thread that the method is invoked upon.
401
402      - warning: This method must be invoked on a thread with an active run loop.
403
404      - warning: Do not pass the `Results` returned by the callback between threads.
405
406      - parameter callback: A callback providing either a `Results` containing the
407                            permissions, or an error describing what went wrong.
408      */
409     public func retrievePermissions(callback: @escaping (SyncPermissionResults?, SyncPermissionError?) -> Void) {
410         self.__retrievePermissions { (results, error) in
411             guard let results = results else {
412                 callback(nil, error as! SyncPermissionError?)
413                 return
414             }
415             let upcasted: RLMResults<SyncPermission> = results
416             callback(Results(upcasted as! RLMResults<AnyObject>), nil)
417         }
418     }
419
420     /**
421      Create a permission offer for a Realm.
422
423      A permission offer is used to grant access to a Realm this user manages to another
424      user. Creating a permission offer produces a string token which can be passed to the
425      recepient in any suitable way (for example, via e-mail).
426
427      The operation will take place asynchronously. The token can be accepted by the recepient
428      using the `SyncUser.acceptOffer(forToken:, callback:)` method.
429
430      - parameter url: The URL of the Realm for which the permission offer should pertain. This
431                       may be the URL of any Realm which this user is allowed to manage. If the URL
432                       has a `~` wildcard it will be replaced with this user's user identity.
433      - parameter accessLevel: What access level to grant to whoever accepts the token.
434      - parameter expiration: Optionally, a date which indicates when the offer expires. If the
435                              recepient attempts to accept the offer after the date it will be rejected.
436                              If nil, the offer will never expire.
437      - parameter callback: A callback indicating whether the operation succeeded or failed. If it
438                            succeeded the token will be passed in as a string.
439      */
440     public func createOfferForRealm(at url: URL,
441                                     accessLevel: SyncAccessLevel,
442                                     expiration: Date? = nil,
443                                     callback: @escaping (String?, SyncPermissionError?) -> Void) {
444         self.__createOfferForRealm(at: url, accessLevel: accessLevel, expiration: expiration) { (token, error) in
445             guard let token = token else {
446                 callback(nil, error as! SyncPermissionError?)
447                 return
448             }
449             callback(token, nil)
450         }
451     }
452 }
453
454 /**
455  A value which represents a permission granted to a user to interact
456  with a Realm. These values are passed into APIs on `SyncUser`, and
457  returned from `SyncPermissionResults`.
458
459  - see: `RLMSyncPermission`
460  */
461 public typealias SyncPermission = RLMSyncPermission
462
463 /**
464  An enumeration describing possible access levels.
465
466  - see: `RLMSyncAccessLevel`
467  */
468 public typealias SyncAccessLevel = RLMSyncAccessLevel
469
470 public extension SyncSession {
471     /**
472      The transfer direction (upload or download) tracked by a given progress notification block.
473
474      Progress notification blocks can be registered on sessions if your app wishes to be informed
475      how many bytes have been uploaded or downloaded, for example to show progress indicator UIs.
476      */
477     public enum ProgressDirection {
478         /// For monitoring upload progress.
479         case upload
480         /// For monitoring download progress.
481         case download
482     }
483
484     /**
485      The desired behavior of a progress notification block.
486
487      Progress notification blocks can be registered on sessions if your app wishes to be informed
488      how many bytes have been uploaded or downloaded, for example to show progress indicator UIs.
489      */
490     public enum ProgressMode {
491         /**
492          The block will be called forever, or until it is unregistered by calling
493          `ProgressNotificationToken.invalidate()`.
494
495          Notifications will always report the latest number of transferred bytes, and the
496          most up-to-date number of total transferrable bytes.
497          */
498         case reportIndefinitely
499         /**
500          The block will, upon registration, store the total number of bytes
501          to be transferred. When invoked, it will always report the most up-to-date number
502          of transferrable bytes out of that original number of transferrable bytes.
503
504          When the number of transferred bytes reaches or exceeds the
505          number of transferrable bytes, the block will be unregistered.
506          */
507         case forCurrentlyOutstandingWork
508     }
509
510     /**
511      A token corresponding to a progress notification block.
512
513      Call `invalidate()` on the token to stop notifications. If the notification block has already
514      been automatically stopped, calling `invalidate()` does nothing. `invalidate()` should be called
515      before the token is destroyed.
516      */
517     public typealias ProgressNotificationToken = RLMProgressNotificationToken
518
519     /**
520      A struct encapsulating progress information, as well as useful helper methods.
521      */
522     public struct Progress {
523         /// The number of bytes that have been transferred.
524         public let transferredBytes: Int
525
526         /**
527          The total number of transferrable bytes (bytes that have been transferred,
528          plus bytes pending transfer).
529
530          If the notification block is tracking downloads, this number represents the size of the
531          changesets generated by all other clients using the Realm.
532          If the notification block is tracking uploads, this number represents the size of the
533          changesets representing the local changes on this client.
534          */
535         public let transferrableBytes: Int
536
537         /// The fraction of bytes transferred out of all transferrable bytes. If this value is 1,
538         /// no bytes are waiting to be transferred (either all bytes have already been transferred,
539         /// or there are no bytes to be transferred in the first place).
540         public var fractionTransferred: Double {
541             if transferrableBytes == 0 {
542                 return 1
543             }
544             let percentage = Double(transferredBytes) / Double(transferrableBytes)
545             return percentage > 1 ? 1 : percentage
546         }
547
548         /// Whether all pending bytes have already been transferred.
549         public var isTransferComplete: Bool {
550             return transferredBytes >= transferrableBytes
551         }
552
553         fileprivate init(transferred: UInt, transferrable: UInt) {
554             transferredBytes = Int(transferred)
555             transferrableBytes = Int(transferrable)
556         }
557     }
558
559     /**
560      Register a progress notification block.
561
562      If the session has already received progress information from the
563      synchronization subsystem, the block will be called immediately. Otherwise, it
564      will be called as soon as progress information becomes available.
565
566      Multiple blocks can be registered with the same session at once. Each block
567      will be invoked on a side queue devoted to progress notifications.
568
569      The token returned by this method must be retained as long as progress
570      notifications are desired, and the `invalidate()` method should be called on it
571      when notifications are no longer needed and before the token is destroyed.
572
573      If no token is returned, the notification block will never be called again.
574      There are a number of reasons this might be true. If the session has previously
575      experienced a fatal error it will not accept progress notification blocks. If
576      the block was configured in the `forCurrentlyOutstandingWork` mode but there
577      is no additional progress to report (for example, the number of transferrable bytes
578      and transferred bytes are equal), the block will not be called again.
579
580      - parameter direction: The transfer direction (upload or download) to track in this progress notification block.
581      - parameter mode:      The desired behavior of this progress notification block.
582      - parameter block:     The block to invoke when notifications are available.
583
584      - returns: A token which must be held for as long as you want notifications to be delivered.
585
586      - see: `ProgressDirection`, `Progress`, `ProgressNotificationToken`
587      */
588     public func addProgressNotification(for direction: ProgressDirection,
589                                         mode: ProgressMode,
590                                         block: @escaping (Progress) -> Void) -> ProgressNotificationToken? {
591         return __addProgressNotification(for: (direction == .upload ? .upload : .download),
592                                          mode: (mode == .reportIndefinitely
593                                             ? .reportIndefinitely
594                                             : .forCurrentlyOutstandingWork)) { transferred, transferrable in
595                                                 block(Progress(transferred: transferred, transferrable: transferrable))
596         }
597     }
598 }
599
600 extension Realm {
601     /**
602      If the Realm is a partially synchronized Realm, fetch and synchronize the objects
603      of a given object type that match the given query (in string format).
604
605      The results will be returned asynchronously in the callback.
606      Use `Results.observe(_:)` to be notified to changes to the set of synchronized objects.
607
608      -warning: Partial synchronization is a tech preview. Its APIs are subject to change.
609      */
610     public func subscribe<T: Object>(to objects: T.Type, where: String,
611                                      completion: @escaping (Results<T>?, Swift.Error?) -> Void) {
612         rlmRealm.subscribe(toObjects: objects, where: `where`) { (results, error) in
613             completion(results.map { Results<T>($0) }, error)
614         }
615     }
616 }
617
618 // MARK: - Permissions and permission results
619
620 extension SyncPermission: RealmCollectionValue { }
621
622 /**
623  A `Results` collection containing sync permission results.
624  */
625 public typealias SyncPermissionResults = Results<SyncPermission>
626
627 /**
628  A property upon which a `SyncPermissionResults` can be sorted or queried.
629  The raw value string can be used to construct predicates and queries
630  manually.
631
632  - warning: If building `NSPredicate`s using format strings including these
633             raw values, use `%K` instead of `%@` as the substitution
634             parameter.
635
636  - see: `RLMSyncPermissionSortProperty`
637  */
638 public typealias SyncPermissionSortProperty = RLMSyncPermissionSortProperty
639
640 extension SortDescriptor {
641     /**
642      Construct a sort descriptor using a `SyncPermissionSortProperty`.
643      */
644     public init(sortProperty: SyncPermissionSortProperty, ascending: Bool = true) {
645         self.init(keyPath: sortProperty.rawValue, ascending: ascending)
646     }
647 }
648
649 #if swift(>=3.1)
650 extension Results where Element == SyncPermission {
651     /**
652      Return a `Results<SyncPermissionValue>` containing the objects represented
653      by the results, but sorted on the specified property.
654
655      - see: `sorted(byKeyPath:, ascending:)`
656      */
657     public func sorted(bySortProperty sortProperty: SyncPermissionSortProperty,
658                        ascending: Bool = true) -> Results<Element> {
659         return sorted(by: [SortDescriptor(sortProperty: sortProperty, ascending: ascending)])
660     }
661 }
662 #endif
663
664 // MARK: - Migration assistance
665
666 /// :nodoc:
667 @available(*, unavailable, renamed: "SyncPermission")
668 public final class SyncPermissionValue { }