Skip to content
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

feat(DataStore): DisableSubscriptions flag for watchOS #3368

Merged
merged 9 commits into from
Dec 5, 2023

Conversation

lawmicha
Copy link
Contributor

@lawmicha lawmicha commented Nov 17, 2023

Issue #

#3220

Table of Contents

  1. fix(DataStore): Store larger than 32-bit values in Int64 over Int #3367
  2. feat(DataStore): DisableSubscriptions flag for watchOS #3368 (You are here)
  3. fix: watchOS support - disable network monitoring, add client timeout #141

Description

DataStore’s sync engine currently does not start successfully on watchOS. The reason for this is due to the lack of low-level networking capabilities on watchOS, which DataStore uses when establishing subscriptions for real-time data. When DataStore’s sync engine attempts to establish the websocket connection, it will hang on connecting, never completing the sync engine activation steps.

Low-level networking on watchOS is limited but there are special circumstances in which an app does allow low-level networking, such as websockets, to work. They are listed here:

  • While actively streaming audio
  • While running a call with CallKit

This PR introduces a required client configuration option to disable subscriptions when building on watchOS. When building an watchOS app, set this value to what is required for your app to work properly. In most cases, if you are not operating in the special circumstance, then set the implementation of disableSubscriptions to return true

let plugin = AWSDataStorePlugin(modelRegistration: AmplifyModels(),
                                        configuration: .custom(disableSubscriptions: { true }))

Limited Functionality

With this value set to true, during the runtime of the app, data saved from other devices will not appear on the current device until DataStore has been restarted.

DataStore will start when any of the DataStore operations are called, or explicitly through DataStore.start(). When it starts, it will sync the latest data from the remote store.

Force restart DataStore

You may find it useful to restart DataStore by calling DataStore.stop() followed by DataStore.start() in parts of you application such as navigating to a view of the app that uses data from DataStore or based on some end-user actions.

Conflict Handler
If two devices are operating on the same model data, and DataStore is running without subscriptions, there will likely be conflicts. To handle conflicting updates, based on your use cases, you can implement conflictHandler to make decisions such as when there's a conflict, to retry the local model, remote model, or a merged model to be re-synced. See conflict resolution for more details

Special Circumstances

If you are building a watchOS app in which uses low-level networking during the special circumstance, you can re-enable subscriptions to get real-time updates during the special circumstances:

var subscriptionsDisabled = true
let disableSubscriptions = {
    subscriptionsDisabled
}
try Amplify.add(plugin: AWSAPIPlugin())
try Amplify.add(plugin: AWSDataStorePlugin(
    modelRegistration: AmplifyModels(),
    configuration: .custom(disableSubscriptions: disableSubscriptions)))
try Amplify.configure()
    
// 1. Activate special circumstances
AVAudioSession.sharedInstance().activate(options: [], completionHandler: { _, _ in 
    print("AVAudioSession activated.")
    
    // 2. enable subscriptions
    subscriptionsDisabled = false 

    // 3. restart datastore
    try await Amplify.DataStore.stop() 
    try await Amplify.DataStore.start()
})

// ...
// 4. When deactivating the audio session, disable subscriptions, and restart DataStore.
try AVAudioSession.sharedInstance().setActive(false)
subscriptionsDisabled = true
try await Amplify.DataStore.stop() 
try await Amplify.DataStore.start()

General Checklist

  • Added new tests to cover change, if needed
  • Build succeeds with all target using Swift Package Manager
  • All unit tests pass
  • All integration tests pass
  • Security oriented best practices and standards are followed (e.g. using input sanitization, principle of least privilege, etc)
  • Documentation update for the change if required
  • PR title conforms to conventional commit style
  • New or updated tests include Given When Then inline code documentation and are named accordingly testThing_condition_expectation()
  • If breaking change, documentation/changelog update with migration instructions

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@codecov-commenter
Copy link

codecov-commenter commented Nov 17, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

❗ No coverage uploaded for pull request base (lawmicha.int64@b622589). Click here to learn what that means.

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@                Coverage Diff                @@
##             lawmicha.int64    #3368   +/-   ##
=================================================
  Coverage                  ?   67.99%           
=================================================
  Files                     ?     1078           
  Lines                     ?    36002           
  Branches                  ?        0           
=================================================
  Hits                      ?    24480           
  Misses                    ?    11522           
  Partials                  ?        0           
Flag Coverage Δ
API_plugin_unit_test 67.07% <0.00%> (?)
AWSPluginsCore 64.45% <0.00%> (?)
Amplify 47.81% <0.00%> (?)
Analytics_plugin_unit_test 81.16% <0.00%> (?)
Auth_plugin_unit_test 79.54% <0.00%> (?)
CoreMLPredictions_plugin_unit_test 59.44% <0.00%> (?)
DataStore_plugin_unit_test 79.88% <0.00%> (?)
Geo_plugin_unit_test 70.75% <0.00%> (?)
Logging_plugin_unit_test 63.09% <0.00%> (?)
Predictions_plugin_unit_test 37.29% <0.00%> (?)
PushNotifications_plugin_unit_test 87.03% <0.00%> (?)
Storage_plugin_unit_test 78.00% <0.00%> (?)
unit_tests 67.99% <0.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@lawmicha lawmicha force-pushed the lawmicha.disable-subscriptions branch from ad032ec to 145387b Compare November 21, 2023 16:02
@lawmicha lawmicha changed the title feat(DataStore): DisableRealTimeUpdates flag feat(DataStore): DisableSubscriptions flag for watchOS Nov 21, 2023
@lawmicha lawmicha force-pushed the lawmicha.disable-subscriptions branch from 145387b to e427d72 Compare November 21, 2023 16:06
syncPageSize: UInt = DataStoreConfiguration.defaultSyncPageSize,
syncExpressions: [DataStoreSyncExpression] = [],
authModeStrategy: AuthModeStrategyType = .default,
disableSubscriptions: @escaping () -> Bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is a bit confusing, as it's actually more of a "delegate" callback than a setter.

How about areSubscriptionsDisabled or something like that?
Alternatively we can make this an actual non-optional delegate, but it's probably an overkill at this point.


#if os(watchOS)
/// Internal method for testing
static func testDefault(disableSubscriptions: @escaping () -> Bool = { false }) -> DataStoreConfiguration {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of the name, but I also can't think of anything other than internalDefault, which I equally dislike 😅

sebaland
sebaland previously approved these changes Nov 27, 2023
@lawmicha lawmicha force-pushed the lawmicha.disable-subscriptions branch from 371f6bb to e0d1f41 Compare November 30, 2023 16:45
Base automatically changed from lawmicha.int64 to main December 1, 2023 16:11
@lawmicha lawmicha dismissed sebaland’s stale review December 1, 2023 16:11

The base branch was changed.

@lawmicha lawmicha merged commit 459aaed into main Dec 5, 2023
72 checks passed
@lawmicha lawmicha deleted the lawmicha.disable-subscriptions branch December 5, 2023 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants