1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2016 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 ////////////////////////////////////////////////////////////////////////////
23 An object representing a Realm Object Server user.
27 public typealias SyncUser = RLMSyncUser
30 An immutable data object representing information retrieved from the Realm Object
31 Server about a particular user.
33 - see: `RLMSyncUserInfo`
35 public typealias SyncUserInfo = RLMSyncUserInfo
38 An immutable data object representing an account belonging to a particular user.
40 - see: `SyncUserInfo`, `RLMSyncUserAccountInfo`
42 public typealias SyncUserAccountInfo = RLMSyncUserAccountInfo
45 A singleton which configures and manages the Realm Object Server synchronization-related
48 - see: `RLMSyncManager`
50 public typealias SyncManager = RLMSyncManager
52 extension SyncManager {
53 /// The sole instance of the singleton.
54 public static var shared: SyncManager {
60 A session object which represents communication between the client and server for a specific
63 - see: `RLMSyncSession`
65 public typealias SyncSession = RLMSyncSession
68 A closure type for a closure which can be set on the `SyncManager` to allow errors to be reported
71 - see: `RLMSyncErrorReportingBlock`
73 public typealias ErrorReportingBlock = RLMSyncErrorReportingBlock
76 A closure type for a closure which is used by certain APIs to asynchronously return a `SyncUser`
77 object to the application.
79 - see: `RLMUserCompletionBlock`
81 public typealias UserCompletionBlock = RLMUserCompletionBlock
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.
89 public typealias SyncError = RLMSyncError
93 An opaque token allowing the user to take action after certain types of
94 errors have been reported.
96 - see: `RLMSyncErrorActionToken`
98 public typealias ActionToken = RLMSyncErrorActionToken
101 Given a client reset error, extract and return the recovery file path
102 and the action token.
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.
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.
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.
117 - see: `SyncError.ActionToken`, `SyncSession.immediatelyHandleError(_:)`
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)
129 Given a permission denied error, extract and return the action token.
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.
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.
141 - see: `SyncError.ActionToken`, `SyncSession.immediatelyHandleError(_:)`
143 public func deleteRealmUserInfo() -> SyncError.ActionToken? {
144 return _nsError.__rlmSync_errorActionToken()
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).
153 - see: `RLMSyncAuthError`
155 public typealias SyncAuthError = RLMSyncAuthError
158 An error associated with retrieving or modifying user permissions to access a synchronized Realm.
160 - see: `RLMSyncPermissionError`
162 public typealias SyncPermissionError = RLMSyncPermissionError
165 An enum which can be used to specify the level of logging.
167 - see: `RLMSyncLogLevel`
169 public typealias SyncLogLevel = RLMSyncLogLevel
172 A data type whose values represent different authentication providers that can be used with
173 the Realm Object Server.
175 - see: `RLMIdentityProvider`
177 public typealias Provider = RLMIdentityProvider
180 A `SyncConfiguration` represents configuration parameters for Realms intended to sync with
181 a Realm Object Server.
183 public struct SyncConfiguration {
184 /// The `SyncUser` who owns the Realm that this configuration should open.
185 public let user: SyncUser
188 The URL of the Realm on the Realm Object Server that this configuration should open.
190 - warning: The URL must be absolute (e.g. `realms://example.com/~/foo`), and cannot end with
191 `.realm`, `.realm.lock` or `.realm.management`.
193 public let realmURL: URL
196 A policy that determines what should happen when all references to Realms opened by this
197 configuration go out of scope.
199 internal let stopPolicy: RLMSyncStopPolicy
202 Whether the SSL certificate of the Realm Object Server should be validated.
204 public let enableSSLValidation: Bool
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.
211 -warning: Partial synchronization is a tech preview. Its APIs are subject to change.
213 public let isPartial: Bool
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
223 func asConfig() -> RLMSyncConfiguration {
224 let config = RLMSyncConfiguration(user: user, realmURL: realmURL)
225 config.stopPolicy = stopPolicy
226 config.enableSSLValidation = enableSSLValidation
227 config.isPartial = isPartial
232 Initialize a sync configuration with a user and a Realm URL.
234 Additional settings can be optionally specified. Descriptions of these
237 `enableSSLValidation` is true by default. It can be disabled for debugging
240 - warning: The URL must be absolute (e.g. `realms://example.com/~/foo`), and cannot end with
241 `.realm`, `.realm.lock` or `.realm.management`.
243 - warning: NEVER disable SSL validation for a system running in production.
245 public init(user: SyncUser, realmURL: URL, enableSSLValidation: Bool = true, isPartial: Bool = false) {
247 self.realmURL = realmURL
248 self.stopPolicy = .afterChangesUploaded
249 self.enableSSLValidation = enableSSLValidation
250 self.isPartial = isPartial
254 /// A `SyncCredentials` represents data that uniquely identifies a Realm Object Server user.
255 public struct SyncCredentials {
256 public typealias Token = String
258 internal var token: Token
259 internal var provider: Provider
260 internal var userInfo: [String: Any]
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.
266 public init(customToken token: Token, provider: Provider, userInfo: [String: Any] = [:]) {
268 self.provider = provider
269 self.userInfo = userInfo
272 internal init(_ credentials: RLMSyncCredentials) {
273 self.token = credentials.token
274 self.provider = credentials.provider
275 self.userInfo = credentials.userInfo
278 /// Initialize new credentials using a Facebook account token.
279 public static func facebook(token: Token) -> SyncCredentials {
280 return SyncCredentials(RLMSyncCredentials(facebookToken: token))
283 /// Initialize new credentials using a Google account token.
284 public static func google(token: Token) -> SyncCredentials {
285 return SyncCredentials(RLMSyncCredentials(googleToken: token))
288 /// Initialize new credentials using a CloudKit account token.
289 public static func cloudKit(token: Token) -> SyncCredentials {
290 return SyncCredentials(RLMSyncCredentials(cloudKitToken: token))
293 /// Initialize new credentials using a Realm Object Server username and password.
294 public static func usernamePassword(username: String,
296 register: Bool = false) -> SyncCredentials {
297 return SyncCredentials(RLMSyncCredentials(username: username, password: password, register: register))
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))
305 /// Initialize new credentials using a JSON Web Token.
306 public static func jwt(_ token: Token) -> SyncCredentials {
307 return SyncCredentials(RLMSyncCredentials(jwt: token))
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))
315 /// Initialize new credentials anonymously
316 public static func anonymous() -> SyncCredentials {
317 return SyncCredentials(RLMSyncCredentials.anonymous())
321 extension RLMSyncCredentials {
322 internal convenience init(_ credentials: SyncCredentials) {
323 self.init(customToken: credentials.token, provider: credentials.provider, userInfo: credentials.userInfo)
329 Log in a user and asynchronously retrieve a user object.
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.
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.
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,
350 callbackQueue: queue,
351 onCompletion: completion)
354 /// A dictionary of all valid, logged-in user identities corresponding to their `SyncUser` objects.
355 public static var all: [String: SyncUser] {
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.
363 - warning: Throws an Objective-C exception if more than one logged-in user exists.
365 public static var current: SyncUser? {
370 An optional error handler which can be set to notify the host application when
371 the user encounters an error.
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.
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.
380 @nonobjc public var errorHandler: ((SyncUser, SyncAuthError) -> Void)? {
382 return __errorHandler
385 if let newValue = newValue {
386 __errorHandler = { (user, error) in
387 newValue(user, error as! SyncAuthError)
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.
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.
402 - warning: This method must be invoked on a thread with an active run loop.
404 - warning: Do not pass the `Results` returned by the callback between threads.
406 - parameter callback: A callback providing either a `Results` containing the
407 permissions, or an error describing what went wrong.
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?)
415 let upcasted: RLMResults<SyncPermission> = results
416 callback(Results(upcasted as! RLMResults<AnyObject>), nil)
421 Create a permission offer for a Realm.
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).
427 The operation will take place asynchronously. The token can be accepted by the recepient
428 using the `SyncUser.acceptOffer(forToken:, callback:)` method.
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.
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?)
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`.
459 - see: `RLMSyncPermission`
461 public typealias SyncPermission = RLMSyncPermission
464 An enumeration describing possible access levels.
466 - see: `RLMSyncAccessLevel`
468 public typealias SyncAccessLevel = RLMSyncAccessLevel
470 public extension SyncSession {
472 The transfer direction (upload or download) tracked by a given progress notification block.
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.
477 public enum ProgressDirection {
478 /// For monitoring upload progress.
480 /// For monitoring download progress.
485 The desired behavior of a progress notification block.
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.
490 public enum ProgressMode {
492 The block will be called forever, or until it is unregistered by calling
493 `ProgressNotificationToken.invalidate()`.
495 Notifications will always report the latest number of transferred bytes, and the
496 most up-to-date number of total transferrable bytes.
498 case reportIndefinitely
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.
504 When the number of transferred bytes reaches or exceeds the
505 number of transferrable bytes, the block will be unregistered.
507 case forCurrentlyOutstandingWork
511 A token corresponding to a progress notification block.
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.
517 public typealias ProgressNotificationToken = RLMProgressNotificationToken
520 A struct encapsulating progress information, as well as useful helper methods.
522 public struct Progress {
523 /// The number of bytes that have been transferred.
524 public let transferredBytes: Int
527 The total number of transferrable bytes (bytes that have been transferred,
528 plus bytes pending transfer).
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.
535 public let transferrableBytes: Int
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 {
544 let percentage = Double(transferredBytes) / Double(transferrableBytes)
545 return percentage > 1 ? 1 : percentage
548 /// Whether all pending bytes have already been transferred.
549 public var isTransferComplete: Bool {
550 return transferredBytes >= transferrableBytes
553 fileprivate init(transferred: UInt, transferrable: UInt) {
554 transferredBytes = Int(transferred)
555 transferrableBytes = Int(transferrable)
560 Register a progress notification block.
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.
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.
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.
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.
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.
584 - returns: A token which must be held for as long as you want notifications to be delivered.
586 - see: `ProgressDirection`, `Progress`, `ProgressNotificationToken`
588 public func addProgressNotification(for direction: ProgressDirection,
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))
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).
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.
608 -warning: Partial synchronization is a tech preview. Its APIs are subject to change.
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)
618 // MARK: - Permissions and permission results
620 extension SyncPermission: RealmCollectionValue { }
623 A `Results` collection containing sync permission results.
625 public typealias SyncPermissionResults = Results<SyncPermission>
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
632 - warning: If building `NSPredicate`s using format strings including these
633 raw values, use `%K` instead of `%@` as the substitution
636 - see: `RLMSyncPermissionSortProperty`
638 public typealias SyncPermissionSortProperty = RLMSyncPermissionSortProperty
640 extension SortDescriptor {
642 Construct a sort descriptor using a `SyncPermissionSortProperty`.
644 public init(sortProperty: SyncPermissionSortProperty, ascending: Bool = true) {
645 self.init(keyPath: sortProperty.rawValue, ascending: ascending)
650 extension Results where Element == SyncPermission {
652 Return a `Results<SyncPermissionValue>` containing the objects represented
653 by the results, but sorted on the specified property.
655 - see: `sorted(byKeyPath:, ascending:)`
657 public func sorted(bySortProperty sortProperty: SyncPermissionSortProperty,
658 ascending: Bool = true) -> Results<Element> {
659 return sorted(by: [SortDescriptor(sortProperty: sortProperty, ascending: ascending)])
664 // MARK: - Migration assistance
667 @available(*, unavailable, renamed: "SyncPermission")
668 public final class SyncPermissionValue { }