-
Notifications
You must be signed in to change notification settings - Fork 89
(DOCSP-26313): Swift: Add a Handle Sync Errors page for SwiftUI #2524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0925a29
4dd976d
35519d8
7ab90de
e46ad32
13b3bf4
3f3c731
31d93b6
6fb63bc
25e7d7d
68fff5a
6e17dbc
4663354
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,62 @@ struct FlexibleSyncContentView: View { | |
} | ||
// :snippet-end: | ||
|
||
// :snippet-start: flexible-sync-view-with-client-reset-handling | ||
struct FlexSyncContentView: View { | ||
// Observe the Realm App object in order to react to login state changes. | ||
@ObservedObject var flexibleSyncApp: RealmSwift.App | ||
// Use the error handler that you've injected into the environment | ||
// to react to Device Sync errors. | ||
// :uncomment-start: | ||
//@EnvironmentObject var errorHandler: ErrorHandler | ||
// :uncomment-end: | ||
|
||
var body: some View { | ||
if let user = flexibleSyncApp.currentUser { | ||
let config = user.flexibleSyncConfiguration( | ||
// :emphasize-start: | ||
clientResetMode: .recoverUnsyncedChanges( | ||
beforeReset: { realm in | ||
// A block called after a client reset error is detected, but before the | ||
// client recovery process is executed. | ||
// This block could be used for any custom logic, reporting, debugging etc. | ||
// For more information, refer to: https://www.mongodb.com/docs/realm/sdk/swift/sync/handle-sync-errors/ | ||
print("Before client reset block") | ||
}, afterReset: { before,after in | ||
// A block called after the client recovery process has executed. | ||
// This block could be used for custom recovery, reporting, debugging etc. | ||
// For SwiftUI, you might modify a @State variable to trigger views to reload | ||
// or advise the user to restart the app. | ||
// For more information, refer to: https://www.mongodb.com/docs/realm/sdk/swift/sync/handle-sync-errors/ | ||
print("After client reset block") | ||
}), | ||
// :emphasize-end: | ||
initialSubscriptions: { subs in | ||
let peopleSubscriptionExists = subs.first(named: "people") | ||
let dogSubscriptionExists = subs.first(named: "dogs") | ||
// Check whether the subscription already exists. Adding it more | ||
// than once causes an error. | ||
if (peopleSubscriptionExists != nil) && (dogSubscriptionExists != nil) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not needed to specified != than nil, you can
or
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just tried updating this syntax, but I get an error message:
That's why I originally had the != nil in the example. This seemed like the easiest way to bypass the optional unwrapping. |
||
// Existing subscriptions found - do nothing | ||
return | ||
} else { | ||
// Add queries for any objects you want to use in the app. | ||
// Linked objects do not automatically get queried, so you | ||
// must explicitly query for all linked objects you want to include. | ||
subs.append(QuerySubscription<SwiftUI_Person>(name: "people")) | ||
subs.append(QuerySubscription<SwiftUI_Dog>(name: "dogs")) | ||
} | ||
} | ||
) | ||
OpenFlexibleSyncRealmView() | ||
.environment(\.realmConfiguration, config) | ||
} else { | ||
FlexibleSyncLoginView() | ||
} | ||
} | ||
} | ||
// :snippet-end: | ||
|
||
struct PBSContentView: View { | ||
// Observe the Realm app object in order to react to login state changes. | ||
@ObservedObject var partitionBasedSyncApp: RealmSwift.App | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import SwiftUI | ||
import RealmSwift | ||
import Foundation | ||
|
||
// :snippet-start: swiftui-app-with-error-handler | ||
let app = App(id: flexibleSyncAppId) | ||
|
||
// :uncomment-start: | ||
// @main | ||
// :uncomment-end: | ||
struct realmSwiftUIApp: SwiftUI.App { | ||
// Initialize the error handler | ||
@StateObject var errorHandler = ErrorHandler(app: app) | ||
|
||
var body: some Scene { | ||
WindowGroup { | ||
NextView(app: app) | ||
// Inject the error handler as an environment object | ||
.environmentObject(errorHandler) | ||
// Display an alert to the user containing the error when a Sync error occurs | ||
.alert(Text("Error"), isPresented: .constant(errorHandler.error != nil)) { | ||
Button("OK", role: .cancel) { errorHandler.error = nil } | ||
} message: { | ||
Text(errorHandler.error?.localizedDescription ?? "") | ||
} | ||
} | ||
} | ||
} | ||
// :snippet-end: | ||
|
||
// :snippet-start: swiftui-error-handler | ||
final class ErrorHandler: ObservableObject { | ||
@Published var error: Swift.Error? | ||
|
||
init(app: RealmSwift.App) { | ||
// Sync Manager listens for sync errors. | ||
app.syncManager.errorHandler = { error, syncSession in | ||
if let error = error as? SyncError { | ||
/* Handle specific SyncError cases, or use a switch | ||
* statement to handle all Sync error codes. | ||
* In this case, ignore a .connectionFailed error and | ||
* continue executing the app code. */ | ||
if error.code == .connectionFailed { | ||
return | ||
} | ||
self.error = error | ||
} else if let error = error as? POSIXError { | ||
/* The error handler may also report NSError types to | ||
* allow for error handling in a platform-idiomatic way. | ||
* In this case, handle a connection timeout error as | ||
* an .ETIMEDOUT error in the POSIXError domain. */ | ||
if error.code == .ETIMEDOUT { | ||
return | ||
} | ||
self.error = error | ||
} | ||
} | ||
} | ||
} | ||
// :snippet-end: | ||
|
||
// :snippet-start: use-app-and-error-handler-in-next-view | ||
struct NextView: View { | ||
@ObservedObject var app: RealmSwift.App | ||
// Use the error handler that you injected into the environment | ||
@EnvironmentObject var errorHandler: ErrorHandler | ||
|
||
var body: some View { | ||
Text("You might log users in or handle errors in this view") | ||
} | ||
} | ||
// :snippet-end: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
.. code-block:: swift | ||
:emphasize-lines: 11-25 | ||
|
||
struct FlexSyncContentView: View { | ||
// Observe the Realm App object in order to react to login state changes. | ||
@ObservedObject var app: RealmSwift.App | ||
// Use the error handler that you've injected into the environment | ||
// to react to Device Sync errors. | ||
@EnvironmentObject var errorHandler: ErrorHandler | ||
|
||
var body: some View { | ||
if let user = app.currentUser { | ||
let config = user.flexibleSyncConfiguration( | ||
clientResetMode: .recoverUnsyncedChanges( | ||
beforeReset: { realm in | ||
// A block called after a client reset error is detected, but before the | ||
// client recovery process is executed. | ||
// This block could be used for any custom logic, reporting, debugging etc. | ||
// For more information, refer to: https://www.mongodb.com/docs/realm/sdk/swift/sync/handle-sync-errors/ | ||
print("Before client reset block") | ||
}, afterReset: { before,after in | ||
// A block called after the client recovery process has executed. | ||
// This block could be used for custom recovery, reporting, debugging etc. | ||
// For SwiftUI, you might modify a @State variable to trigger views to reload | ||
// or advise the user to restart the app. | ||
// For more information, refer to: https://www.mongodb.com/docs/realm/sdk/swift/sync/handle-sync-errors/ | ||
print("After client reset block") | ||
}), | ||
initialSubscriptions: { subs in | ||
let peopleSubscriptionExists = subs.first(named: "people") | ||
let dogSubscriptionExists = subs.first(named: "dogs") | ||
// Check whether the subscription already exists. Adding it more | ||
// than once causes an error. | ||
if (peopleSubscriptionExists != nil) && (dogSubscriptionExists != nil) { | ||
// Existing subscriptions found - do nothing | ||
return | ||
} else { | ||
// Add queries for any objects you want to use in the app. | ||
// Linked objects do not automatically get queried, so you | ||
// must explicitly query for all linked objects you want to include. | ||
subs.append(QuerySubscription<Person>(name: "people")) | ||
subs.append(QuerySubscription<Dog>(name: "dogs")) | ||
} | ||
} | ||
) | ||
OpenFlexibleSyncRealmView() | ||
.environment(\.realmConfiguration, config) | ||
} else { | ||
LoginView() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
let app = App(id: flexibleSyncAppId) | ||
|
||
@main | ||
struct realmSwiftUIApp: SwiftUI.App { | ||
// Initialize the error handler | ||
@StateObject var errorHandler = ErrorHandler(app: app) | ||
|
||
var body: some Scene { | ||
WindowGroup { | ||
NextView(app: app) | ||
// Inject the error handler as an environment object | ||
.environmentObject(errorHandler) | ||
// Display an alert to the user containing the error when a Sync error occurs | ||
.alert(Text("Error"), isPresented: .constant(errorHandler.error != nil)) { | ||
Button("OK", role: .cancel) { errorHandler.error = nil } | ||
} message: { | ||
Text(errorHandler.error?.localizedDescription ?? "") | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
final class ErrorHandler: ObservableObject { | ||
@Published var error: Swift.Error? | ||
|
||
init(app: RealmSwift.App) { | ||
// Sync Manager listens for sync errors. | ||
app.syncManager.errorHandler = { error, syncSession in | ||
if let error = error as? SyncError { | ||
/* Handle specific SyncError cases, or use a switch | ||
* statement to handle all Sync error codes. | ||
* In this case, ignore a .connectionFailed error and | ||
* continue executing the app code. */ | ||
if error.code == .connectionFailed { | ||
return | ||
} | ||
self.error = error | ||
} else if let error = error as? POSIXError { | ||
/* The error handler may also report NSError types to | ||
* allow for error handling in a platform-idiomatic way. | ||
* In this case, handle a connection timeout error as | ||
* an .ETIMEDOUT error in the POSIXError domain. */ | ||
if error.code == .ETIMEDOUT { | ||
return | ||
} | ||
self.error = error | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
struct NextView: View { | ||
@ObservedObject var app: RealmSwift.App | ||
// Use the error handler that you injected into the environment | ||
@EnvironmentObject var errorHandler: ErrorHandler | ||
|
||
var body: some View { | ||
Text("You might log users in or handle errors in this view") | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.