diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index cbc53b12e..51b9d83d2 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -18,26 +18,18 @@ Then perform these steps in order: 2. Update the version in these files: - `pubspec.yaml` — the `version` property - - `README.md` — the `ably_flutter: ^OLD_VERSION` line in the Installation section (update to `^NEW_VERSION`) + - `README.md` — the `ably_pubsub_device_flutter: ^OLD_VERSION` line in the Installation section (update to `^NEW_VERSION`) + - `ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterVersion.h` — the `FLUTTER_PACKAGE_PLUGIN_VERSION` string (the iOS plugin reports it to Ably; `test/ios_plugin_version_test.dart` checks it matches `pubspec.yaml`) -3. Regenerate lock files: - - a. Run `flutter pub get` in `example/` and `test_integration/` to update their `pubspec.lock` files: +3. Regenerate lock files. Run `flutter pub get` in `example/` and `test_integration/` to update their `pubspec.lock` files: ``` cd example && flutter pub get && cd .. cd test_integration && flutter pub get && cd .. ``` If either directory doesn't exist, skip it and note it in the summary. - b. Regenerate iOS Podfile.lock files by running: - ``` - pod install --project-directory=example/ios - pod install --project-directory=test_integration/ios - ``` - If CocoaPods is not installed or either directory doesn't exist, skip that directory and note it in the summary. - 4. Commit all changed files together with message: `chore: bump version to NEW_VERSION` - Stage: `pubspec.yaml`, `README.md`, `example/pubspec.lock`, `example/ios/Podfile.lock`, `test_integration/pubspec.lock`, `test_integration/ios/Podfile.lock` (only include files that actually changed). + Stage: `pubspec.yaml`, `README.md`, `ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterVersion.h`, `example/pubspec.lock`, `test_integration/pubspec.lock` (only include files that actually changed). 5. Fetch merged PRs since the last release tag: ``` diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 12ba268e3..c8aba2fd2 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -19,7 +19,7 @@ jobs: - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - flutter-version: '3.24' + flutter-version: '3.47.3' cache: true # This step requires fetch of test_integration packages because flutter format and @@ -37,3 +37,10 @@ jobs: - name: 'Run unit tests' run: flutter test --no-pub + + # Runs the same validation as the publish step in release.yaml, so a + # problem that would block publishing to pub.dev (a missing CHANGELOG + # entry, an invalid pubspec, an oversized archive) fails here first. + # Warnings fail the step; hints don't. + - name: 'Check the package can be published' + run: flutter pub publish --dry-run diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3f9cb3860..83cd926dd 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - flutter-version: '3.24' + flutter-version: '3.47.3' cache: true - name: Use Intro Blurb As Homepage diff --git a/.github/workflows/flutter_example_app.yaml b/.github/workflows/flutter_example_app.yaml index 5667878ae..e223b4962 100644 --- a/.github/workflows/flutter_example_app.yaml +++ b/.github/workflows/flutter_example_app.yaml @@ -9,18 +9,25 @@ permissions: jobs: ios: - runs-on: "macos-latest" + runs-on: macos-26 # ably-pubsub-cocoa 2.0 needs Xcode 26 steps: + # Check out into a directory named after the plugin. When Flutter adds + # Swift Package Manager integration to a plugin's own example app, it + # references the plugin's package both directly (identity: + # `ably_pubsub_device_flutter`) and through a symlink named after the + # checkout directory, and package resolution fails if the two differ. - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false + path: ably_pubsub_device_flutter - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - flutter-version: "3.24" + flutter-version: "3.47.3" cache: true - name: "Build example app for iOS" + working-directory: ably_pubsub_device_flutter run: | flutter pub get cd example && flutter build ios --no-codesign @@ -39,7 +46,7 @@ jobs: - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - flutter-version: "3.29" + flutter-version: "3.47.3" cache: true - name: "Build example app for Android" diff --git a/.github/workflows/flutter_integration.yaml b/.github/workflows/flutter_integration.yaml index b6bb41ebe..fe1308953 100644 --- a/.github/workflows/flutter_integration.yaml +++ b/.github/workflows/flutter_integration.yaml @@ -10,7 +10,7 @@ permissions: jobs: ios: - runs-on: macos-latest + runs-on: macos-26 # ably-pubsub-cocoa 2.0 needs Xcode 26 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: @@ -18,13 +18,13 @@ jobs: - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - flutter-version: '3.29' + flutter-version: '3.47.3' cache: true - # test_integration package depends on ably_flutter, so before we run integration + # test_integration package depends on ably_pubsub_device_flutter, so before we run integration # tests it's best to perform dependency update in both packages, to make sure that # integration tests are run with exactly the same dependencies as specified in - # current version of ably_flutter package + # current version of ably_pubsub_device_flutter package - name: Update dependencies run: | flutter pub get @@ -81,7 +81,7 @@ jobs: - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - flutter-version: '3.29' + flutter-version: '3.47.3' cache: true - name: 'Run Flutter Driver tests' @@ -89,8 +89,11 @@ jobs: uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0 with: api-level: ${{ matrix.api-level }} + # Flutter 3.47 treats 32-bit x86 Android devices as unsupported. + arch: x86_64 emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: true script: | flutter pub get - cd test_integration && flutter build apk && flutter drive + adb devices + cd test_integration && flutter drive -d emulator-5554 diff --git a/.github/workflows/ios_unit_tests.yml b/.github/workflows/ios_unit_tests.yml index 71510f3d2..fff960b61 100644 --- a/.github/workflows/ios_unit_tests.yml +++ b/.github/workflows/ios_unit_tests.yml @@ -10,7 +10,7 @@ permissions: jobs: ios-unit-tests: - runs-on: macos-latest + runs-on: macos-26 # ably-pubsub-cocoa 2.0 needs Xcode 26 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: @@ -18,33 +18,9 @@ jobs: - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - flutter-version: '3.29' + flutter-version: '3.47.3' cache: true - - name: Set up iOS test project - # --skip-tests sets up the CocoaPods workspace (downloads all deps, generates - # the ably_flutter-Unit-Tests scheme) without booting a simulator - run: | - cd ios - pod lib lint ably_flutter.podspec \ - --allow-warnings \ - --skip-tests \ - --validation-dir=/tmp/ably_test_build \ - --no-clean - - - name: Pick simulator - run: | - UDID=$(xcrun simctl list devices available | \ - grep -E "iPhone" | head -n 1 | \ - sed -E 's/.*\(([A-F0-9-]+)\).*/\1/') - echo "SIMULATOR_ID=$UDID" >> $GITHUB_ENV - - - name: Run iOS unit tests timeout-minutes: 30 - run: | - xcodebuild test \ - -workspace /tmp/ably_test_build/App.xcworkspace \ - -scheme ably_flutter-Unit-Tests \ - -destination "id=$SIMULATOR_ID,arch=arm64" \ - CODE_SIGNING_ALLOWED=NO + run: ios/run_unit_tests.sh diff --git a/.pubignore b/.pubignore index 2ed1eb012..71d374b9c 100644 --- a/.pubignore +++ b/.pubignore @@ -1,3 +1,20 @@ +# pub uses this file instead of the root .gitignore, so it repeats the +# .gitignore entries that would otherwise be published from a local checkout, +# such as build output. +.DS_Store +.dart_tool/ +.packages +.pub/ +build/ +doc/ +.fvm +.project +.classpath +.settings +.factorypath +.idea/ +*.iml + # Test suite is not published for pub.dev # See test/ diff --git a/.tool-versions b/.tool-versions index 2d7c47ed4..544ec6c3d 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ ruby 3.4.5 -flutter 3.29.0 +flutter 3.47.3-stable diff --git a/API_REF_INTRO_BLURB.md b/API_REF_INTRO_BLURB.md index 2a993a72f..12c6a9064 100644 --- a/API_REF_INTRO_BLURB.md +++ b/API_REF_INTRO_BLURB.md @@ -1,7 +1,7 @@ # Ably Flutter Client Library SDK API Reference -The Flutter Client Library SDK supports a realtime and a REST interface. The Flutter API references are generated from the [Ably Flutter Client Library SDK source code](https://github.com/ably/ably-pubsub-flutter/) using [dartdoc](https://dart.dev/tools/dart-doc) and structured by classes. +The Flutter Client Library SDK supports a realtime interface. The Flutter API references are generated from the [Ably Flutter Client Library SDK source code](https://github.com/ably/ably-pubsub-flutter/) using [dartdoc](https://dart.dev/tools/dart-doc) and structured by classes. -The realtime interface enables a client to maintain a persistent connection to Ably and publish, subscribe and be present on channels. The REST interface is stateless and typically implemented server-side. It is used to make requests such as retrieving statistics, token authentication and publishing to a channel. +The realtime interface enables a client to maintain a persistent connection to Ably and publish, subscribe and be present on channels. View the [Ably docs](https://ably.com/docs/) for conceptual information on using Ably, and for API references featuring all languages. The combined [API references](https://ably.com/docs/api/) are organized by features and split between the [realtime](https://ably.com/docs/api/realtime-sdk) and [REST](https://ably.com/docs/api/rest-sdk) interfaces. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index feaf7be23..be88dc677 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Contributing to Ably Flutter ## Overview -A [Flutter](https://flutter.dev/) plugin for [Ably](https://www.ably.com), built on top of Ably's [iOS](https://github.com/ably/ably-cocoa) and [Android](https://github.com/ably/ably-java) SDKs. +A [Flutter](https://flutter.dev/) plugin for [Ably](https://www.ably.com), built on top of Ably's [iOS](https://github.com/ably/ably-pubsub-cocoa) and [Android](https://github.com/ably/ably-pubsub-java) SDKs. ## Running the example @@ -49,6 +49,7 @@ The code in this repository has been constructed to be [built as a Flutter Plugin](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin). In this repository the main branch contains the latest development version of the Ably SDK. All development (bug fixing, feature implementation, etc.) is done against the main branch, which you should branch from whenever you'd like to make modifications. Here's the steps to follow when contributing to this repository. - Fork it +- Clone it into a directory named `ably_pubsub_device_flutter` (see [iOS: clone directory name](#ios-clone-directory-name)) - Install and update dependencies (`flutter pub get`) - Create your feature branch from `main` - Run static analysis on your changes and make sure there are no issues (`flutter analyze`) @@ -57,6 +58,22 @@ The code in this repository has been constructed to be - Push to the branch - Create a new Pull Request +### iOS: clone directory name + +The iOS plugin is a Swift package, at [ios/ably_pubsub_device_flutter](ios/ably_pubsub_device_flutter). When Flutter builds the example app, it references that package twice: directly, which gives it the identity `ably_pubsub_device_flutter`, and through a symlink named after the directory the repository is cloned into. Swift Package Manager rejects the build if the two identities differ, with an error like: + +``` +unable to override package 'ably_pubsub_device_flutter' because its identity 'ably-pubsub-flutter' doesn't match override's identity (directory name) 'ably_pubsub_device_flutter' +``` + +So clone the repository into a directory named `ably_pubsub_device_flutter`: + +```bash +git clone https://github.com/ably/ably-pubsub-flutter.git ably_pubsub_device_flutter +``` + +If you've already cloned it under another name, rename the directory, then delete `example/build` and `example/ios/Flutter/ephemeral`, which cache the old name. Apps that depend on this plugin from pub.dev, and the `test_integration` app, are not affected. + ### Hot restart When performing `hot restart`, it's required to also reset the platform clients by calling: @@ -87,15 +104,15 @@ After the app is launched, it can be re-launched from XCode/Android Studio with ### Testing changes in platform libraries -After making changes to `ably-java` or `ably-cocoa`, you can test changes without releasing those dependencies to users. To do this, you need a local copy of the repo with the changes you want to test. +After making changes to `ably-pubsub-java` or `ably-pubsub-cocoa`, you can test changes without releasing those dependencies to users. To do this, you need a local copy of the repo with the changes you want to test. -To test `ably-cocoa` changes, in `Podfile`, below `target 'Runner' do`, add: +To test `ably-pubsub-cocoa` changes, in [ios/ably_pubsub_device_flutter/Package.swift](ios/ably_pubsub_device_flutter/Package.swift), temporarily replace the `ably-pubsub-cocoa` dependency with a local one: -```ruby - pod 'Ably', :path => 'local/path/to/ably-cocoa' +```swift +.package(name: "ably-pubsub-cocoa", path: "local/path/to/ably-pubsub-cocoa"), ``` -To test `ably-java` changes, see [Using ably-java / ably-android locally in other projects](https://github.com/ably/ably-java/blob/main/CONTRIBUTING.md#using-ably-java--ably-android-locally-in-other-projects). +To test `ably-pubsub-java` changes, publish them to your local Maven repository and point [android/build.gradle](android/build.gradle) at that version; see the [ably-pubsub-java contributing guide](https://github.com/ably/ably-pubsub-java/blob/main/CONTRIBUTING.md). ### Generating platform constants @@ -120,9 +137,9 @@ dart codegen.dart so new codec type is obtained from runtime type 7. Update `codecMap` in [android.src.main.java.io.ably.flutter.plugin.AblyMessageCodec](android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java) so new encoder/decoder is assigned for new type -8. Add new codec encoder method in [ios.Classes.codec.AblyFlutterWriter](ios/Classes/codec/AblyFlutterWriter.m) +8. Add new codec encoder method in [AblyFlutterWriter](ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m) and update `getType` and `getEncoder` so that new codec encoder is called -9. Add new codec encoder method in [ios.classes.codec.AblyFlutterReader](ios/Classes/codec/AblyFlutterReader.m) +9. Add new codec encoder method in [AblyFlutterReader](ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m) and update `getDecoder` so that new codec decoder is called ### Implementing new platform methods @@ -131,7 +148,7 @@ dart codegen.dart 2. Generate platform constants 3. Add new method in [android.src.main.java.io.ably.flutter.plugin.AblyMethodCallHandler.java](android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java) and update `_map` so that new method name is assigned to a method call -4. Add new method in [ios.classes.AblyFlutter](ios/Classes/AblyFlutter.m) +4. Add new method in [AblyFlutter](ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m) and update `initWithChannel` so that new method name is assigned to a method call ## Running Tests @@ -144,6 +161,16 @@ Unit tests are located in [test](test/) directory and built on top of `flutter_t flutter test ``` +### iOS unit tests + +The iOS plugin's XCTest suite lives in [ios/ably_pubsub_device_flutter/Tests](ios/ably_pubsub_device_flutter/Tests). Run it on a simulator with: + +```bash +ios/run_unit_tests.sh +``` + +The script generates the `FlutterFramework` package the plugin's `Package.swift` expects alongside it (at `ios/FlutterFramework`, gitignored), wrapping the active Flutter SDK's engine, which a Flutter app build would otherwise provide. + ### Integration tests Integration tests are located in [test_integration](test_integration/) directory and use `flutter_test` and `flutter_driver` packages. Integration tests have to be executed on virtual machine or real device, and a dedicated test app is provided to track the test progress. To run the test app and all tests, execute: @@ -196,8 +223,7 @@ The release process must include the following steps: 3. Add a commit to bump the version number - Update the version in `pubspec.yaml` - Update the version in the 'Installation' section of `README.md` - - Update the version of ably-flutter used in the example app and test integration app `podfile.lock` files: - - Run `pod install` in `example/ios` and `test_integration/ios`, or run `pod install --project-directory=example/ios` and `pod install --project-directory=test_integration/ios` + - Update `FLUTTER_PACKAGE_PLUGIN_VERSION` in [AblyFlutterVersion.h](ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterVersion.h) to match; `test/ios_plugin_version_test.dart` fails if they differ - Commit this 4. Run [`github_changelog_generator`](https://github.com/github-changelog-generator/github-changelog-generator) to automate the update of the [CHANGELOG](./CHANGELOG.md). This may require some manual intervention, both in terms of how the command is run and how the change log file is modified. Your mileage may vary: - The command you will need to run will look something like this: `github_changelog_generator -u ably -p ably-flutter --since-tag v1.2.2 --output delta.md --token $GITHUB_TOKEN_WITH_REPO_ACCESS`. Generate token [here](https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token). diff --git a/PushNotifications.md b/PushNotifications.md index 6ecf4acda..7a3719ae2 100644 --- a/PushNotifications.md +++ b/PushNotifications.md @@ -22,10 +22,10 @@ See [issue #226](https://github.com/ably/ably-flutter/issues/226). ## Supported platforms -- Android API level 19+ (Android 4.4+) +- Android API level 24+ (Android 7.0+) - Android devices - Android emulator (with Google APIs) -- iOS 10+ +- iOS 15+ - Physical devices only - **Not supported:** iOS Simulator. Calling [`UIApplication:registerForRemoteNotifications`](https://developer.apple.com/documentation/uikit/uiapplication/1623078-registerforremotenotifications) will result in [`application:didFailToRegisterForRemoteNotificationsWithError`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application) method being called in your AppDelegate with an error: `remote notifications are not supported in the simulator`). This is an iOS simulator limitation. @@ -77,11 +77,19 @@ To get push notifications setup in your own app, read [Setting up your own app]( - Add `remote notification` Background mode: - Under the **Signing & Capabilities** tab, click `+ Capability` and select `Background Modes`. - Check `remote notifications`. -- In your `AppDelegate.swift` or `AppDelegate.m`, implement [`application:didFailToRegisterForRemoteNotificationsWithError:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application). An example is shown in the example app, [`AppDelegate.m`](./example/ios/Runner/AppDelegate.m). +- In your `AppDelegate.swift` or `AppDelegate.m`, implement [`application:didFailToRegisterForRemoteNotificationsWithError:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application). An example is shown in the example app, [`AppDelegate.swift`](./example/ios/Runner/AppDelegate.swift). - During development, place a breakpoint in this method to diagnose why your device cannot register with APNs. This method will be called when there is an error, for example, if entitlements are not configured or when registering for APNs on the iOS simulator. You can check the `error` argument. If an error occurs in this method, Ably will not get the APNs device token. [From the `application(_:didFailToRegisterForRemoteNotificationsWithError:)` documentation](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application): > UIKit calls this method if it was unable to register your app with APNs or if your app is not properly configured for remote notifications. During development, make sure your app has the proper entitlements and that its App ID is configured to support push notifications. You might use your implementation of this method to make a note of the failed registration so that you can try again later. - Like the example app, you do not need to implement [`UIApplication:registerForRemoteNotifications`](https://developer.apple.com/documentation/uikit/uiapplication/1623078-registerforremotenotifications), as the Ably plugin does this for you at app launch. -- Handle messages received on your device by implementing the methods in your `AppDelegate`, such as `didReceiveRemoteNotification`, `didReceiveNotificationResponse` and `willPresentNotification`. This is shown in the example app, [`AppDelegate.m`](./example/ios/Runner/AppDelegate.m). For more information, have a look at the [receiving messages](#receiving-messages) section. +- Handle messages received on your device by implementing the methods in your `AppDelegate`, such as `didReceiveRemoteNotification`, `didReceiveNotificationResponse` and `willPresentNotification`. This is shown in the example app, [`AppDelegate.swift`](./example/ios/Runner/AppDelegate.swift). For more information, have a look at the [receiving messages](#receiving-messages) section. + +#### UIScene life cycle + +Ably Flutter supports apps that have adopted the [UIScene life cycle](https://flutter.dev/to/uiscene-migration), which Apple will soon require, as well as apps still on the `UIApplicationDelegate` life cycle. No Ably-specific changes are needed to migrate: + +- Register plugins in `didInitializeImplicitFlutterEngine(_:)` as Flutter's migration guide describes. Ably Flutter registers for remote notifications once the scene connects, so the APNs device token still reaches it. +- When the user launches the app by tapping a notification, iOS passes the tap to the scene rather than in the app's launch options. Ably Flutter reads it from there, so `ably.Push.notificationEvents.notificationTapLaunchedAppFromTerminated` keeps working. +- The `UIApplicationDelegate` methods for remote notifications, such as `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` and `application(_:didFailToRegisterForRemoteNotificationsWithError:)`, stay on your `AppDelegate`; they are not scene methods. ## Usage @@ -93,11 +101,11 @@ The example app contains an example of how to use the Push Notification function ### Ensure `WidgetsBinding` is initialized -If you invoke any methods from the `ably_flutter` package before calling `runApp()`, you must call `WidgetsFlutterBinding.ensureInitialized();`. This is done to ensure all platform methods will be successfully received by the native Ably plugin on the host platform. +If you invoke any methods from the `ably_pubsub_device_flutter` package before calling `runApp()`, you must call `WidgetsFlutterBinding.ensureInitialized();`. This is done to ensure all platform methods will be successfully received by the native Ably plugin on the host platform. ### Device activation -- Create a rest or realtime client: e.g. `final realtime = ably.Realtime(options: clientOptions);` +- Create a realtime client: e.g. `final realtime = ably.createClient(options: clientOptions);` - Activate the device for push notifications with Ably: `ablyClient.push.activate();`. This only needs to be done once, and will be used across all future app launches, as long as the app is not deactivated. This method will throw an AblyException if it fails. - The `Future` returned by `activate` is not guaranteed to complete quickly. For example, if there is no internet connection, `activate` will wait until it is available. Therefore, there is no guarantee any code awaiting the completion of the `Future` will run. @@ -144,7 +152,7 @@ In this case, you should provide us with the device token manually in your `didR If you're working in Objective-C then this will look something like this in `AppDelegate.m`: ```objective-c -#import "AblyFlutter.h" +@import ably_pubsub_device_flutter; @implementation AppDelegate @@ -162,9 +170,9 @@ If you're working in Objective-C then this will look something like this in `App If you're working in Swift then this will look something like this in `AppDelegate.swift`: ```swift -import ably_flutter +import ably_pubsub_device_flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { AblyInstanceStore.sharedInstance().didRegisterForRemoteNotifications(withDeviceToken: deviceToken) @@ -178,7 +186,7 @@ import ably_flutter ### Subscribing to channels for push notifications -- Get the Realtime/ Rest channel: `final channel = realtime!.channels.get(Constants.channelNameForPushNotifications)` +- Get the realtime channel: `final channel = realtime!.channels.get(Constants.channelNameForPushNotifications)` - Subscribe the device to the **push channel**, by either using the device ID or client ID: - `channel.push.subscribeClient()` or `channel.push.subscribeDevice()` - This is different to subscribing to a channel for messages. @@ -238,7 +246,7 @@ Requesting permissions: ```dart // Create an Ably client to access the push field -final realtime = ably.Realtime(options: clientOptions); +final realtime = ably.createClient(options: clientOptions); final push = realtime.push; // Request permission from user on iOS bool permissionGranted = await push.requestPermission(); @@ -446,7 +454,7 @@ Then, in your Android Manifest, disable Ably Flutter's broadcast receiver by rem **iOS**: Implementing the [`didReceiveRemoteNotification` delegate method](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application) declared in `UIApplicationDelegate`. -Take a look at the example app platform specific code to handle messages. For iOS, this is `AppDelegate.m`, and in Android, it is `PushMessagingService.java`. For further help on implementing the Platform specific message handlers, see "On Android" and "On iOS" sections on [Push Notifications - Device activation and subscription](https://ably.com/documentation/general/push/activate-subscribe). +Take a look at the example app platform specific code to handle messages. For iOS, this is `AppDelegate.swift`, and in Android, it is `PushMessagingService.java`. For further help on implementing the Platform specific message handlers, see "On Android" and "On iOS" sections on [Push Notifications - Device activation and subscription](https://ably.com/documentation/general/push/activate-subscribe). ### Additional considerations and resources - For tips on how best to use push messaging on Android, read [Notifying your users with FCM](https://android-developers.googleblog.com/2018/09/notifying-your-users-with-fcm.html). For example: @@ -488,7 +496,7 @@ During development, you may want to validate or debug situations where your appl ### Activation and deactivation -The platform SDKs ([ably-android](https://github.com/ably/ably-java) and [ably-cocoa](https://github.com/ably/ably-cocoa)) enable users to check if device activation, deactivation, or registration update fails. On Android, these errors are sent in Intents which users should register for at runtime. In Cocoa, errors are returned through `ARTPushRegistererDelegate` methods. However, in both SDKs, this error does not always return quickly. For example, if there was no internet connection, then `Push.activate()` will not throw an error, it will just block forever, because errors are not provided by the SDKs. Once an internet connection is made, the Intent will be sent and delegate methods will be called. +The platform SDKs ([ably-pubsub-java](https://github.com/ably/ably-pubsub-java) and [ably-pubsub-cocoa](https://github.com/ably/ably-pubsub-cocoa)) enable users to check if device activation, deactivation, or registration update fails. On Android, these errors are sent in Intents which users should register for at runtime. In Cocoa, errors are returned through `ARTPushRegistererDelegate` methods. However, in both SDKs, this error does not always return quickly. For example, if there was no internet connection, then `Push.activate()` will not throw an error, it will just block forever, because errors are not provided by the SDKs. Once an internet connection is made, the Intent will be sent and delegate methods will be called. Ably Flutter does this by passing a reference to the `FlutterResult` used to pass back the result to the Dart side, when the activation or deactivation completes. This makes it convenient for users: they can `await push.activate()`. However, users should not rely on this Future completing, in the case of network issues. @@ -563,7 +571,7 @@ For Android device registrations, the device push state error are errors passed ### Why are notifications not shown to the user when the app is open on Android? -When the app is in the foreground (open by the user), Firebase messaging ignores the message. You would need to send a data message and build a local notification instead. On iOS, you can specify this in your `UNUserNotificationCenterDelegate`'s `userNotificationCenter:_willPresentNotification:withCompletionHandler` method. In the example app, this is implemented in `AppDelegate.m`, where the notification is always shown. You can perform logic to decide if it should be shown or not based on the notification. +When the app is in the foreground (open by the user), Firebase messaging ignores the message. You would need to send a data message and build a local notification instead. On iOS, you can specify this in your `UNUserNotificationCenterDelegate`'s `userNotificationCenter:_willPresentNotification:withCompletionHandler` method. In the example app, this is implemented in `AppDelegate.swift`, where the notification is always shown. You can perform logic to decide if it should be shown or not based on the notification. ### Messaging generated from the "compose notification" in Firebase cloud messaging console are not received. @@ -601,6 +609,6 @@ ActivationContext.getActivationContext(this).onNewRegistrationToken(Registration **iOS:** Ensure you set the `sound` key in the `push.notification` object when sending the Ably message. -### When building my application, I get the following error: `Manifest merger failed : uses-sdk:minSdkVersion 16 cannot be smaller than version 19 declared in library [:ably_flutter]` +### When building my application, I get the following error: `Manifest merger failed : uses-sdk:minSdkVersion 16 cannot be smaller than version 19 declared in library [:ably_pubsub_device_flutter]` A fresh Flutter project generates an Android project with a `minSdkVersion` of 16, but Ably-flutter only supports API level 19 (Android Kitkat 4.4, which was released in 2013) and above. In `android/app/build.gradle`, change `minSdkVersion 16` to `minSdkVersion 19`. diff --git a/README.md b/README.md index febbb2d81..0d2c31498 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![Ably Pub/Sub Flutter Header](images/flutter-github.png) -[![Pub Version](https://img.shields.io/pub/v/ably_flutter)](https://pub.dev/packages/ably_flutter) +[![Pub Version](https://img.shields.io/pub/v/ably_pubsub_device_flutter)](https://pub.dev/packages/ably_pubsub_device_flutter) [![License](https://badgen.net/github/license/ably/ably-pubsub-flutter)](https://github.com/ably/ably-pubsub-flutter/blob/main/LICENSE) --- @@ -34,12 +34,10 @@ This SDK supports the following platforms: | Platform | Support | |----------|---------| -| Android | Android 4.4 (API level 19) or newer. Java 8 language features supported via [Desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring). | -| iOS | iOS 10 or newer | -| Flutter | Flutter 2.5.0 or higher | +| Android | Android 7.0 (API level 24) or newer | +| iOS | iOS 15 or newer | +| Flutter | Flutter 3.44.0 or higher | -> [!NOTE] -> If your project needs support for SDK Version lower than 24, Android Gradle Plugin 4.0.0+ must be used. You might also need to upgrade [gradle distribution](https://developer.android.com/studio/releases/gradle-plugin#updating-plugin). > [!IMPORTANT] > SDK versions < 1.2.25 will be [deprecated](https://ably.com/docs/platform/deprecate/protocol-v1) from November 1, 2025. @@ -54,17 +52,17 @@ Add the Ably Flutter package to your project by including it in your `pubspec.ya ```yaml dependencies: - ably_flutter: ^1.2.44 + ably_pubsub_device_flutter: ^1.2.44 ``` Once added to your dependencies, import it in your Dart code: ```dart -import 'package:ably_flutter/ably_flutter.dart' as ably; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' as ably; ``` > [!NOTE] -> When increasing the version of `ably_flutter` in your `pubspec.yaml`, if there are breaking changes, follow the [updating / migration guide](UPDATING.md). +> When increasing the version of `ably_pubsub_device_flutter` in your `pubspec.yaml`, if there are breaking changes, follow the [updating / migration guide](UPDATING.md). --- @@ -73,16 +71,16 @@ import 'package:ably_flutter/ably_flutter.dart' as ably; The following code connects to Ably's realtime messaging service, subscribes to a channel to receive messages, and publishes a test message to that same channel: ```dart - // Initialize Ably Realtime client + // Initialize Ably Pub/Sub client final clientOptions = ably.ClientOptions( key: 'your-ably-api-key', clientId: 'me', ); - final realtimeClient = ably.Realtime(options: clientOptions); + final pubSubClient = ably.createClient(options: clientOptions); // Wait for connection to be established - await realtimeClient.connection + await pubSubClient.connection .on(ably.ConnectionEvent.connected) .first .then((stateChange) { @@ -90,7 +88,7 @@ The following code connects to Ably's realtime messaging service, subscribes to }); // Get a reference to the 'test-channel' channel - final channel = realtimeClient.channels.get('test-channel'); + final channel = pubSubClient.channels.get('test-channel'); // Subscribe to all messages published to this channel channel.subscribe().listen((ably.Message message) { diff --git a/UPDATING.md b/UPDATING.md index e28afd24c..9b69a35d9 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -2,6 +2,41 @@ This guide lists the changes needed to upgrade from one version of Ably to a newer one when there are breaking changes. +## [Upgrading from v1.2.x] + +- The package has been renamed from `ably_flutter` to `ably_pubsub_device_flutter`. Update the dependency in your `pubspec.yaml` and every import: + - `ably_flutter: ^1.2.x` becomes `ably_pubsub_device_flutter: ^2.0.0`. + - `import 'package:ably_flutter/ably_flutter.dart' as ably;` becomes `import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' as ably;`. + - On iOS, the pod and Swift module are renamed too, so an AppDelegate that did `import ably_flutter` now needs `import ably_pubsub_device_flutter`. +- On iOS, the plugin is now distributed as a Swift package rather than a CocoaPod, and requires Flutter 3.44 or later, where Swift Package Manager is enabled by default. + - If your app has turned Swift Package Manager off (`enable-swift-package-manager: false` in `pubspec.yaml`, or `flutter config --no-enable-swift-package-manager`), turn it back on: the plugin no longer ships a podspec, so CocoaPods cannot install it. Other plugins that only support CocoaPods keep working alongside it. + - The first iOS build after upgrading migrates your Xcode project to Swift Package Manager automatically. Afterwards, `pod install` no longer installs `ably_pubsub_device_flutter` or `Ably`, and if no other plugin needs CocoaPods you can remove your `Podfile`. + - If you pinned `Ably` in your `Podfile`, remove that pin: the plugin now resolves `ably-cocoa` through Swift Package Manager, and a second copy from CocoaPods would conflict with it. +- The plugin is now built on the v2 native SDKs, [ably-pubsub-cocoa](https://github.com/ably/ably-pubsub-cocoa) 2.0 on iOS and [ably-pubsub-java](https://github.com/ably/ably-pubsub-java) 2.0 (`io.ably.pubsub:device`) on Android. This raises the platform floors and changes how clients reach and identify themselves to Ably: + - The minimum iOS version is now iOS 15, and building requires Xcode 26 or later. The minimum Android version is now API level 24 (Android 7.0); set `minSdkVersion` to at least 24 in your app's `android/app/build.gradle`. + - `ClientOptions.environment`, `ClientOptions.restHost` and `ClientOptions.realtimeHost` are removed. Use `ClientOptions.endpoint`, which selects one host for both REST requests and the realtime connection: + - Replace `environment: 'sandbox'` with `endpoint: 'nonprod:sandbox'`, and any other `environment: 'acme'` with `endpoint: 'acme'`. + - Replace `restHost` / `realtimeHost` with `endpoint` set to that host. Setting the two to different hosts is no longer supported. Don't put a port in `endpoint`; set `port` or `tlsPort` instead. A hostname `endpoint` has no default fallback hosts, so pass `fallbackHosts` if you need them. + - Clients now connect to `main.realtime.ably.net` by default, with fallbacks at `main.[a-e].fallback.ably-realtime.com`, instead of `rest.ably.io`, `realtime.ably.io` and `[a-e].ably-realtime.com`. If your network restricts outbound traffic by hostname, allow `*.realtime.ably.net` and `*.fallback.ably-realtime.com`. + - Every client declares to Ably that it runs on an end user's device. On an account billed by monthly active users, device traffic counts toward the total, and the connection must carry a `clientId`, set in `ClientOptions.clientId` or carried by the token. Authenticate devices with tokens from your own server (`authUrl` or `authCallback`) rather than embedding an API key in the app. + - Clients identify themselves in the `Ably-Agent` header and the realtime `agent` parameter as `ably-pubsub-cocoa` or `ably-pubsub-java` with an `ably-pubsub-device` entry, where they sent `ably-cocoa` or `ably-java`. Update anything that matches on the old names, such as proxy rules or log queries. + - `PushChannel.listSubscriptions()` now requires exactly one of `deviceId` or `clientId` in its params, because ably-pubsub-cocoa only supports filtering by one of them. Params with neither, with both, or with only `channel` or `deviceClientId` now throw an `AblyException` on both platforms. + - On iOS, if your AppDelegate imports the native SDK directly, `import Ably` becomes `import AblyPubSubDeviceSwift` (or `@import AblyPubSubDevice;` in Objective-C), and Swift sees its types without the `ART` prefix. On Android, native code that uses the SDK directly moves from `io.ably.lib.*` to `io.ably.pubsub.*`. +- The `Realtime` client has been renamed to `PubSubClient`, and it is now created with the top-level `ably.createClient()` function rather than a constructor. `PubSubClient` no longer has a public constructor, and `Realtime.fromKey(key)` and the `key` parameter have both been removed; pass the key through `ClientOptions` instead. + - Replace `ably.Realtime(options: clientOptions)` with `ably.createClient(options: clientOptions)`. + - Replace `ably.Realtime.fromKey(key)` / `ably.Realtime(key: key)` with `ably.createClient(options: ably.ClientOptions(key: key))`. +- All previously deprecated API has been removed: + - `Connection.recoveryKey`. Use `Connection.createRecoveryKey()`. + - `ClientOptions.logHandler` and the `LogHandler` typedef. Log output is handled by the underlying native SDK; use `ClientOptions.logLevel` to control verbosity. + - `ClientOptions.fallbackHostsUseDefault`. There is no replacement; default fallback hosts are used automatically unless `ClientOptions.fallbackHosts` is set. + - `ClientOptions.fromKey(key)` and `AuthOptions.fromKey(key)`. Use the constructors' named `key` parameter instead. +- The REST client has been removed. Use `PubSubClient` instead: + - Replace `ably.Rest(options: clientOptions)` / `ably.Rest.fromKey(key)` with `ably.createClient(options: clientOptions)` / `ably.createClient(options: ably.ClientOptions(key: key))`, and use `channels`, `auth`, `push` and `time()` on the `PubSubClient` in place of their `Rest` equivalents. + - `RestChannel`, `RestChannels`, `RestPresence`, `RestChannelOptions`, `RestHistoryParams` and `RestPresenceParams` have been removed. Use `RealtimeChannel`, `RealtimeChannels`, `RealtimePresence`, `RealtimeChannelOptions`, `RealtimeHistoryParams` and `RealtimePresenceParams`. + - `Message.fromEncoded`, `Message.fromEncodedArray`, `PresenceMessage.fromEncoded` and `PresenceMessage.fromEncodedArray` now take a `RealtimeChannelOptions` instead of a `RestChannelOptions`. + - `Push` and `PushChannel` no longer accept a `rest` client; they now require a `realtime` client. +- `RealtimeChannels.release()` now returns a `Future`, which completes once the channel has been released. A realtime channel can only be released when it is in the `INITIALIZED`, `DETACHED`, or `FAILED` state. Releasing a channel in any other state no longer detaches it implicitly. Instead, the future completes with an `AblyException` with code 90011 and the channel is not released. To release an attached channel, `await channel.detach()` first and then `await realtime.channels.release(name)`. + ## [Upgrading from v1.2.13] - Updated SDK constraint to `>=2.14.0 <3.0.0` diff --git a/analysis_options.yaml b/analysis_options.yaml index f815b7078..91aa6cefb 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,6 +4,9 @@ analyzer: # Templates for code generation can be excluded from analysis exclude: - bin/templates/** + - build/** + - android/** + - ios/** language: # Replacements for deprecated implicit-casts: false # Following https://dart.dev/guides/language/analysis-options#enabling-additional-type-checks @@ -18,6 +21,11 @@ analyzer: # allow self-reference to deprecated members # suppresses issues for deprecated members of the library like ClientOptions.fallbackHostsUseDefault deprecated_member_use_from_same_package: ignore + # Dart 3.13's analyzer extended this rule to private fields initialised from + # a named parameter, but the fix it suggests -- `this._field` in the + # parameter list -- is only legal under the off-by-default + # `private-named-parameters` experiment, so the reports are unactionable. + prefer_initializing_formals: ignore linter: # subject to false positives, perhaps worth suppressing in code in such cases @@ -37,7 +45,6 @@ linter: - avoid_implementing_value_types - avoid_init_to_null - avoid_js_rounded_ints - - avoid_null_checks_in_equality_operators - avoid_positional_boolean_parameters - avoid_redundant_argument_values - avoid_relative_lib_imports @@ -73,7 +80,6 @@ linter: - no_runtimeType_toString - omit_local_variable_types - only_throw_errors - - package_api_docs - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists - prefer_expression_function_bodies diff --git a/android/build.gradle b/android/build.gradle index 934d1be70..cf18d330e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -24,8 +24,8 @@ rootProject.allprojects { apply plugin: 'com.android.library' dependencies { - // https://github.com/ably/ably-java/ - implementation 'io.ably:ably-android:1.6.0' + // https://github.com/ably/ably-pubsub-java (io.ably.pubsub:device is the end-user-device artifact) + implementation 'io.ably.pubsub:device:2.0.0' // https://firebase.google.com/docs/cloud-messaging/android/client implementation 'com.google.firebase:firebase-messaging:23.0.6' @@ -46,7 +46,7 @@ android { } defaultConfig { - minSdkVersion 19 + minSdkVersion 24 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { diff --git a/android/settings.gradle b/android/settings.gradle index c18abf25e..8ae9b8f60 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1 @@ -rootProject.name = 'ably_flutter' +rootProject.name = 'ably_pubsub_device_flutter' diff --git a/android/src/main/java/io/ably/flutter/plugin/AblyEventStreamHandler.java b/android/src/main/java/io/ably/flutter/plugin/AblyEventStreamHandler.java index 75c3ef97c..4de466213 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyEventStreamHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyEventStreamHandler.java @@ -7,15 +7,15 @@ import io.ably.flutter.plugin.dto.EnrichedConnectionStateChange; import io.ably.flutter.plugin.generated.PlatformConstants; -import io.ably.lib.realtime.AblyRealtime; -import io.ably.lib.realtime.Channel; -import io.ably.lib.realtime.ChannelStateListener; -import io.ably.lib.realtime.ConnectionStateListener; -import io.ably.lib.realtime.Presence; -import io.ably.lib.types.AblyException; -import io.ably.lib.types.Message; -import io.ably.lib.types.PresenceMessage; -import io.ably.lib.util.Log; +import io.ably.pubsub.realtime.PubSubRealtimeClient; +import io.ably.pubsub.realtime.Channel; +import io.ably.pubsub.realtime.ChannelStateListener; +import io.ably.pubsub.realtime.ConnectionStateListener; +import io.ably.pubsub.realtime.Presence; +import io.ably.pubsub.types.AblyException; +import io.ably.pubsub.types.Message; +import io.ably.pubsub.types.PresenceMessage; +import io.ably.pubsub.util.Log; import io.flutter.plugin.common.EventChannel; @@ -78,9 +78,9 @@ static private class Listener { static private class PluginConnectionStateListener extends Listener implements ConnectionStateListener { - private final AblyRealtime realtime; + private final PubSubRealtimeClient realtime; - PluginConnectionStateListener(EventChannel.EventSink eventSink, AblyRealtime realtime) { + PluginConnectionStateListener(EventChannel.EventSink eventSink, PubSubRealtimeClient realtime) { super(eventSink); this.realtime = realtime; } @@ -137,7 +137,7 @@ public void onListen(Object object, EventChannel.EventSink uiThreadEventSink) { try { switch (eventName) { case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged: - final AblyRealtime realtime = instanceStore.getRealtime(ablyMessage.handle); + final PubSubRealtimeClient realtime = instanceStore.getRealtime(ablyMessage.handle); connectionStateListener = new PluginConnectionStateListener(eventSink, realtime); realtime.connection.on(connectionStateListener); break; diff --git a/android/src/main/java/io/ably/flutter/plugin/AblyFlutter.java b/android/src/main/java/io/ably/flutter/plugin/AblyFlutter.java index 8d9f0ceed..f35c62ac9 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyFlutter.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyFlutter.java @@ -14,7 +14,7 @@ import io.ably.flutter.plugin.push.PushActivationEventHandlers; import io.ably.flutter.plugin.push.PushMessagingEventHandlers; import io.ably.flutter.plugin.util.CipherParamsStorage; -import io.ably.lib.util.Log; +import io.ably.pubsub.util.Log; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; diff --git a/android/src/main/java/io/ably/flutter/plugin/AblyInstanceStore.java b/android/src/main/java/io/ably/flutter/plugin/AblyInstanceStore.java index d3040f3b6..4c244e471 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyInstanceStore.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyInstanceStore.java @@ -5,14 +5,13 @@ import java.util.concurrent.atomic.AtomicLong; -import io.ably.lib.push.Push; -import io.ably.lib.push.PushChannel; -import io.ably.lib.realtime.AblyRealtime; -import io.ably.lib.rest.AblyBase; -import io.ably.lib.rest.AblyRest; -import io.ably.lib.types.AblyException; -import io.ably.lib.types.AsyncPaginatedResult; -import io.ably.lib.types.ClientOptions; +import io.ably.pubsub.device.PubSubDevice; +import io.ably.pubsub.push.Push; +import io.ably.pubsub.push.PushChannel; +import io.ably.pubsub.realtime.PubSubRealtimeClient; +import io.ably.pubsub.types.AblyException; +import io.ably.pubsub.types.AsyncPaginatedResult; +import io.ably.pubsub.types.ClientOptions; /** * Stores instances created by Ably Java, by handle. This handle is passed to the Dart side @@ -31,8 +30,7 @@ class AblyInstanceStore { // suggests using LongSparseArray. More information at https://stackoverflow.com/a/31413003 // It may be simpler to go back to HashMap because this is an unmeasured memory optimisation. // > the Hashmap and the SparseArray are very similar for data structure sizes under 1,000 - private final LongSparseArray restInstances = new LongSparseArray<>(); - private final LongSparseArray realtimeInstances = new LongSparseArray<>(); + private final LongSparseArray realtimeInstances = new LongSparseArray<>(); private final LongSparseArray> paginatedResults = new LongSparseArray<>(); private final AtomicLong nextHandle = new AtomicLong(1); @@ -43,8 +41,8 @@ static synchronized AblyInstanceStore getInstance() { /** * A reserved client handle. Safe to be used from any thread. * - * Instances support the creation of a single Rest or Realtime instance, where only one of the - * create methods may be called and it may only be called once. + * Instances support the creation of a single Realtime instance, where the create method may + * only be called once. */ interface ClientHandle { /** @@ -55,26 +53,14 @@ interface ClientHandle { long getHandle(); /** - * Create an {@link AblyRest} instance and store it using this handle. - * @param clientOptions The Ably client options for the new Rest instance. - * @param applicationContext The Android application context to supply to the new Rest - * instance using its {@link AblyRest#setAndroidContext(Context)} method. - * @return The handle used to store the instance. Same as {@link #getHandle()}. - * @throws IllegalStateException If this handle has already been used to create a Rest or - * Realtime instance. - * @throws AblyException If the {@link AblyRest} instance creation failed. - */ - long createRest(ClientOptions clientOptions, Context applicationContext) throws AblyException; - - /** - * Create an {@link AblyRealtime} instance and store it using this handle. + * Create an {@link PubSubRealtimeClient} instance and store it using this handle. * @param clientOptions The Ably client options for the new Realtime instance. * @param applicationContext The Android application context to supply to the new Realtime - * instance using its {@link AblyRealtime#setAndroidContext(Context)} method. + * instance using its {@link PubSubRealtimeClient#setAndroidContext(Context)} method. * @return The handle used to store the instance. Same as {@link #getHandle()}. - * @throws IllegalStateException If this handle has already been used to create a Rest or + * @throws IllegalStateException If this handle has already been used to create a * Realtime instance. - * @throws AblyException If the {@link AblyRealtime} instance creation failed. + * @throws AblyException If the {@link PubSubRealtimeClient} instance creation failed. */ long createRealtime(ClientOptions clientOptions, Context applicationContext) throws AblyException; } @@ -92,19 +78,12 @@ public long getHandle() { return handle; } - @Override - public synchronized long createRest(final ClientOptions clientOptions, final Context applicationContext) throws AblyException { - final long handle = use(); - final AblyRest rest = new AblyRest(clientOptions); - rest.setAndroidContext(applicationContext); - restInstances.put(handle, rest); - return handle; - } - @Override public synchronized long createRealtime(final ClientOptions clientOptions, final Context applicationContext) throws AblyException { final long handle = use(); - final AblyRealtime realtime = new AblyRealtime(clientOptions); + // The device builder copies the options and marks the client as running on an + // end-user device (the `ably-pubsub-device` agent). + final PubSubRealtimeClient realtime = PubSubDevice.clientBuilder(clientOptions).build(); realtime.setAndroidContext(applicationContext); realtimeInstances.put(handle, realtime); return handle; @@ -123,36 +102,16 @@ synchronized ClientHandle reserveClientHandle() { return new ReservedClientHandle(nextHandle.getAndIncrement()); } - synchronized AblyRest getRest(final long handle) { - return restInstances.get(handle); - } - - synchronized AblyRealtime getRealtime(final long handle) { + synchronized PubSubRealtimeClient getRealtime(final long handle) { return realtimeInstances.get(handle); } - /** - * Gets the Ably client (either REST or Realtime) when the interface being - * used is the same (e.g. When using Push from AblyBase / when it does - * not matter). - * - * This method relies on the fact handles are unique between all Ably clients, - * (both rest and realtime). - * @param handle integer handle to either AblyRealtime or AblyRest - * @return AblyBase - */ - synchronized AblyBase getAblyClient(final long handle) { - AblyRealtime realtime = getRealtime(handle); - return (realtime != null) ? realtime : getRest(handle); - } - synchronized Push getPush(final long handle) { - AblyRealtime realtime = getRealtime(handle); - return (realtime != null) ? realtime.push : getRest(handle).push; + return getRealtime(handle).push; } - + synchronized PushChannel getPushChannel(final long handle, final String channelName) { - return getAblyClient(handle) + return getRealtime(handle) .channels .get(channelName).push; } @@ -175,7 +134,7 @@ synchronized AsyncPaginatedResult getPaginatedResult(long handle) { synchronized void reset() { for (int i = 0; i < realtimeInstances.size(); i++) { long key = realtimeInstances.keyAt(i); - AblyRealtime r = realtimeInstances.get(key); + PubSubRealtimeClient r = realtimeInstances.get(key); try { r.close(); } catch (Throwable t) { @@ -183,7 +142,6 @@ synchronized void reset() { } } realtimeInstances.clear(); - restInstances.clear(); paginatedResults.clear(); } } diff --git a/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java b/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java index e87e0e890..7a3f6cbff 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java @@ -23,29 +23,29 @@ import io.ably.flutter.plugin.types.PlatformClientOptions; import io.ably.flutter.plugin.util.CipherParamsStorage; import io.ably.flutter.plugin.util.Consumer; -import io.ably.lib.push.LocalDevice; -import io.ably.lib.push.Push; -import io.ably.lib.push.PushBase; -import io.ably.lib.realtime.ChannelEvent; -import io.ably.lib.realtime.ChannelState; -import io.ably.lib.realtime.ChannelStateListener; -import io.ably.lib.realtime.ConnectionEvent; -import io.ably.lib.realtime.ConnectionState; -import io.ably.lib.rest.Auth; -import io.ably.lib.rest.Auth.TokenDetails; -import io.ably.lib.rest.DeviceDetails; -import io.ably.lib.types.AsyncPaginatedResult; -import io.ably.lib.types.ChannelMode; -import io.ably.lib.types.ChannelOptions; -import io.ably.lib.types.ClientOptions; -import io.ably.lib.types.DeltaExtras; -import io.ably.lib.types.ErrorInfo; -import io.ably.lib.types.Message; -import io.ably.lib.types.MessageExtras; -import io.ably.lib.types.Param; -import io.ably.lib.types.PresenceMessage; -import io.ably.lib.util.Crypto; -import io.ably.lib.util.Log; +import io.ably.pubsub.push.LocalDevice; +import io.ably.pubsub.push.Push; +import io.ably.pubsub.push.PushBase; +import io.ably.pubsub.realtime.ChannelEvent; +import io.ably.pubsub.realtime.ChannelState; +import io.ably.pubsub.realtime.ChannelStateListener; +import io.ably.pubsub.realtime.ConnectionEvent; +import io.ably.pubsub.realtime.ConnectionState; +import io.ably.pubsub.http.Auth; +import io.ably.pubsub.http.Auth.TokenDetails; +import io.ably.pubsub.http.DeviceDetails; +import io.ably.pubsub.types.AsyncPaginatedResult; +import io.ably.pubsub.types.ChannelMode; +import io.ably.pubsub.types.ChannelOptions; +import io.ably.pubsub.types.ClientOptions; +import io.ably.pubsub.types.DeltaExtras; +import io.ably.pubsub.types.ErrorInfo; +import io.ably.pubsub.types.Message; +import io.ably.pubsub.types.MessageExtras; +import io.ably.pubsub.types.Param; +import io.ably.pubsub.types.PresenceMessage; +import io.ably.pubsub.util.Crypto; +import io.ably.pubsub.util.Log; import io.flutter.plugin.common.StandardMessageCodec; public class AblyMessageCodec extends StandardMessageCodec { @@ -110,18 +110,12 @@ public AblyMessageCodec(CipherParamsStorage cipherParamsStorage) { new CodecPair<>(self::encodeTokenDetails, self::decodeTokenDetails)); put(PlatformConstants.CodecTypes.tokenRequest, new CodecPair<>(self::encodeTokenRequest, self::decodeTokenRequest)); - put(PlatformConstants.CodecTypes.restChannelOptions, - new CodecPair<>(null, self::decodeRestChannelOptions)); put(PlatformConstants.CodecTypes.realtimeChannelOptions, new CodecPair<>(null, self::decodeRealtimeChannelOptions)); put(PlatformConstants.CodecTypes.paginatedResult, new CodecPair<>(self::encodePaginatedResult, null)); - put(PlatformConstants.CodecTypes.restHistoryParams, - new CodecPair<>(null, self::decodeRestHistoryParams)); put(PlatformConstants.CodecTypes.realtimeHistoryParams, new CodecPair<>(null, self::decodeRealtimeHistoryParams)); - put(PlatformConstants.CodecTypes.restPresenceParams, - new CodecPair<>(null, self::decodeRestPresenceParams)); put(PlatformConstants.CodecTypes.realtimePresenceParams, new CodecPair<>(null, self::decodeRealtimePresenceParams)); put(PlatformConstants.CodecTypes.errorInfo, @@ -207,7 +201,6 @@ private Byte getType(Object value) { } else if (value instanceof Crypto.CipherParams) { return PlatformConstants.CodecTypes.cipherParams; } else if (value instanceof ChannelOptions) { - // Encoding it into a RealtimeChannelOptions instance, because it extends RestChannelOptions return PlatformConstants.CodecTypes.realtimeChannelOptions; } else if (value instanceof TokenDetails) { return PlatformConstants.CodecTypes.tokenDetails; @@ -337,8 +330,7 @@ private PlatformClientOptions decodeClientOptions(Map jsonMap) { readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.clientId, v -> clientOptions.clientId = (String) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.logLevel, v -> clientOptions.logLevel = decodeLogLevel((String) v)); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.tls, v -> clientOptions.tls = (Boolean) v); - readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.restHost, v -> clientOptions.restHost = (String) v); - readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.realtimeHost, v -> clientOptions.realtimeHost = (String) v); + readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.endpoint, v -> clientOptions.endpoint = (String) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.port, v -> clientOptions.port = (Integer) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.tlsPort, v -> clientOptions.tlsPort = (Integer) v); clientOptions.autoConnect = false; // Always avoid auto-connect, to allow handle to be returned back to Dart side before authCallback is called. @@ -348,14 +340,12 @@ private PlatformClientOptions decodeClientOptions(Map jsonMap) { readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.queueMessages, v -> clientOptions.queueMessages = (Boolean) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.echoMessages, v -> clientOptions.echoMessages = (Boolean) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.recover, v -> clientOptions.recover = (String) v); - readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.environment, v -> clientOptions.environment = (String) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.idempotentRestPublishing, v -> clientOptions.idempotentRestPublishing = (Boolean) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.httpOpenTimeout, v -> clientOptions.httpOpenTimeout = (Integer) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.httpRequestTimeout, v -> clientOptions.httpRequestTimeout = (Integer) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.httpMaxRetryCount, v -> clientOptions.httpMaxRetryCount = (Integer) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.realtimeRequestTimeout, v -> clientOptions.realtimeRequestTimeout = readValueAsLong(v)); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.fallbackHosts, v -> clientOptions.fallbackHosts = Arrays.copyOf(((ArrayList)v).toArray(), ((ArrayList)v).size(), String[].class)); - readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.fallbackHostsUseDefault, v -> clientOptions.fallbackHostsUseDefault = (Boolean) v); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.fallbackRetryTimeout, v -> clientOptions.fallbackRetryTimeout = readValueAsLong(v)); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.defaultTokenParams, v -> clientOptions.defaultTokenParams = decodeTokenParams((Map) v)); readValueFromJson(jsonMap, PlatformConstants.TxClientOptions.disconnectedRetryTimeout, v -> clientOptions.disconnectedRetryTimeout = (Integer) v); @@ -454,16 +444,6 @@ private Auth.TokenRequest decodeTokenRequest(Map jsonMap) { return o; } - private ChannelOptions decodeRestChannelOptions(Map jsonMap) { - if (jsonMap == null) return null; - ChannelOptions options = new ChannelOptions(); - options.cipherParams = decodeCipherParams((Map) jsonMap.get(PlatformConstants.TxRestChannelOptions.cipherParams)); - if (options.cipherParams != null) { - options.encrypted = true; - } - return options; - } - private ChannelOptions decodeRealtimeChannelOptions(Map jsonMap) { if (jsonMap == null) return null; ChannelOptions options = new ChannelOptions(); @@ -520,29 +500,6 @@ private ChannelMode decodeChannelOptionsMode(String mode) { } } - private Param[] decodeRestHistoryParams(Map jsonMap) { - if (jsonMap == null) return null; - Param[] params = new Param[jsonMap.size()]; - int index = 0; - final Object start = jsonMap.get(PlatformConstants.TxRestHistoryParams.start); - final Object end = jsonMap.get(PlatformConstants.TxRestHistoryParams.end); - final Object limit = jsonMap.get(PlatformConstants.TxRestHistoryParams.limit); - final Object direction = jsonMap.get(PlatformConstants.TxRestHistoryParams.direction); - if (start != null) { - params[index++] = new Param(PlatformConstants.TxRestHistoryParams.start, readValueAsLong(start)); - } - if (end != null) { - params[index++] = new Param(PlatformConstants.TxRestHistoryParams.end, readValueAsLong(end)); - } - if (limit != null) { - params[index++] = new Param(PlatformConstants.TxRestHistoryParams.limit, (Integer) limit); - } - if (direction != null) { - params[index] = new Param(PlatformConstants.TxRestHistoryParams.direction, (String) direction); - } - return params; - } - private Param[] decodeRealtimeHistoryParams(Map jsonMap) { if (jsonMap == null) return null; Param[] params = new Param[jsonMap.size()]; @@ -570,25 +527,6 @@ private Param[] decodeRealtimeHistoryParams(Map jsonMap) { return params; } - private Param[] decodeRestPresenceParams(Map jsonMap) { - if (jsonMap == null) return null; - Param[] params = new Param[jsonMap.size()]; - int index = 0; - final Object limit = jsonMap.get(PlatformConstants.TxRestPresenceParams.limit); - final Object clientId = jsonMap.get(PlatformConstants.TxRestPresenceParams.clientId); - final Object connectionId = jsonMap.get(PlatformConstants.TxRestPresenceParams.connectionId); - if (limit != null) { - params[index++] = new Param(PlatformConstants.TxRestPresenceParams.limit, (Integer) limit); - } - if (clientId != null) { - params[index++] = new Param(PlatformConstants.TxRestPresenceParams.clientId, (String) clientId); - } - if (connectionId != null) { - params[index] = new Param(PlatformConstants.TxRestPresenceParams.connectionId, (String) connectionId); - } - return params; - } - private Param[] decodeRealtimePresenceParams(Map jsonMap) { if (jsonMap == null) return null; Param[] params = new Param[jsonMap.size()]; diff --git a/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java b/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java index 368e8e1dc..211ef41bf 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java @@ -20,24 +20,24 @@ import io.ably.flutter.plugin.push.PushActivationEventHandlers; import io.ably.flutter.plugin.types.PlatformClientOptions; import io.ably.flutter.plugin.util.BiConsumer; -import io.ably.lib.realtime.AblyRealtime; -import io.ably.lib.realtime.Channel; -import io.ably.lib.realtime.CompletionListener; -import io.ably.lib.realtime.Presence; -import io.ably.lib.rest.AblyBase; -import io.ably.lib.rest.AblyRest; -import io.ably.lib.rest.Auth; -import io.ably.lib.transport.Defaults; -import io.ably.lib.types.AblyException; -import io.ably.lib.types.AsyncPaginatedResult; -import io.ably.lib.types.Callback; -import io.ably.lib.types.ChannelOptions; -import io.ably.lib.types.ErrorInfo; -import io.ably.lib.types.Message; -import io.ably.lib.types.Param; -import io.ably.lib.util.Base64Coder; -import io.ably.lib.util.Crypto; -import io.ably.lib.util.Log; +import io.ably.pubsub.realtime.PubSubRealtimeClient; +import io.ably.pubsub.realtime.Channel; +import io.ably.pubsub.realtime.CompletionListener; +import io.ably.pubsub.realtime.Presence; +import io.ably.pubsub.http.PubSubBase; +import io.ably.pubsub.http.Auth; +import io.ably.pubsub.transport.Defaults; +import io.ably.pubsub.types.AblyException; +import io.ably.pubsub.types.AsyncPaginatedResult; +import io.ably.pubsub.types.Callback; +import io.ably.pubsub.types.ChannelOptions; +import io.ably.pubsub.types.ErrorInfo; +import io.ably.pubsub.types.PublishResult; +import io.ably.pubsub.types.Message; +import io.ably.pubsub.types.Param; +import io.ably.pubsub.util.Base64Coder; +import io.ably.pubsub.util.Crypto; +import io.ably.pubsub.util.Log; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -65,15 +65,6 @@ public AblyMethodCallHandler(final MethodChannel methodChannel, _map.put(PlatformConstants.PlatformMethod.getVersion, this::getVersion); _map.put(PlatformConstants.PlatformMethod.resetAblyClients, this::resetAblyClients); - // Rest - _map.put(PlatformConstants.PlatformMethod.createRest, this::createRest); - _map.put(PlatformConstants.PlatformMethod.setRestChannelOptions, this::setRestChannelOptions); - _map.put(PlatformConstants.PlatformMethod.publish, this::publishRestMessage); - _map.put(PlatformConstants.PlatformMethod.restHistory, this::getRestHistory); - _map.put(PlatformConstants.PlatformMethod.restPresenceGet, this::getRestPresence); - _map.put(PlatformConstants.PlatformMethod.restPresenceHistory, this::getRestPresenceHistory); - _map.put(PlatformConstants.PlatformMethod.releaseRestChannel, this::releaseRestChannel); - //Realtime _map.put(PlatformConstants.PlatformMethod.createRealtime, this::createRealtime); _map.put(PlatformConstants.PlatformMethod.connectRealtime, this::connectRealtime); @@ -90,27 +81,12 @@ public AblyMethodCallHandler(final MethodChannel methodChannel, _map.put(PlatformConstants.PlatformMethod.realtimePresenceLeave, this::leaveRealtimePresence); _map.put(PlatformConstants.PlatformMethod.releaseRealtimeChannel, this::releaseRealtimeChannel); _map.put(PlatformConstants.PlatformMethod.realtimeTime, this::realtimeTime); - _map.put(PlatformConstants.PlatformMethod.restTime, this::restTime); //authorizations - _map.put(PlatformConstants.PlatformMethod.realtimeAuthAuthorize, - (methodCall, result) -> authMethodHandler.authorize(methodCall, result, AuthMethodHandler.Type.Realtime)); - _map.put(PlatformConstants.PlatformMethod.realtimeAuthRequestToken, - (methodCall, result) -> authMethodHandler.requestToken(methodCall, result, AuthMethodHandler.Type.Realtime)); - _map.put(PlatformConstants.PlatformMethod.realtimeAuthCreateTokenRequest, - (methodCall, result) -> authMethodHandler.createTokenRequest(methodCall, result, AuthMethodHandler.Type.Realtime)); - _map.put(PlatformConstants.PlatformMethod.realtimeAuthGetClientId, - (methodCall, result) -> authMethodHandler.clientId(methodCall, result, AuthMethodHandler.Type.Realtime)); - - _map.put(PlatformConstants.PlatformMethod.restAuthAuthorize, - (methodCall, result) -> authMethodHandler.authorize(methodCall, result, AuthMethodHandler.Type.Rest)); - _map.put(PlatformConstants.PlatformMethod.restAuthRequestToken, - (methodCall, result) -> authMethodHandler.requestToken(methodCall, result, AuthMethodHandler.Type.Rest)); - _map.put(PlatformConstants.PlatformMethod.restAuthCreateTokenRequest, - (methodCall, result) -> authMethodHandler.createTokenRequest(methodCall, result, - AuthMethodHandler.Type.Rest)); - _map.put(PlatformConstants.PlatformMethod.restAuthGetClientId, - (methodCall, result) -> authMethodHandler.clientId(methodCall, result, AuthMethodHandler.Type.Rest)); + _map.put(PlatformConstants.PlatformMethod.realtimeAuthAuthorize, authMethodHandler::authorize); + _map.put(PlatformConstants.PlatformMethod.realtimeAuthRequestToken, authMethodHandler::requestToken); + _map.put(PlatformConstants.PlatformMethod.realtimeAuthCreateTokenRequest, authMethodHandler::createTokenRequest); + _map.put(PlatformConstants.PlatformMethod.realtimeAuthGetClientId, authMethodHandler::clientId); // Connection specific handlers _map.put(PlatformConstants.PlatformMethod.connectionRecoveryKey, this::connectionRecoveryKey); @@ -172,94 +148,28 @@ public void onError(ErrorInfo reason) { }; } - private void resetAblyClients(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - streamsChannel.reset(); - instanceStore.reset(); - result.success(null); - } - - private void createRest(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; - final PlatformClientOptions clientOptions = (PlatformClientOptions) ablyMessage.message.get(PlatformConstants.TxTransportKeys.options); - try { - final AblyInstanceStore.ClientHandle clientHandle = instanceStore.reserveClientHandle(); - if (clientOptions.hasAuthCallback) { - clientOptions.options.authCallback = (Auth.TokenParams params) -> { - final Object[] token = {null}; - final CountDownLatch latch = new CountDownLatch(1); - new Handler(Looper.getMainLooper()).post(() -> { - AblyFlutterMessage channelMessage = new AblyFlutterMessage<>(params, clientHandle.getHandle()); - methodChannel.invokeMethod(PlatformConstants.PlatformMethod.authCallback, channelMessage, new MethodChannel.Result() { - @Override - public void success(@Nullable Object result) { - token[0] = result; - latch.countDown(); - } - - @Override - public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) { - Log.w(TAG, String.format("\"%s\" platform method received error from Dart side: %s", PlatformConstants.PlatformMethod.authCallback, errorMessage)); - latch.countDown(); - } - - @Override - public void notImplemented() { - Log.w(TAG, String.format("\"%s\" platform method not implemented on Dart side: %s", PlatformConstants.PlatformMethod.authCallback)); - latch.countDown(); - } - }); - }); - - try { - latch.await(); - } catch (InterruptedException e) { - throw AblyException.fromErrorInfo(e, new ErrorInfo("Exception while waiting for authCallback to return", 400, 40000)); - } - - return token[0]; - }; + /** + * Completes a publish. The Dart API does not expose the published messages' serials, so the + * {@link PublishResult} is dropped and the call completes with no value, as it did before + * ably-java 2.0 removed the {@link CompletionListener} publish overloads. + */ + private Callback handlePublishResult(@NonNull MethodChannel.Result result) { + return new Callback() { + @Override + public void onSuccess(PublishResult publishResult) { + result.success(null); } - result.success(clientHandle.createRest(clientOptions.options, applicationContext)); - } catch (final AblyException e) { - handleAblyException(result, e); - } - } - - private void setRestChannelOptions( - @NonNull MethodCall call, @NonNull MethodChannel.Result result - ) { - // setOptions is not supported on a rest instance directly - // Track @ https://github.com/ably/ably-flutter/issues/14 - // An alternative is to use the side effect of get channel - // with options which updates passed channel options. - final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; - final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); - final ChannelOptions channelOptions = (ChannelOptions) ablyMessage.message.get(PlatformConstants.TxTransportKeys.options); - try { - instanceStore.getRest(ablyMessage.handle).channels.get(channelName, channelOptions); - result.success(null); - } catch (AblyException ae) { - handleAblyException(result, ae); - } - } - private void publishRestMessage(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; - final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); - final ArrayList channelMessages = (ArrayList) ablyMessage.message.get(PlatformConstants.TxTransportKeys.messages); - if (channelMessages == null) { - result.error("Messages cannot be null", null, null); - return; - } - Message[] messages = new Message[channelMessages.size()]; - messages = channelMessages.toArray(messages); - instanceStore.getRest(ablyMessage.handle).channels.get(channelName).publishAsync(messages, handleCompletionWithListener(result)); + @Override + public void onError(ErrorInfo reason) { + handleAblyException(result, AblyException.fromErrorInfo(reason)); + } + }; } - private void releaseRestChannel(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; - final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); - instanceStore.getRest(ablyMessage.handle).channels.release(channelName); + private void resetAblyClients(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + streamsChannel.reset(); + instanceStore.reset(); result.success(null); } @@ -278,45 +188,6 @@ public void onError(ErrorInfo reason) { }; } - private void getRestHistory(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; - final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); - Param[] params = (Param[]) ablyMessage.message.get(PlatformConstants.TxTransportKeys.params); - if (params == null) { - params = new Param[0]; - } - instanceStore - .getRest(ablyMessage.handle) - .channels.get(channelName) - .historyAsync(params, this.paginatedResponseHandler(result, null)); - } - - private void getRestPresence(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; - final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); - Param[] params = (Param[]) ablyMessage.message.get(PlatformConstants.TxTransportKeys.params); - if (params == null) { - params = new Param[0]; - } - instanceStore - .getRest(ablyMessage.handle) - .channels.get(channelName) - .presence.getAsync(params, this.paginatedResponseHandler(result, null)); - } - - private void getRestPresenceHistory(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; - final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); - Param[] params = (Param[]) ablyMessage.message.get(PlatformConstants.TxTransportKeys.params); - if (params == null) { - params = new Param[0]; - } - instanceStore - .getRest(ablyMessage.handle) - .channels.get(channelName) - .presence.historyAsync(params, this.paginatedResponseHandler(result, null)); - } - private void getRealtimePresence(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); @@ -535,7 +406,7 @@ private void publishRealtimeChannelMessage( } Message[] messages = new Message[channelMessages.size()]; messages = channelMessages.toArray(messages); - channel.publish(messages, handleCompletionWithListener(result)); + channel.publish(messages, handlePublishResult(result)); } catch (AblyException e) { handleAblyException(result, e); } @@ -544,9 +415,19 @@ private void publishRealtimeChannelMessage( private void releaseRealtimeChannel(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); + final PubSubRealtimeClient realtime = instanceStore.getRealtime(ablyMessage.handle); + if (realtime == null) { + result.error("release_error", "No Ably client with handle " + ablyMessage.handle, null); + return; + } - instanceStore.getRealtime(ablyMessage.handle).channels.release(channelName); - result.success(null); + try { + // Throws 90011 unless the channel is INITIALIZED, DETACHED or FAILED (RTS4c-e). + realtime.channels.release(channelName); + result.success(null); + } catch (AblyException e) { + handleAblyException(result, e); + } } private void realtimeTime(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) { @@ -554,18 +435,13 @@ private void realtimeTime(@NonNull MethodCall methodCall, @NonNull MethodChannel time(result, instanceStore.getRealtime(ablyMessage.handle)); } - private void restTime(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) { - final AblyFlutterMessage ablyMessage = (AblyFlutterMessage) methodCall.arguments; - time(result, instanceStore.getRest(ablyMessage.handle)); - } - private void connectionRecoveryKey(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) { final AblyFlutterMessage ablyMessage = (AblyFlutterMessage) methodCall.arguments; - AblyRealtime realtime = instanceStore.getRealtime(ablyMessage.handle); + PubSubRealtimeClient realtime = instanceStore.getRealtime(ablyMessage.handle); result.success(realtime.connection.createRecoveryKey()); } - private void time(@NonNull MethodChannel.Result result, AblyBase client) { + private void time(@NonNull MethodChannel.Result result, PubSubBase client) { Callback callback = new Callback() { @Override public void onSuccess(Long timeResult) { @@ -687,18 +563,12 @@ private Param[] createParamsArrayFromMap(@Nullable Map paramsMap private void pushDevice(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { final AblyFlutterMessage ablyMessage = (AblyFlutterMessage) call.arguments; try { - AblyRealtime realtime = instanceStore.getRealtime(ablyMessage.handle); + PubSubRealtimeClient realtime = instanceStore.getRealtime(ablyMessage.handle); if (realtime != null) { result.success(realtime.device()); return; } - AblyRest rest = instanceStore.getRest(ablyMessage.handle); - if (rest != null) { - result.success(rest.device()); - return; - } - throw AblyException.fromErrorInfo(new ErrorInfo("No Ably client exists", 400, 40000)); } catch (AblyException e) { handleAblyException(result, e); diff --git a/android/src/main/java/io/ably/flutter/plugin/AuthMethodHandler.java b/android/src/main/java/io/ably/flutter/plugin/AuthMethodHandler.java index 6ce5c0135..e59e75183 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AuthMethodHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/AuthMethodHandler.java @@ -10,14 +10,12 @@ import java.util.concurrent.Executors; import io.ably.flutter.plugin.generated.PlatformConstants; -import io.ably.lib.rest.Auth; -import io.ably.lib.types.AblyException; +import io.ably.pubsub.http.Auth; +import io.ably.pubsub.types.AblyException; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; class AuthMethodHandler { - enum Type{Realtime,Rest} - private final AblyInstanceStore instanceStore; private final ExecutorService executor = Executors.newSingleThreadExecutor(); @@ -26,7 +24,7 @@ public AuthMethodHandler(AblyInstanceStore instanceStore) { } - void authorize(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result, Type type) { + void authorize(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) { final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage) methodCall.arguments; final Auth.TokenParams tokenParams = (Auth.TokenParams) ablyMessage.message.get(PlatformConstants.TxTransportKeys.params); @@ -36,7 +34,7 @@ void authorize(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result res executor.execute(() -> { try { - final Auth.TokenDetails tokenDetails = getAuth(ablyMessage, type) + final Auth.TokenDetails tokenDetails = getAuth(ablyMessage) .authorize(tokenParams, options); result.success(tokenDetails); } catch (AblyException e) { @@ -46,14 +44,11 @@ void authorize(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result res } - private Auth getAuth(AblyFlutterMessage> ablyMessage, Type type) { - if (type == Type.Realtime) { - return instanceStore.getRealtime(ablyMessage.handle).auth; - } - return instanceStore.getRest(ablyMessage.handle).auth; + private Auth getAuth(AblyFlutterMessage> ablyMessage) { + return instanceStore.getRealtime(ablyMessage.handle).auth; } - void requestToken(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result, Type type) { + void requestToken(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) { final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage) methodCall.arguments; final Auth.TokenParams tokenParams = (Auth.TokenParams) ablyMessage.message.get(PlatformConstants.TxTransportKeys.params); @@ -63,7 +58,7 @@ void requestToken(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result executor.execute(() -> { try { - final Auth.TokenDetails tokenDetails = getAuth(ablyMessage, type) + final Auth.TokenDetails tokenDetails = getAuth(ablyMessage) .requestToken(tokenParams, options); result.success(tokenDetails); } catch (AblyException e) { @@ -72,7 +67,7 @@ void requestToken(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result }); } - void createTokenRequest(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result, Type type) { + void createTokenRequest(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) { final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage) methodCall.arguments; final Auth.TokenParams tokenParams = (Auth.TokenParams) ablyMessage.message.get(PlatformConstants.TxTransportKeys.params); @@ -82,7 +77,7 @@ void createTokenRequest(@NonNull MethodCall methodCall, @NonNull MethodChannel.R executor.execute(() -> { try { - final Auth.TokenRequest tokenRequest = getAuth(ablyMessage, type) + final Auth.TokenRequest tokenRequest = getAuth(ablyMessage) .createTokenRequest(tokenParams, options); result.success(tokenRequest); } catch (AblyException e) { @@ -91,8 +86,8 @@ void createTokenRequest(@NonNull MethodCall methodCall, @NonNull MethodChannel.R }); } - public void clientId(MethodCall methodCall, MethodChannel.Result result, Type type) { - String clientId = getAuth((AblyFlutterMessage) methodCall.arguments, type).clientId; + public void clientId(MethodCall methodCall, MethodChannel.Result result) { + String clientId = getAuth((AblyFlutterMessage) methodCall.arguments).clientId; result.success(clientId); } } diff --git a/android/src/main/java/io/ably/flutter/plugin/BackgroundMethodCallHandler.java b/android/src/main/java/io/ably/flutter/plugin/BackgroundMethodCallHandler.java index e3a9a4740..4e48057b7 100644 --- a/android/src/main/java/io/ably/flutter/plugin/BackgroundMethodCallHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/BackgroundMethodCallHandler.java @@ -9,7 +9,7 @@ import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodCodec; -import io.ably.lib.util.Log; +import io.ably.pubsub.util.Log; /** * Receives method calls on the background method channel from the Dart side's diff --git a/android/src/main/java/io/ably/flutter/plugin/MainThreadMethodResult.java b/android/src/main/java/io/ably/flutter/plugin/MainThreadMethodResult.java index fd1f5ef27..b69528675 100644 --- a/android/src/main/java/io/ably/flutter/plugin/MainThreadMethodResult.java +++ b/android/src/main/java/io/ably/flutter/plugin/MainThreadMethodResult.java @@ -3,7 +3,7 @@ import android.os.Handler; import android.os.Looper; -import io.ably.lib.util.Log; +import io.ably.pubsub.util.Log; import io.flutter.plugin.common.MethodChannel; // MethodChannel.Result wrapper that responds on the platform thread. diff --git a/android/src/main/java/io/ably/flutter/plugin/StreamsChannel.java b/android/src/main/java/io/ably/flutter/plugin/StreamsChannel.java index 5c476fa15..11a39ec2f 100644 --- a/android/src/main/java/io/ably/flutter/plugin/StreamsChannel.java +++ b/android/src/main/java/io/ably/flutter/plugin/StreamsChannel.java @@ -34,7 +34,7 @@ import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodCodec; -import io.ably.lib.util.Log; +import io.ably.pubsub.util.Log; final class StreamsChannel { diff --git a/android/src/main/java/io/ably/flutter/plugin/dto/EnrichedConnectionStateChange.java b/android/src/main/java/io/ably/flutter/plugin/dto/EnrichedConnectionStateChange.java index dbee3bbcf..64e021143 100644 --- a/android/src/main/java/io/ably/flutter/plugin/dto/EnrichedConnectionStateChange.java +++ b/android/src/main/java/io/ably/flutter/plugin/dto/EnrichedConnectionStateChange.java @@ -1,6 +1,6 @@ package io.ably.flutter.plugin.dto; -import io.ably.lib.realtime.ConnectionStateListener; +import io.ably.pubsub.realtime.ConnectionStateListener; public class EnrichedConnectionStateChange { public final ConnectionStateListener.ConnectionStateChange stateChange; diff --git a/android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java b/android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java index 06b0e2bf3..e5ff33242 100644 --- a/android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java +++ b/android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java @@ -19,43 +19,28 @@ static final public class CodecTypes { public static final byte tokenParams = (byte) 135; public static final byte tokenDetails = (byte) 136; public static final byte tokenRequest = (byte) 137; - public static final byte restChannelOptions = (byte) 138; - public static final byte realtimeChannelOptions = (byte) 139; - public static final byte paginatedResult = (byte) 140; - public static final byte restHistoryParams = (byte) 141; - public static final byte realtimeHistoryParams = (byte) 142; - public static final byte restPresenceParams = (byte) 143; - public static final byte presenceMessage = (byte) 144; - public static final byte realtimePresenceParams = (byte) 145; - public static final byte deviceDetails = (byte) 146; - public static final byte localDevice = (byte) 147; - public static final byte pushChannelSubscription = (byte) 148; - public static final byte unNotificationSettings = (byte) 149; - public static final byte remoteMessage = (byte) 150; - public static final byte errorInfo = (byte) 151; - public static final byte logLevel = (byte) 152; - public static final byte connectionStateChange = (byte) 153; - public static final byte channelStateChange = (byte) 154; - public static final byte cipherParams = (byte) 155; + public static final byte realtimeChannelOptions = (byte) 138; + public static final byte paginatedResult = (byte) 139; + public static final byte realtimeHistoryParams = (byte) 140; + public static final byte presenceMessage = (byte) 141; + public static final byte realtimePresenceParams = (byte) 142; + public static final byte deviceDetails = (byte) 143; + public static final byte localDevice = (byte) 144; + public static final byte pushChannelSubscription = (byte) 145; + public static final byte unNotificationSettings = (byte) 146; + public static final byte remoteMessage = (byte) 147; + public static final byte errorInfo = (byte) 148; + public static final byte logLevel = (byte) 149; + public static final byte connectionStateChange = (byte) 150; + public static final byte channelStateChange = (byte) 151; + public static final byte cipherParams = (byte) 152; } static final public class PlatformMethod { public static final String getPlatformVersion = "getPlatformVersion"; public static final String getVersion = "getVersion"; public static final String resetAblyClients = "resetAblyClients"; - public static final String authCallback = "authCallback"; public static final String realtimeAuthCallback = "realtimeAuthCallback"; - public static final String createRest = "createRest"; - public static final String setRestChannelOptions = "setRestChannelOptions"; - public static final String publish = "publish"; - public static final String restHistory = "restHistory"; - public static final String restPresenceGet = "restPresenceGet"; - public static final String restPresenceHistory = "restPresenceHistory"; - public static final String releaseRestChannel = "releaseRestChannel"; - public static final String restAuthAuthorize = "restAuthAuthorize"; - public static final String restAuthCreateTokenRequest = "restAuthCreateTokenRequest"; - public static final String restAuthRequestToken = "restAuthRequestToken"; - public static final String restAuthGetClientId = "restAuthGetClientId"; public static final String createRealtime = "createRealtime"; public static final String connectRealtime = "connectRealtime"; public static final String closeRealtime = "closeRealtime"; @@ -72,14 +57,11 @@ static final public class PlatformMethod { public static final String releaseRealtimeChannel = "releaseRealtimeChannel"; public static final String realtimeHistory = "realtimeHistory"; public static final String realtimeTime = "realtimeTime"; - public static final String restTime = "restTime"; public static final String realtimeAuthAuthorize = "realtimeAuthAuthorize"; public static final String realtimeAuthCreateTokenRequest = "realtimeAuthCreateTokenRequest"; public static final String realtimeAuthRequestToken = "realtimeAuthRequestToken"; public static final String realtimeAuthGetClientId = "realtimeAuthGetClientId"; - public static final String connectionRecoveryKey = "connectionRecoveryKey"; - public static final String pushActivate = "pushActivate"; public static final String pushDeactivate = "pushDeactivate"; public static final String pushReset = "pushReset"; @@ -168,8 +150,7 @@ static final public class TxClientOptions { public static final String clientId = "clientId"; public static final String logLevel = "logLevel"; public static final String tls = "tls"; - public static final String restHost = "restHost"; - public static final String realtimeHost = "realtimeHost"; + public static final String endpoint = "endpoint"; public static final String port = "port"; public static final String tlsPort = "tlsPort"; public static final String autoConnect = "autoConnect"; @@ -177,7 +158,6 @@ static final public class TxClientOptions { public static final String queueMessages = "queueMessages"; public static final String echoMessages = "echoMessages"; public static final String recover = "recover"; - public static final String environment = "environment"; public static final String idempotentRestPublishing = "idempotentRestPublishing"; public static final String disconnectedRetryTimeout = "disconnectedRetryTimeout"; public static final String suspendedRetryTimeout = "suspendedRetryTimeout"; @@ -187,7 +167,6 @@ static final public class TxClientOptions { public static final String httpMaxRetryCount = "httpMaxRetryCount"; public static final String realtimeRequestTimeout = "realtimeRequestTimeout"; public static final String fallbackHosts = "fallbackHosts"; - public static final String fallbackHostsUseDefault = "fallbackHostsUseDefault"; public static final String fallbackRetryTimeout = "fallbackRetryTimeout"; public static final String defaultTokenParams = "defaultTokenParams"; public static final String channelRetryTimeout = "channelRetryTimeout"; @@ -195,10 +174,6 @@ static final public class TxClientOptions { public static final String dartVersion = "dartVersion"; } - static final public class TxRestChannelOptions { - public static final String cipherParams = "cipherParams"; - } - static final public class TxRealtimeChannelOptions { public static final String params = "params"; public static final String modes = "modes"; @@ -352,13 +327,6 @@ static final public class TxPaginatedResult { public static final String hasNext = "hasNext"; } - static final public class TxRestHistoryParams { - public static final String start = "start"; - public static final String end = "end"; - public static final String direction = "direction"; - public static final String limit = "limit"; - } - static final public class TxRealtimeHistoryParams { public static final String start = "start"; public static final String end = "end"; @@ -367,12 +335,6 @@ static final public class TxRealtimeHistoryParams { public static final String untilAttach = "untilAttach"; } - static final public class TxRestPresenceParams { - public static final String limit = "limit"; - public static final String clientId = "clientId"; - public static final String connectionId = "connectionId"; - } - static final public class TxRealtimePresenceParams { public static final String waitForSync = "waitForSync"; public static final String clientId = "clientId"; diff --git a/android/src/main/java/io/ably/flutter/plugin/push/FirebaseMessagingReceiver.java b/android/src/main/java/io/ably/flutter/plugin/push/FirebaseMessagingReceiver.java index 6034f7c94..1835cfec5 100644 --- a/android/src/main/java/io/ably/flutter/plugin/push/FirebaseMessagingReceiver.java +++ b/android/src/main/java/io/ably/flutter/plugin/push/FirebaseMessagingReceiver.java @@ -17,7 +17,7 @@ import java.util.concurrent.Executors; import io.ably.flutter.plugin.AblyFlutter; -import io.ably.lib.util.Log; +import io.ably.pubsub.util.Log; public class FirebaseMessagingReceiver extends BroadcastReceiver { private static final String TAG = FirebaseMessagingReceiver.class.getName(); diff --git a/android/src/main/java/io/ably/flutter/plugin/push/ManualFlutterApplicationRunner.java b/android/src/main/java/io/ably/flutter/plugin/push/ManualFlutterApplicationRunner.java index f108091b1..728250397 100644 --- a/android/src/main/java/io/ably/flutter/plugin/push/ManualFlutterApplicationRunner.java +++ b/android/src/main/java/io/ably/flutter/plugin/push/ManualFlutterApplicationRunner.java @@ -13,7 +13,7 @@ import io.ably.flutter.plugin.AblyMessageCodec; import io.ably.flutter.plugin.util.CipherParamsStorage; -import io.ably.lib.util.Log; +import io.ably.pubsub.util.Log; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.plugin.common.MethodCall; diff --git a/android/src/main/java/io/ably/flutter/plugin/push/PushActivationEventHandlers.java b/android/src/main/java/io/ably/flutter/plugin/push/PushActivationEventHandlers.java index 289bd9ccb..067a4e98d 100644 --- a/android/src/main/java/io/ably/flutter/plugin/push/PushActivationEventHandlers.java +++ b/android/src/main/java/io/ably/flutter/plugin/push/PushActivationEventHandlers.java @@ -9,8 +9,8 @@ import androidx.annotation.Nullable; import io.ably.flutter.plugin.generated.PlatformConstants; -import io.ably.lib.types.ErrorInfo; -import io.ably.lib.util.Log; +import io.ably.pubsub.types.ErrorInfo; +import io.ably.pubsub.util.Log; import io.flutter.plugin.common.MethodChannel; public class PushActivationEventHandlers { diff --git a/android/src/main/java/io/ably/flutter/plugin/push/PushActivationReceiver.java b/android/src/main/java/io/ably/flutter/plugin/push/PushActivationReceiver.java index 568512dfb..db62d3eb1 100644 --- a/android/src/main/java/io/ably/flutter/plugin/push/PushActivationReceiver.java +++ b/android/src/main/java/io/ably/flutter/plugin/push/PushActivationReceiver.java @@ -8,8 +8,8 @@ import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import io.ably.lib.types.ErrorInfo; -import io.ably.lib.util.IntentUtils; +import io.ably.pubsub.types.ErrorInfo; +import io.ably.pubsub.util.IntentUtils; class PushActivationReceiver extends android.content.BroadcastReceiver { static final String PUSH_ACTIVATE_ACTION = "io.ably.broadcast.PUSH_ACTIVATE"; diff --git a/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingEventHandlers.java b/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingEventHandlers.java index d215e8c86..67c268816 100644 --- a/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingEventHandlers.java +++ b/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingEventHandlers.java @@ -14,7 +14,7 @@ import com.google.firebase.messaging.RemoteMessage; import io.ably.flutter.plugin.generated.PlatformConstants; -import io.ably.lib.util.Log; +import io.ably.pubsub.util.Log; import io.flutter.plugin.common.MethodChannel; /** diff --git a/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingService.java b/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingService.java index 3ac543c3d..4be8749b0 100644 --- a/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingService.java +++ b/android/src/main/java/io/ably/flutter/plugin/push/PushMessagingService.java @@ -5,8 +5,8 @@ import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; -import io.ably.lib.push.ActivationContext; -import io.ably.lib.types.RegistrationToken; +import io.ably.pubsub.push.ActivationContext; +import io.ably.pubsub.types.RegistrationToken; // This service exists to update Ably with the latest FCM registration token, and not // to listen to any messages from FCM. diff --git a/android/src/main/java/io/ably/flutter/plugin/types/PlatformClientOptions.java b/android/src/main/java/io/ably/flutter/plugin/types/PlatformClientOptions.java index d123dbb22..4b5137db7 100644 --- a/android/src/main/java/io/ably/flutter/plugin/types/PlatformClientOptions.java +++ b/android/src/main/java/io/ably/flutter/plugin/types/PlatformClientOptions.java @@ -1,13 +1,13 @@ package io.ably.flutter.plugin.types; -import io.ably.lib.types.ClientOptions; +import io.ably.pubsub.types.ClientOptions; public class PlatformClientOptions { /** * Whether dart side has authCallback. * If true, autCallback proxy will be added in methodCallHandler - * while creating rest/realtime instances + * while creating realtime instances */ public final boolean hasAuthCallback; public final ClientOptions options; diff --git a/android/src/main/java/io/ably/flutter/plugin/util/CipherParamsStorage.java b/android/src/main/java/io/ably/flutter/plugin/util/CipherParamsStorage.java index 557bd383b..bda5fc1bf 100644 --- a/android/src/main/java/io/ably/flutter/plugin/util/CipherParamsStorage.java +++ b/android/src/main/java/io/ably/flutter/plugin/util/CipherParamsStorage.java @@ -2,7 +2,7 @@ import java.util.HashMap; -import io.ably.lib.util.Crypto; +import io.ably.pubsub.util.Crypto; /** * Stores and gets CipherParams by an Integer handle. On Android, ably-flutter will use this handle diff --git a/bin/README.md b/bin/README.md index 0e8fcbe05..5589d0fbd 100644 --- a/bin/README.md +++ b/bin/README.md @@ -29,8 +29,8 @@ These files are generated/modified upon code generation 1. `lib/src/generated/platformconstants.dart` for use in Flutter/Dart 2. `android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java` for use in Android/Java -3. `ios/Classes/codec/AblyPlatformConstants.h` for use in iOS/Objective-C -4. `ios/Classes/codec/AblyPlatformConstants.m` for use in iOS/Objective-C +3. `ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h` for use in iOS/Objective-C +4. `ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m` for use in iOS/Objective-C #### When would I need to run code generation? diff --git a/bin/codegen.dart b/bin/codegen.dart index d58448ddb..319d47279 100644 --- a/bin/codegen.dart +++ b/bin/codegen.dart @@ -16,9 +16,9 @@ Map toGenerate = { java_template.$: '${projectRoot}android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java', objc_header_template.$: - '${projectRoot}ios/Classes/codec/AblyPlatformConstants.h', + '${projectRoot}ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h', objc_impl_template.$: - '${projectRoot}ios/Classes/codec/AblyPlatformConstants.m', + '${projectRoot}ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m', }; void main() { diff --git a/bin/codegen_context.dart b/bin/codegen_context.dart index 3b91e3079..fbfcd9049 100644 --- a/bin/codegen_context.dart +++ b/bin/codegen_context.dart @@ -14,12 +14,9 @@ Iterable> get _types sync* { 'tokenParams', 'tokenDetails', 'tokenRequest', - 'restChannelOptions', 'realtimeChannelOptions', 'paginatedResult', - 'restHistoryParams', 'realtimeHistoryParams', - 'restPresenceParams', 'presenceMessage', 'realtimePresenceParams', @@ -64,22 +61,8 @@ const List> _platformMethods = [ {'name': 'resetAblyClients', 'value': 'resetAblyClients'}, // Auth - {'name': 'authCallback', 'value': 'authCallback'}, {'name': 'realtimeAuthCallback', 'value': 'realtimeAuthCallback'}, - // Rest - {'name': 'createRest', 'value': 'createRest'}, - {'name': 'setRestChannelOptions', 'value': 'setRestChannelOptions'}, - {'name': 'publish', 'value': 'publish'}, - {'name': 'restHistory', 'value': 'restHistory'}, - {'name': 'restPresenceGet', 'value': 'restPresenceGet'}, - {'name': 'restPresenceHistory', 'value': 'restPresenceHistory'}, - {'name': 'releaseRestChannel', 'value': 'releaseRestChannel'}, - {'name': 'restAuthAuthorize', 'value': 'restAuthAuthorize'}, - {'name': 'restAuthCreateTokenRequest', 'value': 'restAuthCreateTokenRequest'}, - {'name': 'restAuthRequestToken', 'value': 'restAuthRequestToken'}, - {'name': 'restAuthGetClientId', 'value': 'restAuthGetClientId'}, - // Realtime {'name': 'createRealtime', 'value': 'createRealtime'}, {'name': 'connectRealtime', 'value': 'connectRealtime'}, @@ -95,19 +78,19 @@ const List> _platformMethods = [ {'name': 'onRealtimePresenceMessage', 'value': 'onRealtimePresenceMessage'}, { 'name': 'publishRealtimeChannelMessage', - 'value': 'publishRealtimeChannelMessage' + 'value': 'publishRealtimeChannelMessage', }, {'name': 'releaseRealtimeChannel', 'value': 'releaseRealtimeChannel'}, {'name': 'realtimeHistory', 'value': 'realtimeHistory'}, {'name': 'realtimeTime', 'value': 'realtimeTime'}, - {'name': 'restTime', 'value': 'restTime'}, {'name': 'realtimeAuthAuthorize', 'value': 'realtimeAuthAuthorize'}, { 'name': 'realtimeAuthCreateTokenRequest', - 'value': 'realtimeAuthCreateTokenRequest' + 'value': 'realtimeAuthCreateTokenRequest', }, {'name': 'realtimeAuthRequestToken', 'value': 'realtimeAuthRequestToken'}, {'name': 'realtimeAuthGetClientId', 'value': 'realtimeAuthGetClientId'}, + {'name': 'connectionRecoveryKey', 'value': 'connectionRecoveryKey'}, // Push Notifications {'name': 'pushActivate', 'value': 'pushActivate'}, @@ -124,7 +107,7 @@ const List> _platformMethods = [ // Used only on iOS { 'name': 'pushGetNotificationSettings', - 'value': 'pushGetNotificationSettings' + 'value': 'pushGetNotificationSettings', }, // Used only on iOS {'name': 'pushOpenSettingsFor', 'value': 'pushOpenSettingsFor'}, @@ -135,11 +118,11 @@ const List> _platformMethods = [ // Push Notification Events { 'name': 'pushNotificationTapLaunchedAppFromTerminated', - 'value': 'pushNotificationTapLaunchedAppFromTerminated' + 'value': 'pushNotificationTapLaunchedAppFromTerminated', }, { 'name': 'pushOnShowNotificationInForeground', - 'value': 'pushOnShowNotificationInForeground' + 'value': 'pushOnShowNotificationInForeground', }, {'name': 'pushOnMessage', 'value': 'pushOnMessage'}, {'name': 'pushOnBackgroundMessage', 'value': 'pushOnBackgroundMessage'}, @@ -147,17 +130,17 @@ const List> _platformMethods = [ // Used only on Android { 'name': 'pushBackgroundFlutterApplicationReadyOnAndroid', - 'value': 'pushBackgroundFlutterApplicationReadyOnAndroid' + 'value': 'pushBackgroundFlutterApplicationReadyOnAndroid', }, // Realtime events { 'name': 'onRealtimeConnectionStateChanged', - 'value': 'onRealtimeConnectionStateChanged' + 'value': 'onRealtimeConnectionStateChanged', }, { 'name': 'onRealtimeChannelStateChanged', - 'value': 'onRealtimeChannelStateChanged' + 'value': 'onRealtimeChannelStateChanged', }, {'name': 'onRealtimeChannelMessage', 'value': 'onRealtimeChannelMessage'}, @@ -183,15 +166,15 @@ const List> _objects = [ 'clientId', 'options', 'messages', - ] + ], }, { 'name': 'AblyMessage', - 'properties': ['registrationHandle', 'type', 'message'] + 'properties': ['registrationHandle', 'type', 'message'], }, { 'name': 'AblyEventMessage', - 'properties': ['eventName', 'type', 'message'] + 'properties': ['eventName', 'type', 'message'], }, { 'name': 'ErrorInfo', @@ -201,20 +184,20 @@ const List> _objects = [ 'statusCode', 'href', 'requestId', - 'cause' - ] + 'cause', + ], }, { 'name': 'MessageData', - 'properties': ['data', 'type'] + 'properties': ['data', 'type'], }, { 'name': 'DeltaExtras', - 'properties': ['format', 'from'] + 'properties': ['format', 'from'], }, { 'name': 'MessageExtras', - 'properties': ['extras', 'delta'] + 'properties': ['extras', 'delta'], }, { 'name': 'ClientOptions', @@ -233,8 +216,7 @@ const List> _objects = [ 'clientId', 'logLevel', 'tls', - 'restHost', - 'realtimeHost', + 'endpoint', 'port', 'tlsPort', 'autoConnect', @@ -242,28 +224,25 @@ const List> _objects = [ 'queueMessages', 'echoMessages', 'recover', - 'environment', 'idempotentRestPublishing', + 'disconnectedRetryTimeout', + 'suspendedRetryTimeout', + 'httpMaxRetryDuration', 'httpOpenTimeout', 'httpRequestTimeout', 'httpMaxRetryCount', 'realtimeRequestTimeout', 'fallbackHosts', - 'fallbackHostsUseDefault', 'fallbackRetryTimeout', 'defaultTokenParams', 'channelRetryTimeout', 'transportParams', 'dartVersion', - ] - }, - { - 'name': 'RestChannelOptions', - 'properties': ['cipherParams'] + ], }, { 'name': 'RealtimeChannelOptions', - 'properties': ['params', 'modes', 'cipherParams'] + 'properties': ['params', 'modes', 'cipherParams'], }, { 'name': 'CipherParams', @@ -276,8 +255,8 @@ const List> _objects = [ 'expires', 'issued', 'capability', - 'clientId' - ] + 'clientId', + ], }, { 'name': 'AuthOptions', @@ -291,8 +270,8 @@ const List> _objects = [ 'authHeaders', 'authParams', 'queryTime', - 'useTokenAuth' - ] + 'useTokenAuth', + ], }, { 'name': 'TokenParams', @@ -301,8 +280,8 @@ const List> _objects = [ 'clientId', 'nonce', 'timestamp', - 'ttl' - ] + 'ttl', + ], }, { 'name': 'TokenRequest', @@ -313,8 +292,8 @@ const List> _objects = [ 'mac', 'nonce', 'timestamp', - 'ttl' - ] + 'ttl', + ], }, { 'name': 'EnumConstants', @@ -342,8 +321,8 @@ const List> _objects = [ 'presence', 'publish', 'subscribe', - 'presenceSubscribe' - ] + 'presenceSubscribe', + ], }, { 'name': 'FormFactorEnum', @@ -355,8 +334,8 @@ const List> _objects = [ 'watch', 'car', 'embedded', - 'other' - ] + 'other', + ], }, { 'name': 'LogLevelEnum', @@ -364,19 +343,27 @@ const List> _objects = [ }, { 'name': 'DevicePlatformEnum', - 'properties': ['ios', 'android', 'browser'] + 'properties': ['ios', 'android', 'browser'], }, { 'name': 'DevicePushStateEnum', - 'properties': ['active', 'failing', 'failed'] + 'properties': ['active', 'failing', 'failed'], }, { 'name': 'ConnectionStateChange', - 'properties': ['current', 'previous', 'event', 'retryIn', 'reason'] + 'properties': [ + 'current', + 'previous', + 'event', + 'retryIn', + 'reason', + 'connectionId', + 'connectionKey', + ], }, { 'name': 'ChannelStateChange', - 'properties': ['current', 'previous', 'event', 'resumed', 'reason'] + 'properties': ['current', 'previous', 'event', 'resumed', 'reason'], }, { 'name': 'Message', @@ -388,8 +375,8 @@ const List> _objects = [ 'encoding', 'data', 'name', - 'extras' - ] + 'extras', + ], }, { 'name': 'PresenceMessage', @@ -402,46 +389,19 @@ const List> _objects = [ 'encoding', 'extras', 'timestamp', - ] + ], }, { 'name': 'PaginatedResult', - 'properties': ['items', 'type', 'hasNext'] - }, - { - 'name': 'RestHistoryParams', - 'properties': [ - 'start', - 'end', - 'direction', - 'limit', - ] + 'properties': ['items', 'type', 'hasNext'], }, { 'name': 'RealtimeHistoryParams', - 'properties': [ - 'start', - 'end', - 'direction', - 'limit', - 'untilAttach', - ] - }, - { - 'name': 'RestPresenceParams', - 'properties': [ - 'limit', - 'clientId', - 'connectionId', - ] + 'properties': ['start', 'end', 'direction', 'limit', 'untilAttach'], }, { 'name': 'RealtimePresenceParams', - 'properties': [ - 'waitForSync', - 'clientId', - 'connectionId', - ] + 'properties': ['waitForSync', 'clientId', 'connectionId'], }, { 'name': 'DeviceDetails', @@ -451,20 +411,20 @@ const List> _objects = [ 'platform', 'formFactor', 'metadata', - 'devicePushDetails' - ] + 'devicePushDetails', + ], }, { 'name': 'DevicePushDetails', - 'properties': ['recipient', 'state', 'errorReason'] + 'properties': ['recipient', 'state', 'errorReason'], }, { 'name': 'LocalDevice', - 'properties': ['deviceSecret', 'deviceIdentityToken'] + 'properties': ['deviceSecret', 'deviceIdentityToken'], }, { 'name': 'PushChannelSubscription', - 'properties': ['channel', 'deviceId', 'clientId'] + 'properties': ['channel', 'deviceId', 'clientId'], }, { 'name': 'PushRequestPermission', @@ -477,7 +437,7 @@ const List> _objects = [ 'providesAppNotificationSettings', 'provisional', 'announcement', - ] + ], }, { 'name': 'UNNotificationSettings', @@ -496,15 +456,15 @@ const List> _objects = [ 'announcementSetting', 'scheduledDeliverySetting', 'timeSensitiveSetting', - ] + ], }, { 'name': 'UNNotificationSettingEnum', - 'properties': ['notSupported', 'disabled', 'enabled'] + 'properties': ['notSupported', 'disabled', 'enabled'], }, { 'name': 'UNAlertStyleEnum', - 'properties': ['none', 'banner', 'alert'] + 'properties': ['none', 'banner', 'alert'], }, { 'name': 'UNAuthorizationStatusEnum', @@ -514,31 +474,28 @@ const List> _objects = [ 'authorized', 'provisional', 'ephemeral', - ] + ], }, { 'name': 'UNShowPreviewsSettingEnum', - 'properties': ['always', 'whenAuthenticated', 'never'] + 'properties': ['always', 'whenAuthenticated', 'never'], }, { 'name': 'RemoteMessage', - 'properties': [ - 'data', - 'notification', - ] + 'properties': ['data', 'notification'], }, { 'name': 'Notification', - 'properties': ['title', 'body'] + 'properties': ['title', 'body'], }, { 'name': 'CryptoGetParams', - 'properties': ['algorithm', 'key'] + 'properties': ['algorithm', 'key'], }, { 'name': 'CryptoGenerateRandomKey', - 'properties': ['keyLength'] - } + 'properties': ['keyLength'], + }, ]; // exporting all the constants as a single map @@ -546,5 +503,5 @@ const List> _objects = [ Map context = { 'types': _types, 'methods': _platformMethods, - 'objects': _objects + 'objects': _objects, }; diff --git a/bin/templates/platformconstants.dart.dart b/bin/templates/platformconstants.dart.dart index 7864dcde3..b9f7dd00a 100644 --- a/bin/templates/platformconstants.dart.dart +++ b/bin/templates/platformconstants.dart.dart @@ -1,19 +1,20 @@ String getPrefix(String str) => (str.length > 26) ? '\n ' : ' '; -String $(Map c) => ''' +String $(Map c) => + ''' // ignore_for_file: public_member_api_docs class CodecTypes { -${c['types'].map((_) => " static const int ${_['name']} =" - " ${_['value']};").join('\n')} +${c['types'].map((e) => " static const int ${e['name']} =" + " ${e['value']};").join('\n')} } class PlatformMethod { -${c['methods'].map((_) => " static const String ${_['name']} =${getPrefix(_['value'] as String)}'${_['value']}';").join('\n')} +${c['methods'].map((e) => " static const String ${e['name']} =${getPrefix(e['value'] as String)}'${e['value']}';").join('\n')} } -${c['objects'].map((_) => ''' -class Tx${_['name']} { -${_['properties'].map((_p) => " static const String $_p = '$_p';").join('\n')} +${c['objects'].map((e) => ''' +class Tx${e['name']} { +${e['properties'].map((_p) => " static const String $_p = '$_p';").join('\n')} } ''').join('\n')}'''; diff --git a/bin/templates/platformconstants.h.dart b/bin/templates/platformconstants.h.dart index 635ab269c..d7aab37b1 100644 --- a/bin/templates/platformconstants.h.dart +++ b/bin/templates/platformconstants.h.dart @@ -1,17 +1,18 @@ -String $(Map c) => ''' +String $(Map c) => + ''' @import Foundation; typedef NS_ENUM(UInt8, CodecType) { -\t${c['types'].map((_) => 'CodecType${_capitalize(_['name'] as String)} = ${_['value']},').join('\n\t')} +\t${c['types'].map((e) => 'CodecType${_capitalize(e['name'] as String)} = ${e['value']},').join('\n\t')} }; // flutter platform channel method names -${c['methods'].map((_) => 'extern NSString *const AblyPlatformMethod_${_['name']};').join('\n')} +${c['methods'].map((e) => 'extern NSString *const AblyPlatformMethod_${e['name']};').join('\n')} -${c['objects'].map((_) => ''' -// key constants for ${_['name']} -${_['properties'].map((name) => 'extern NSString *const Tx${_['name']}_$name;').join('\n')} +${c['objects'].map((e) => ''' +// key constants for ${e['name']} +${e['properties'].map((name) => 'extern NSString *const Tx${e['name']}_$name;').join('\n')} ''').join('\n')}'''; String _capitalize(String sentence) => diff --git a/bin/templates/platformconstants.java.dart b/bin/templates/platformconstants.java.dart index 17fccf9ab..266d05a47 100644 --- a/bin/templates/platformconstants.java.dart +++ b/bin/templates/platformconstants.java.dart @@ -1,20 +1,21 @@ -String $(Map c) => ''' +String $(Map c) => + ''' package io.ably.flutter.plugin.generated; final public class PlatformConstants { \tstatic final public class CodecTypes { -\t\t${c['types'].map((_) => 'public static final byte ${_['name']} = (byte) ${_['value']};').join('\n\t\t')} +\t\t${c['types'].map((e) => 'public static final byte ${e['name']} = (byte) ${e['value']};').join('\n\t\t')} \t} \tstatic final public class PlatformMethod { -\t\t${c['methods'].map((_) => 'public static final String ${_['name']} = "${_['value']}";').join('\n\t\t')} +\t\t${c['methods'].map((e) => 'public static final String ${e['name']} = "${e['value']}";').join('\n\t\t')} \t} -${c['objects'].map((_) => ''' -\tstatic final public class Tx${_['name']} { -\t\t${_['properties'].map((_p) => 'public static final String $_p = "$_p";').join('\n\t\t')} +${c['objects'].map((e) => ''' +\tstatic final public class Tx${e['name']} { +\t\t${e['properties'].map((_p) => 'public static final String $_p = "$_p";').join('\n\t\t')} \t} ''').join('\n')} } diff --git a/bin/templates/platformconstants.m.dart b/bin/templates/platformconstants.m.dart index 9ac7657b7..2fa5dd92d 100644 --- a/bin/templates/platformconstants.m.dart +++ b/bin/templates/platformconstants.m.dart @@ -1,11 +1,12 @@ -String $(Map c) => ''' +String $(Map c) => + ''' #import "AblyPlatformConstants.h" // flutter platform channel method names -${c['methods'].map((_) => 'NSString *const AblyPlatformMethod_${_['name']}= @"${_['value']}";').join('\n')} +${c['methods'].map((e) => 'NSString *const AblyPlatformMethod_${e['name']}= @"${e['value']}";').join('\n')} -${c['objects'].map((_) => ''' -// key constants for ${_['name']} -${_['properties'].map((name) => 'NSString *const Tx${_['name']}_$name = @"$name";').join('\n')} +${c['objects'].map((e) => ''' +// key constants for ${e['name']} +${e['properties'].map((name) => 'NSString *const Tx${e['name']}_$name = @"$name";').join('\n')} ''').join('\n')}'''; diff --git a/example/README.md b/example/README.md index 7c106fcc3..233e2b0cb 100644 --- a/example/README.md +++ b/example/README.md @@ -135,6 +135,6 @@ samples, guidance on mobile development, and a full API reference. If your emulator was left running when you put your machine to sleep connected to one network interface (e.g. wired ethernet over USB) and then you wake it up and then connect to a different network interface (e.g. Wi-Fi) later, then you may see errors like this when you try to use the example: -> Error provisioning Ably: SocketException: Failed host lookup: 'sandbox-rest.ably.io' (OS Error: No address associated with hostname, errno = 7) +> Error provisioning Ably: SocketException: Failed host lookup: 'sandbox.realtime.ably-nonprod.net' (OS Error: No address associated with hostname, errno = 7) The solution appears to be to quit the emulator and then restart it. diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a3f..475a62803 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,7 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 1e2fbf0d4..3ae1e2f12 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 2c5e3a732..397c998d1 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -18,7 +18,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.7.0" apply false + id "com.android.application" version "8.12.1" apply false id "org.jetbrains.kotlin.android" version "2.2.20" apply false id "com.google.gms.google-services" version "4.3.10" apply false } diff --git a/example/example.md b/example/example.md index 2a006ad5c..7a9fe88be 100644 --- a/example/example.md +++ b/example/example.md @@ -9,7 +9,7 @@ final clientOptions = ably.ClientOptions(key: ''); // Use ClientOptions to create Realtime or REST instance -ably.Realtime realtime = ably.Realtime(options: clientOptions); +ably.PubSubClient realtime = ably.createClient(options: clientOptions); ably.Rest rest = ably.Rest(options: clientOptions); ``` @@ -34,7 +34,7 @@ ably.ClientOptions clientOptions = ably.ClientOptions( ); // Use ClientOptions to create Realtime or REST instance -ably.Realtime realtime = ably.Realtime(options: clientOptions); +ably.PubSubClient realtime = ably.createClient(options: clientOptions); ably.Rest rest = ably.Rest(options: clientOptions); ``` @@ -48,7 +48,7 @@ Also see docs: #### Create an instance of the Realtime Client ```dart -ably.Realtime realtime = ably.Realtime(options: clientOptions); +ably.PubSubClient realtime = ably.createClient(options: clientOptions); ``` #### Read Realtime time diff --git a/example/ios/.gitignore b/example/ios/.gitignore index e96ef602b..eb5573aef 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -20,6 +20,7 @@ Flutter/Flutter.podspec Flutter/Generated.xcconfig Flutter/app.flx Flutter/app.zip +Flutter/ephemeral/ Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index f2872cf47..ab8e063fe 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 9.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index b5196d5f2..f17bddc91 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -38,11 +38,4 @@ post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end - installer.generated_projects.each do |project| - project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0' - end - end - end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ee9865844..6936b442d 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,11 +1,4 @@ PODS: - - Ably (1.2.53): - - AblyDeltaCodec (= 1.3.3) - - msgpack (= 0.4.0) - - ably_flutter (1.2.44): - - Ably (= 1.2.53) - - Flutter - - AblyDeltaCodec (1.3.3) - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) @@ -13,24 +6,14 @@ PODS: - Flutter - fluttertoast (0.0.2): - Flutter - - msgpack (0.4.0) DEPENDENCIES: - - ably_flutter (from `.symlinks/plugins/ably_flutter/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) -SPEC REPOS: - trunk: - - Ably - - AblyDeltaCodec - - msgpack - EXTERNAL SOURCES: - ably_flutter: - :path: ".symlinks/plugins/ably_flutter/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: @@ -41,15 +24,11 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/fluttertoast/ios" SPEC CHECKSUMS: - Ably: 1756d6590a572809c10fc4bcc32e05fe9e7867d7 - ably_flutter: 95785161fc2858c7be524af4334925d30682b3b0 - AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 - msgpack: c85f6251873059738472ae136951cec5f30f3251 -PODFILE CHECKSUM: 5dac7b8eff57b86c98038531a0cc7562fb467acc +PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 96babbb61..3356fbd5a 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -14,6 +14,7 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; AD39CDEF274E39AA00CB3C5F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AD39CDEE274E39AA00CB3C5F /* Assets.xcassets */; }; ADD30C8A274E2F7D00EBD0F0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD30C89274E2F7D00EBD0F0 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -48,6 +49,9 @@ ADD30C8B274E2F8100EBD0F0 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; ADDAF4B526986E1100C4B0AF /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; D16A2A8265BD05ED5C5C0561 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* ably_pubsub_device_flutter */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = ably_pubsub_device_flutter; path = ../../ios/ably_pubsub_device_flutter; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -55,6 +59,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 4040014BC9459F4F22338328 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -75,6 +80,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78DABEA22ED26510000E7860 /* ably_pubsub_device_flutter */, + 784666492D4C4C64000A1A5F /* FlutterFramework */, + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -130,6 +138,9 @@ /* Begin PBXNativeTarget section */ 97C146ED1CF9000F007C117D /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -155,9 +166,12 @@ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -203,10 +217,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -222,41 +238,15 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Ably/Ably.framework", - "${BUILT_PRODUCTS_DIR}/AblyDeltaCodec/AblyDeltaCodec.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", - "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", - "${BUILT_PRODUCTS_DIR}/Toast/Toast.framework", - "${BUILT_PRODUCTS_DIR}/ably_flutter/ably_flutter.framework", "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", "${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework", - "${BUILT_PRODUCTS_DIR}/msgpack/msgpack.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Ably.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AblyDeltaCodec.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ably_flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msgpack.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -265,6 +255,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -374,7 +365,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -400,7 +391,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -465,7 +456,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -514,7 +505,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -540,7 +531,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -576,7 +567,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -618,6 +609,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..5db441f58 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 4fdf2bb72..49d2fc0c9 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,16 +1,21 @@ -import UIKit import Flutter +import UIKit @main -@objc class AppDelegate: FlutterAppDelegate { +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + // With the UIScene life cycle, `FlutterSceneDelegate` creates the engine + // once the scene connects, and plugins are registered here. + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } + override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { NSLog("application:didFailToRegisterForRemoteNotificationsWithError was called with error: %@", error.localizedDescription) } diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index ed3e8743b..5e86a79f7 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -26,6 +26,27 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + UIBackgroundModes remote-notification @@ -49,5 +70,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/app_provisioning.dart b/example/lib/app_provisioning.dart index 87f035128..0ed307ee2 100644 --- a/example/lib/app_provisioning.dart +++ b/example/lib/app_provisioning.dart @@ -2,19 +2,19 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:http/http.dart' as http; import 'package:http/retry.dart'; /// Used to provision the app with Ably sandbox environment /// A copy of this class is also present in test_integration package, to avoid -/// putting it in the base ably_flutter package +/// putting it in the base ably_pubsub_device_flutter package class AppProvisioning { - /// Prefix of REST environment used to provision the app. For example, - /// with prefix `sandbox-` URL `sandbox-rest.ably.io` will be used + /// Host of the Ably environment the app is provisioned in. /// - /// Defaults to [defaultEnvironmentPrefix] - String environmentPrefix; + /// Defaults to [defaultHost], the sandbox environment that clients reach + /// with `endpoint: 'nonprod:sandbox'`. + String host; /// List of capabilities for the provisioned key /// @@ -28,13 +28,13 @@ class AppProvisioning { /// Creates instance of [AppProvisioning] AppProvisioning({ - this.environmentPrefix = defaultEnvironmentPrefix, + this.host = defaultHost, this.keyCapabilities = defaultKeyCapabilities, this.pushEnabled = true, }); - /// Default prefix for REST provisioning url - static const String defaultEnvironmentPrefix = 'sandbox-'; + /// Host of the sandbox environment + static const String defaultHost = 'sandbox.realtime.ably-nonprod.net'; /// Default list of key capabilities /// Enables all capabilities on all channels @@ -57,8 +57,7 @@ class AppProvisioning { ); /// URL used for app provisioning requests - String get _provisioningUrl => - 'https://${environmentPrefix}rest.ably.io/apps'; + String get _provisioningUrl => 'https://$host/apps'; /// URL used for getting token request String get _tokenRequestAuthUrl => @@ -66,30 +65,27 @@ class AppProvisioning { /// A set of base headers for HTTP requests Map get _requestHeaders => { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }; + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; /// App spec used to provision the app /// /// See: https://docs.ably.com/client-lib-development-guide/test-api Map> get _appSpec => { - 'namespaces': [ - { - 'id': 'pushenabled', - 'pushEnabled': pushEnabled, - } - ], - 'keys': [ - { - // The need to use jsonEncode here is a requirement of the - // Sandbox Test API. The capability map has to be JSON encoded - // as a string and then appropriately escaped in order for - // presentation within a string value. - 'capability': jsonEncode(keyCapabilities), - }, - ], - }; + 'namespaces': [ + {'id': 'pushenabled', 'pushEnabled': pushEnabled}, + ], + 'keys': [ + { + // The need to use jsonEncode here is a requirement of the + // Sandbox Test API. The capability map has to be JSON encoded + // as a string and then appropriately escaped in order for + // presentation within a string value. + 'capability': jsonEncode(keyCapabilities), + }, + ], + }; /// Makes request to configured Ably provisioning URL and returns a /// new key for test application instance @@ -102,8 +98,10 @@ class AppProvisioning { if (response.statusCode != HttpStatus.created) { log("Server didn't return success. ${response.body}"); - throw HttpException("Server didn't return success." - ' Status: ${response.statusCode} : ${response.body}'); + throw HttpException( + "Server didn't return success." + ' Status: ${response.statusCode} : ${response.body}', + ); } final responseBody = jsonDecode(response.body) as Map; @@ -122,8 +120,10 @@ class AppProvisioning { if (response.statusCode != HttpStatus.ok) { log("Server didn't return success. ${response.body}"); - throw HttpException("Server didn't return success." - ' Status: ${response.statusCode} : ${response.body}'); + throw HttpException( + "Server didn't return success." + ' Status: ${response.statusCode} : ${response.body}', + ); } return Map.castFrom( diff --git a/example/lib/constants.dart b/example/lib/constants.dart index 6ed2b57ad..474cbb1c1 100644 --- a/example/lib/constants.dart +++ b/example/lib/constants.dart @@ -5,7 +5,7 @@ class Constants { static const String ablyApiKey = 'ABLY_API_KEY'; static String platform = EnumToString.convertToString(defaultTargetPlatform); static String clientId = 'ably-flutter-example-app-$platform-client-id'; - static String sandboxEnvironment = 'sandbox'; + static String sandboxEndpoint = 'nonprod:sandbox'; static const String channelName = 'test-channel'; static const encryptedChannelName = 'encrypted-test-channel'; static const String channelNameForPushNotifications = diff --git a/example/lib/main.dart b/example/lib/main.dart index d3057f547..d076ccf05 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,7 +3,6 @@ import 'package:ably_flutter_example/ui/ably_service.dart'; import 'package:ably_flutter_example/ui/api_key_service.dart'; import 'package:ably_flutter_example/ui/push_notifications/push_notifications_sliver.dart'; import 'package:ably_flutter_example/ui/realtime_sliver.dart'; -import 'package:ably_flutter_example/ui/rest_sliver.dart'; import 'package:ably_flutter_example/ui/system_details_sliver.dart'; import 'package:flutter/material.dart'; @@ -22,33 +21,25 @@ Future main() async { class AblyFlutterExampleApp extends StatelessWidget { final AblyService ablyService; - const AblyFlutterExampleApp({ - required this.ablyService, - Key? key, - }) : super(key: key); + const AblyFlutterExampleApp({required this.ablyService, Key? key}) + : super(key: key); @override Widget build(BuildContext context) => MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Ably Flutter Example App'), - ), - body: Center( - child: ListView( - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 36), - children: [ - SystemDetailsSliver( - apiKeyProvision: ablyService.apiKeyProvision, - ), - const Divider(), - RealtimeSliver(ablyService), - const Divider(), - RestSliver(ablyService.rest), - const Divider(), - PushNotificationsSliver(ablyService.pushNotificationService) - ]), - ), + home: Scaffold( + appBar: AppBar(title: const Text('Ably Flutter Example App')), + body: Center( + child: ListView( + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 36), + children: [ + SystemDetailsSliver(apiKeyProvision: ablyService.apiKeyProvision), + const Divider(), + RealtimeSliver(ablyService), + const Divider(), + PushNotificationsSliver(ablyService.pushNotificationService), + ], ), - ); + ), + ), + ); } diff --git a/example/lib/nested_realtime_events.dart b/example/lib/nested_realtime_events.dart index 574c14758..818842da4 100644 --- a/example/lib/nested_realtime_events.dart +++ b/example/lib/nested_realtime_events.dart @@ -3,28 +3,33 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart' as ably; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; /// This method is a demonstration on how to use multiple listeners /// with realtime connection state change listeners as an example /// /// It aims to demonstrate the working of nested listeners /// and cancelling listeners on specific events -void listenRealtimeConnection(ably.Realtime realtime) { +void listenRealtimeConnection(ably.PubSubClient realtime) { //One can listen from multiple listeners on the same event, // and must cancel each subscription one by one //RETAINING LISTENER - α realtime.connection.on().listen((stateChange) { - print('RETAINING LISTENER α :: Change event arrived!: ${stateChange.event}' - '\nReason: ${stateChange.reason}'); + print( + 'RETAINING LISTENER α :: Change event arrived!: ${stateChange.event}' + '\nReason: ${stateChange.reason}', + ); }); //DISPOSE ON CONNECTED final stream = realtime.connection.on(); late StreamSubscription omegaSubscription; omegaSubscription = stream.listen((stateChange) async { - print('DISPOSABLE LISTENER ω :: Change event arrived!:' - ' ${stateChange.event}'); + print( + 'DISPOSABLE LISTENER ω :: Change event arrived!:' + ' ${stateChange.event}', + ); if (stateChange.event == ably.ConnectionEvent.connected) { await omegaSubscription.cancel(); } @@ -32,8 +37,10 @@ void listenRealtimeConnection(ably.Realtime realtime) { //RETAINING LISTENER - β realtime.connection.on().listen((stateChange) { - print('RETAINING LISTENER β :: Change event arrived!:' - ' ${stateChange.event}'); + print( + 'RETAINING LISTENER β :: Change event arrived!:' + ' ${stateChange.event}', + ); // NESTED LISTENER - ξ // will be registered only when connected event is received by β listener realtime.connection.on().listen((stateChange) { @@ -54,7 +61,8 @@ void listenRealtimeConnection(ably.Realtime realtime) { //RETAINING LISTENER - γ realtime.connection.on().listen((stateChange) async { print( - 'RETAINING LISTENER γ :: Change event arrived!: ${stateChange.event}'); + 'RETAINING LISTENER γ :: Change event arrived!: ${stateChange.event}', + ); if (stateChange.event == ably.ConnectionEvent.connected) { //by the time this cancel is triggered, // preZeta will already have received current event. diff --git a/example/lib/op_state.dart b/example/lib/op_state.dart index 3a2795914..8986ec6c7 100644 --- a/example/lib/op_state.dart +++ b/example/lib/op_state.dart @@ -1,6 +1 @@ -enum OpState { - notStarted, - inProgress, - succeeded, - failed, -} +enum OpState { notStarted, inProgress, succeeded, failed } diff --git a/example/lib/push_notifications/android_push_notification_configuration.dart b/example/lib/push_notifications/android_push_notification_configuration.dart index 248628359..fac28f79a 100644 --- a/example/lib/push_notifications/android_push_notification_configuration.dart +++ b/example/lib/push_notifications/android_push_notification_configuration.dart @@ -18,7 +18,8 @@ class AndroidPushNotificationConfiguration { await _flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin + >() ?.createNotificationChannel(channel); } } diff --git a/example/lib/push_notifications/push_notification_handlers.dart b/example/lib/push_notifications/push_notification_handlers.dart index 7dbbcc9c7..f688a8beb 100644 --- a/example/lib/push_notifications/push_notification_handlers.dart +++ b/example/lib/push_notifications/push_notification_handlers.dart @@ -1,5 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/ui/utilities.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; @@ -9,7 +10,7 @@ class PushNotificationHandlers { static final notificationEvents = ably.Push.notificationEvents; static final BehaviorSubject> - _receivedMessagesBehaviorSubject = + _receivedMessagesBehaviorSubject = BehaviorSubject>.seeded([]); static ValueStream> receivedMessagesStream = @@ -17,8 +18,10 @@ class PushNotificationHandlers { static void setUpEventHandlers() { activationEvents.onUpdateFailed.listen((error) async { - logAndDisplayError(error, - prefixMessage: 'Push update registration failed'); + logAndDisplayError( + error, + prefixMessage: 'Push update registration failed', + ); }); activationEvents.onActivate.listen((error) async { logAndDisplayError(error, prefixMessage: 'Push activation failed'); @@ -39,14 +42,17 @@ class PushNotificationHandlers { notificationEvents.onMessage.listen((message) { addMessage(message); - print('RemoteMessage received while app is in foreground:\n' - 'RemoteMessage.Notification: ${message.notification}' - 'RemoteMessage.Data: ${message.data}'); + print( + 'RemoteMessage received while app is in foreground:\n' + 'RemoteMessage.Notification: ${message.notification}' + 'RemoteMessage.Data: ${message.data}', + ); }); notificationEvents.setOnShowNotificationInForeground((message) async { print( - 'Opting to show the notification when the app is in the foreground.'); + 'Opting to show the notification when the app is in the foreground.', + ); return true; }); @@ -58,8 +64,9 @@ class PushNotificationHandlers { /// You can get the notification which launched the app by a user tapping it. static void getLaunchMessage() { - notificationEvents.notificationTapLaunchedAppFromTerminated - .then((remoteMessage) { + notificationEvents.notificationTapLaunchedAppFromTerminated.then(( + remoteMessage, + ) { if (remoteMessage != null) { addMessage(remoteMessage); print('The app was launched by the user by tapping the notification'); @@ -73,11 +80,14 @@ class PushNotificationHandlers { } static Future _backgroundMessageHandler( - ably.RemoteMessage message) async { + ably.RemoteMessage message, + ) async { addMessage(message); - print('RemoteMessage received while app is in background:\n' - 'RemoteMessage.Notification: ${message.notification}' - 'RemoteMessage.Data: ${message.data}'); + print( + 'RemoteMessage received while app is in background:\n' + 'RemoteMessage.Notification: ${message.notification}' + 'RemoteMessage.Data: ${message.data}', + ); } static void addMessage(ably.RemoteMessage message) { diff --git a/example/lib/push_notifications/push_notification_message_examples.dart b/example/lib/push_notifications/push_notification_message_examples.dart index abc51b8c6..24f78115f 100644 --- a/example/lib/push_notifications/push_notification_message_examples.dart +++ b/example/lib/push_notifications/push_notification_message_examples.dart @@ -1,49 +1,56 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; class PushNotificationMessageExamples { static final ably.Message pushNotificationMessage = ably.Message( - data: 'This is an Ably message published on channels that is also sent ' - 'as a notification message to registered push devices.', - extras: const ably.MessageExtras({ - 'push': { - 'notification': { - 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.', - 'sound': 'default' - }, + data: + 'This is an Ably message published on channels that is also sent ' + 'as a notification message to registered push devices.', + extras: const ably.MessageExtras({ + 'push': { + 'notification': { + 'title': 'Hello from Ably!', + 'body': 'Example push notification from Ably.', + 'sound': 'default', }, - })); + }, + }), + ); static ably.Message pushDataMessage = ably.Message( - data: 'This is a Ably message published on channels that is also ' - 'sent as a data message to registered push devices.', - extras: const ably.MessageExtras({ - 'push': { - 'data': {'foo': 'bar', 'baz': 'quz'}, - 'apns': { - 'apns-headers': { - 'apns-push-type': 'background', - 'apns-priority': '5', - }, - 'aps': {'content-available': 1} - } + data: + 'This is a Ably message published on channels that is also ' + 'sent as a data message to registered push devices.', + extras: const ably.MessageExtras({ + 'push': { + 'data': {'foo': 'bar', 'baz': 'quz'}, + 'apns': { + 'apns-headers': { + 'apns-push-type': 'background', + 'apns-priority': '5', + }, + 'aps': {'content-available': 1}, }, - })); + }, + }), + ); static ably.Message pushDataNotificationMessage = ably.Message( - data: 'This is a Ably message published on channels that is also ' - 'sent as a data message to registered push devices.', - extras: const ably.MessageExtras({ - 'push': { - 'notification': { - 'title': 'Hello from Ably!', - 'body': 'Example push notification from Ably.', - 'sound': 'default' - }, - 'data': {'foo': 'bar', 'baz': 'quz'}, - 'apns': { - 'aps': {'content-available': 1} - } + data: + 'This is a Ably message published on channels that is also ' + 'sent as a data message to registered push devices.', + extras: const ably.MessageExtras({ + 'push': { + 'notification': { + 'title': 'Hello from Ably!', + 'body': 'Example push notification from Ably.', + 'sound': 'default', + }, + 'data': {'foo': 'bar', 'baz': 'quz'}, + 'apns': { + 'aps': {'content-available': 1}, }, - })); + }, + }), + ); } diff --git a/example/lib/push_notifications/push_notification_service.dart b/example/lib/push_notifications/push_notification_service.dart index 4769a7e64..0161962fb 100644 --- a/example/lib/push_notifications/push_notification_service.dart +++ b/example/lib/push_notifications/push_notification_service.dart @@ -1,26 +1,20 @@ import 'dart:async'; import 'dart:io'; -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/constants.dart'; import 'package:ably_flutter_example/push_notifications/android_push_notification_configuration.dart'; import 'package:ably_flutter_example/push_notifications/push_notification_message_examples.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:rxdart/rxdart.dart'; class PushNotificationService { - final bool useRealtimeClient; - final ably.Realtime _realtime; - final ably.Rest _rest; + final ably.PubSubClient _realtime; late ably.RealtimeChannel? _realtimeChannel; late ably.RealtimeChannel? _pushLogMetaChannel; - late ably.RestChannel? _restChannel; late ably.PushChannel? _pushChannel; - PushNotificationService( - this._realtime, - this._rest, { - this.useRealtimeClient = true, - }) { + PushNotificationService(this._realtime) { _getChannels(); getDevice(); if (Platform.isIOS) { @@ -30,20 +24,20 @@ class PushNotificationService { } final BehaviorSubject> - _pushChannelDeviceSubscriptionsSubject = + _pushChannelDeviceSubscriptionsSubject = BehaviorSubject>(); ValueStream> - get pushChannelDeviceSubscriptionsStream => - _pushChannelDeviceSubscriptionsSubject.stream; + get pushChannelDeviceSubscriptionsStream => + _pushChannelDeviceSubscriptionsSubject.stream; final BehaviorSubject> - _pushChannelClientSubscriptionsSubject = + _pushChannelClientSubscriptionsSubject = BehaviorSubject>(); ValueStream> - get pushChannelClientSubscriptionsStream => - _pushChannelClientSubscriptionsSubject.stream; + get pushChannelClientSubscriptionsStream => + _pushChannelClientSubscriptionsSubject.stream; final BehaviorSubject _hasPushChannelSubject = BehaviorSubject.seeded(false); @@ -61,9 +55,9 @@ class PushNotificationService { _userNotificationPermissionGrantedSubject.stream; final BehaviorSubject - _notificationSettingsSubject = BehaviorSubject(); + _notificationSettingsSubject = BehaviorSubject(); late final ValueStream - notificationSettingsStream = _notificationSettingsSubject.stream; + notificationSettingsStream = _notificationSettingsSubject.stream; Future ensureRealtimeClientConnected() async { if (_realtime.connection.state != ably.ConnectionState.connected) { @@ -72,57 +66,39 @@ class PushNotificationService { } /// Only valid on iOS - Future requestNotificationPermission( - {bool provisional = false, - bool providesAppNotificationSettings = true}) async { - if (useRealtimeClient) { - final granted = await _realtime.push.requestPermission( - provisional: provisional, - providesAppNotificationSettings: providesAppNotificationSettings); - _userNotificationPermissionGrantedSubject.add(granted); - } else { - final granted = await _rest.push.requestPermission( - provisional: provisional, - providesAppNotificationSettings: providesAppNotificationSettings); - _userNotificationPermissionGrantedSubject.add(granted); - } + Future requestNotificationPermission({ + bool provisional = false, + bool providesAppNotificationSettings = true, + }) async { + final granted = await _realtime.push.requestPermission( + provisional: provisional, + providesAppNotificationSettings: providesAppNotificationSettings, + ); + _userNotificationPermissionGrantedSubject.add(granted); await updateNotificationSettings(); } /// Only valid on iOS Future updateNotificationSettings() async { - if (useRealtimeClient) { - final settings = await _realtime.push.getNotificationSettings(); - _notificationSettingsSubject.add(settings); - } else { - _notificationSettingsSubject - .add(await _rest.push.getNotificationSettings()); - } + final settings = await _realtime.push.getNotificationSettings(); + _notificationSettingsSubject.add(settings); } Future activateDevice() => getPushFromAblyClient().activate(); Future deactivateDevice() => getPushFromAblyClient().deactivate(); + // `reset` is deliberately exercised here despite being experimental: this + // example app is how we manually verify it. + // ignore: experimental_member_use Future resetActivation() => getPushFromAblyClient().reset(); Future getDevice() async { - if (useRealtimeClient) { - final localDevice = await _realtime.device(); - _localDeviceSubject.add(localDevice); - } else { - final localDevice = await _rest.device(); - _localDeviceSubject.add(localDevice); - } + final localDevice = await _realtime.device(); + _localDeviceSubject.add(localDevice); } - ably.Push getPushFromAblyClient() { - if (useRealtimeClient) { - return _realtime.push; - } else { - return _rest.push; - } - } + ably.Push getPushFromAblyClient() => _realtime.push; /// Subscribes to the channel (not the push channel) which has a Push channel /// rule. This allows the device to receive push notifications when @@ -161,35 +137,23 @@ class PushNotificationService { Future publishNotificationMessageToChannel() async { await ensureRealtimeClientConnected(); - if (useRealtimeClient) { - await _realtimeChannel!.publish( - message: PushNotificationMessageExamples.pushNotificationMessage); - } else { - await _restChannel!.publish( - message: PushNotificationMessageExamples.pushNotificationMessage); - } + await _realtimeChannel!.publish( + message: PushNotificationMessageExamples.pushNotificationMessage, + ); } Future publishDataMessageToChannel() async { await ensureRealtimeClientConnected(); - if (useRealtimeClient) { - await _realtimeChannel! - .publish(message: PushNotificationMessageExamples.pushDataMessage); - } else { - await _restChannel! - .publish(message: PushNotificationMessageExamples.pushDataMessage); - } + await _realtimeChannel!.publish( + message: PushNotificationMessageExamples.pushDataMessage, + ); } Future publishDataNotificationMessageToChannel() async { await ensureRealtimeClientConnected(); - if (useRealtimeClient) { - await _realtimeChannel!.publish( - message: PushNotificationMessageExamples.pushDataNotificationMessage); - } else { - await _restChannel!.publish( - message: PushNotificationMessageExamples.pushDataNotificationMessage); - } + await _realtimeChannel!.publish( + message: PushNotificationMessageExamples.pushDataNotificationMessage, + ); } void close() { @@ -204,19 +168,12 @@ class PushNotificationService { void _getChannels() { _hasPushChannelSubject.add(false); - if (useRealtimeClient) { - _realtimeChannel = - _realtime.channels.get(Constants.channelNameForPushNotifications); - _pushChannel = _realtimeChannel!.push; - _pushLogMetaChannel = - _realtime.channels.get(Constants.pushMetaChannelName); - _hasPushChannelSubject.add(true); - } else { - _restChannel = - _rest.channels.get(Constants.channelNameForPushNotifications); - _pushChannel = _restChannel!.push; - _hasPushChannelSubject.add(true); - } + _realtimeChannel = _realtime.channels.get( + Constants.channelNameForPushNotifications, + ); + _pushChannel = _realtimeChannel!.push; + _pushLogMetaChannel = _realtime.channels.get(Constants.pushMetaChannelName); + _hasPushChannelSubject.add(true); } /// Unfortunately ably-cocoa and ably-java are inconsistent here. @@ -224,7 +181,7 @@ class PushNotificationService { /// ably-cocoa will only give the one you specify in params. /// This behavior is the same for [listSubscriptionsWithDeviceId] Future> - listSubscriptionsWithClientId() async { + listSubscriptionsWithClientId() async { await getDevice(); final subscriptions = await _pushChannel!.listSubscriptions({ 'clientId': localDeviceStream.value!.clientId!, @@ -236,7 +193,7 @@ class PushNotificationService { } Future> - listSubscriptionsWithDeviceId() async { + listSubscriptionsWithDeviceId() async { await getDevice(); final subscriptions = await _pushChannel!.listSubscriptions({ 'deviceId': localDeviceStream.value!.id!, diff --git a/example/lib/ui/ably_service.dart b/example/lib/ui/ably_service.dart index d812df3d9..5f2788ecb 100644 --- a/example/lib/ui/ably_service.dart +++ b/example/lib/ui/ably_service.dart @@ -1,62 +1,56 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/constants.dart'; import 'package:ably_flutter_example/push_notifications/push_notification_service.dart'; import 'package:ably_flutter_example/ui/api_key_service.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; class AblyService { - late final ably.Realtime realtime; - late final ably.Rest rest; + late final ably.PubSubClient realtime; late final PushNotificationService pushNotificationService; late final ApiKeyProvision apiKeyProvision; AblyService({required this.apiKeyProvision}) { - realtime = ably.Realtime( + realtime = ably.createClient( options: ably.ClientOptions( key: apiKeyProvision.key, clientId: Constants.clientId, logLevel: ably.LogLevel.verbose, - environment: apiKeyProvision.source == ApiKeySource.env + endpoint: apiKeyProvision.source == ApiKeySource.env ? null - : Constants.sandboxEnvironment, + : Constants.sandboxEndpoint, autoConnect: false, ), ); - rest = ably.Rest( - options: ably.ClientOptions( - key: apiKeyProvision.key, - clientId: Constants.clientId, - logLevel: ably.LogLevel.verbose, - environment: apiKeyProvision.source == ApiKeySource.env - ? null - : Constants.sandboxEnvironment, - ), - ); - pushNotificationService = PushNotificationService(realtime, rest); + pushNotificationService = PushNotificationService(realtime); } } class ExampleMessages { - static ably.Message message = ably.Message(data: const { - 'I am': null, - 'and': { - 'also': 'nested', - 'too': {'deep': true} - } - }); + static ably.Message message = ably.Message( + data: const { + 'I am': null, + 'and': { + 'also': 'nested', + 'too': {'deep': true}, + }, + }, + ); static List messages = [ ably.Message(data: 42), ably.Message(data: const {'are': 'you'}), ably.Message(data: 'ok?'), - ably.Message(data: const [ - false, - { - 'I am': null, - 'and': { - 'also': 'nested', - 'too': {'deep': true} - } - } - ]), + ably.Message( + data: const [ + false, + { + 'I am': null, + 'and': { + 'also': 'nested', + 'too': {'deep': true}, + }, + }, + ], + ), ]; } diff --git a/example/lib/ui/api_key_service.dart b/example/lib/ui/api_key_service.dart index f5d54518f..951e4c3ac 100644 --- a/example/lib/ui/api_key_service.dart +++ b/example/lib/ui/api_key_service.dart @@ -8,10 +8,7 @@ class ApiKeyService { Future getOrProvisionApiKey() async { if (envKey.isNotEmpty && envKey != defaultEnvKeyPlaceholder) { - return ApiKeyProvision( - key: envKey, - source: ApiKeySource.env, - ); + return ApiKeyProvision(key: envKey, source: ApiKeySource.env); } else { final provisionedKey = await AppProvisioning().provisionApp(); return ApiKeyProvision( @@ -27,13 +24,7 @@ class ApiKeyProvision { ApiKeySource source; - ApiKeyProvision({ - required this.key, - required this.source, - }); + ApiKeyProvision({required this.key, required this.source}); } -enum ApiKeySource { - env, - testProvision, -} +enum ApiKeySource { env, testProvision } diff --git a/example/lib/ui/auth.dart b/example/lib/ui/auth.dart index 30c3b1aaa..d0d87ac1b 100644 --- a/example/lib/ui/auth.dart +++ b/example/lib/ui/auth.dart @@ -16,20 +16,23 @@ Future> getTokenDetails( ]) async { final stringToBase64 = utf8.fuse(base64); final encoded = stringToBase64.encode('$keyName:$keySecret'); - final r = await const RetryOptions( - maxAttempts: 5, - delayFactor: Duration(seconds: 2), - ).retry(() => http.post( - Uri.parse(_tokenDetailsURL(keyName, prefix)), - headers: { - 'Authorization': 'Basic $encoded', - 'Content-Type': 'application/json', - }, - body: json.encode({ - 'keyName': keyName, - 'timestamp': DateTime.now().millisecondsSinceEpoch, - }), - )); + final r = + await const RetryOptions( + maxAttempts: 5, + delayFactor: Duration(seconds: 2), + ).retry( + () => http.post( + Uri.parse(_tokenDetailsURL(keyName, prefix)), + headers: { + 'Authorization': 'Basic $encoded', + 'Content-Type': 'application/json', + }, + body: json.encode({ + 'keyName': keyName, + 'timestamp': DateTime.now().millisecondsSinceEpoch, + }), + ), + ); print('tokenDetails from server: ${r.body}'); return Map.castFrom( jsonDecode(r.body) as Map, diff --git a/example/lib/ui/bool_stream_button.dart b/example/lib/ui/bool_stream_button.dart index bd1dd0a5a..8c4514879 100644 --- a/example/lib/ui/bool_stream_button.dart +++ b/example/lib/ui/bool_stream_button.dart @@ -38,11 +38,10 @@ class BoolStreamButton extends StatelessWidget { @override Widget build(BuildContext context) => StreamBuilder( - stream: (streams != null) ? combineStreams(streams!) : stream, - builder: (context, snapshot) => TextButton( - onPressed: - (snapshot.hasData && snapshot.data == true) ? onPressed : null, - child: child, - ), - ); + stream: (streams != null) ? combineStreams(streams!) : stream, + builder: (context, snapshot) => TextButton( + onPressed: (snapshot.hasData && snapshot.data == true) ? onPressed : null, + child: child, + ), + ); } diff --git a/example/lib/ui/paginated_result_viewer.dart b/example/lib/ui/paginated_result_viewer.dart index 8be6538ca..59e1a10d8 100644 --- a/example/lib/ui/paginated_result_viewer.dart +++ b/example/lib/ui/paginated_result_viewer.dart @@ -1,5 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/ui/text_row.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -24,8 +25,9 @@ class PaginatedResultViewer extends HookWidget { }) : super(key: key); Future getFirstPaginatedResult( - ValueNotifier?> currentPaginatedResult, - ValueNotifier pageNumber) async { + ValueNotifier?> currentPaginatedResult, + ValueNotifier pageNumber, + ) async { final result = await query(); firstPaginatedResult = result; currentPaginatedResult.value = result; @@ -43,9 +45,9 @@ class PaginatedResultViewer extends HookWidget { return; } if (pageNumber.value == 1) { - currentPaginatedResult.value! - .first() - .then((result) => currentPaginatedResult.value = result); + currentPaginatedResult.value!.first().then( + (result) => currentPaginatedResult.value = result, + ); } else if (currentPaginatedResult.value!.hasNext()) { currentPaginatedResult.value!.next().then((result) { currentPaginatedResult.value = result; @@ -73,7 +75,7 @@ class PaginatedResultViewer extends HookWidget { onPressed: () => getFirstPaginatedResult(currentPaginatedResult, pageNumber), icon: const Icon(Icons.refresh), - ) + ), ], ), if (subtitle != null) @@ -88,19 +90,22 @@ class PaginatedResultViewer extends HookWidget { children: (items.isEmpty) ? [const Text('No messages')] : items - .map((item) => Padding( + .map( + (item) => Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: builder(context, item, null), - )) - .toList(), + ), + ) + .toList(), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextRow('Page', '#${pageNumber.value}'), TextButton( - onPressed: - pageNumber.value != 1 ? () => pageNumber.value = 1 : null, + onPressed: pageNumber.value != 1 + ? () => pageNumber.value = 1 + : null, child: const Text('Go to first page'), ), TextButton( @@ -112,7 +117,7 @@ class PaginatedResultViewer extends HookWidget { child: const Text('Next page'), ), ], - ) + ), ], ); } diff --git a/example/lib/ui/push_notifications/push_notifications_activation_sliver.dart b/example/lib/ui/push_notifications/push_notifications_activation_sliver.dart index 3d7725a8a..b05f7dc25 100644 --- a/example/lib/ui/push_notifications/push_notifications_activation_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_activation_sliver.dart @@ -1,8 +1,9 @@ import 'dart:io'; -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/push_notifications/push_notification_service.dart'; import 'package:ably_flutter_example/ui/bool_stream_button.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -15,20 +16,23 @@ class PushNotificationsActivationSliver extends HookWidget { const PushNotificationsActivationSliver({ required PushNotificationService pushNotificationService, Key? key, - }) : _pushNotificationService = pushNotificationService, - super(key: key); + }) : _pushNotificationService = pushNotificationService, + super(key: key); Future showErrorDialog( - BuildContext context, ably.AblyException error) async { + BuildContext context, + ably.AblyException error, + ) async { await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Push notification error'), - content: - Text('Failed to perform operation on push notification service. ' - 'Please verify your push notification setup with Readme ' - 'files in the ably-flutter repository.\n\n' - '${error.errorInfo?.message}'), + content: Text( + 'Failed to perform operation on push notification service. ' + 'Please verify your push notification setup with Readme ' + 'files in the ably-flutter repository.\n\n' + '${error.errorInfo?.message}', + ), ), ); } @@ -66,16 +70,25 @@ class PushNotificationsActivationSliver extends HookWidget { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: RichText( - text: const TextSpan(children: [ - TextSpan( - text: 'Warning: ', - style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold)), - TextSpan( - text: 'APNs is not available on iOS simulators, so you cannot ' - 'activate the device Ably, since this step requires the' - ' APNs device token.', - style: TextStyle(color: Colors.black)) - ])), + text: const TextSpan( + children: [ + TextSpan( + text: 'Warning: ', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: + 'APNs is not available on iOS simulators, so you cannot ' + 'activate the device Ably, since this step requires the' + ' APNs device token.', + style: TextStyle(color: Colors.black), + ), + ], + ), + ), ); } else { return const SizedBox.shrink(); @@ -87,9 +100,9 @@ class PushNotificationsActivationSliver extends HookWidget { final isIOSSimulator = useState(false); useEffect(() { if (Platform.isIOS) { - DeviceInfoPlugin() - .iosInfo - .then((info) => isIOSSimulator.value = !info.isPhysicalDevice); + DeviceInfoPlugin().iosInfo.then( + (info) => isIOSSimulator.value = !info.isPhysicalDevice, + ); } return; }); @@ -108,29 +121,34 @@ class PushNotificationsActivationSliver extends HookWidget { children: [ Expanded( child: BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: () => handleActivateDeviceButton(context), - child: const Text('Activate device')), + stream: _pushNotificationService.hasPushChannelStream, + onPressed: () => handleActivateDeviceButton(context), + child: const Text('Activate device'), + ), ), Expanded( child: BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: () => handleDeactivateDeviceButton(context), - child: const Text('Deactivate device')), + stream: _pushNotificationService.hasPushChannelStream, + onPressed: () => handleDeactivateDeviceButton(context), + child: const Text('Deactivate device'), + ), ), Expanded( child: BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: () => handleResetDeviceButton(context), - child: const Text('Reset activation')), - ) + stream: _pushNotificationService.hasPushChannelStream, + onPressed: () => handleResetDeviceButton(context), + child: const Text('Reset activation'), + ), + ), ], ), - const Text('Once devices are activated, a Push Admin can ' - "send push messages to devices by it's device ID, client ID or " - 'FCM/ APNs token. To send push messages between users, the ' - 'device must push-subscribe to the channel and a push payload ' - 'is added to the channel message.'), + const Text( + 'Once devices are activated, a Push Admin can ' + "send push messages to devices by it's device ID, client ID or " + 'FCM/ APNs token. To send push messages between users, the ' + 'device must push-subscribe to the channel and a push payload ' + 'is added to the channel message.', + ), ], ), ); @@ -139,20 +157,24 @@ class PushNotificationsActivationSliver extends HookWidget { Future showPermissionReminder(BuildContext context) async { if (Platform.isIOS) { final authorizationStatus = _pushNotificationService - .notificationSettingsStream.value.authorizationStatus; + .notificationSettingsStream + .value + .authorizationStatus; if (authorizationStatus == ably.UNAuthorizationStatus.notDetermined) { await showDialog( context: context, builder: (context) => CupertinoAlertDialog( title: const Text('Reminder for the developer'), content: const Text( - 'You should request permission to show notifications to the ' - 'user or a provisional permissions.'), + 'You should request permission to show notifications to the ' + 'user or a provisional permissions.', + ), actions: [ CupertinoDialogAction( onPressed: () { _pushNotificationService.requestNotificationPermission( - provisional: true); + provisional: true, + ); Navigator.pop(context); }, child: const Text('Request provisional permission'), @@ -173,12 +195,13 @@ class PushNotificationsActivationSliver extends HookWidget { ); } else if (authorizationStatus == ably.UNAuthorizationStatus.denied) { await Fluttertoast.showToast( - msg: 'The user has previously denied notifications from this app.', - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.CENTER, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16); + msg: 'The user has previously denied notifications from this app.', + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); } } } diff --git a/example/lib/ui/push_notifications/push_notifications_device_information.dart b/example/lib/ui/push_notifications/push_notifications_device_information.dart index a4e85652b..1ca8d919f 100644 --- a/example/lib/ui/push_notifications/push_notifications_device_information.dart +++ b/example/lib/ui/push_notifications/push_notifications_device_information.dart @@ -1,6 +1,7 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/push_notifications/push_notification_service.dart'; import 'package:ably_flutter_example/ui/text_row.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; class PushNotificationsDeviceInformation extends StatelessWidget { @@ -9,56 +10,67 @@ class PushNotificationsDeviceInformation extends StatelessWidget { const PushNotificationsDeviceInformation({ required PushNotificationService pushNotificationService, Key? key, - }) : _pushNotificationService = pushNotificationService, - super(key: key); + }) : _pushNotificationService = pushNotificationService, + super(key: key); @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Text( - 'Local Device information', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - StreamBuilder( - stream: _pushNotificationService.localDeviceStream, - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data != null) { - final localDevice = snapshot.data!; - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextRow('deviceId', localDevice.id), - TextRow('clientId', localDevice.clientId), - TextRow('platform', localDevice.platform.toString()), - TextRow('formFactor', localDevice.formFactor.toString()), - TextRow('metadata', localDevice.metadata.toString()), - TextRow('DevicePushDetails state', - localDevice.push.state.toString()), - TextRow('DevicePushDetails recipient', - '${localDevice.push.recipient}'), - TextButton( - onPressed: _pushNotificationService.getDevice, - child: const Text('Refresh local device information'), - ), - ], - ); - } - return RichText( - text: const TextSpan(children: [ + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + 'Local Device information', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + StreamBuilder( + stream: _pushNotificationService.localDeviceStream, + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data != null) { + final localDevice = snapshot.data!; + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextRow('deviceId', localDevice.id), + TextRow('clientId', localDevice.clientId), + TextRow('platform', localDevice.platform.toString()), + TextRow('formFactor', localDevice.formFactor.toString()), + TextRow('metadata', localDevice.metadata.toString()), + TextRow( + 'DevicePushDetails state', + localDevice.push.state.toString(), + ), + TextRow( + 'DevicePushDetails recipient', + '${localDevice.push.recipient}', + ), + TextButton( + onPressed: _pushNotificationService.getDevice, + child: const Text('Refresh local device information'), + ), + ], + ); + } + return RichText( + text: const TextSpan( + children: [ TextSpan( - text: 'Warning: ', - style: TextStyle( - color: Colors.red, fontWeight: FontWeight.bold)), + text: 'Warning: ', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), TextSpan( - text: 'no local device information yet.', - style: TextStyle(color: Colors.black)) - ])); - }, - ), - ], + text: 'no local device information yet.', + style: TextStyle(color: Colors.black), + ), + ], + ), + ); + }, ), - ); + ], + ), + ); } diff --git a/example/lib/ui/push_notifications/push_notifications_ios_permissions_sliver.dart b/example/lib/ui/push_notifications/push_notifications_ios_permissions_sliver.dart index 54b985a57..9906f122d 100644 --- a/example/lib/ui/push_notifications/push_notifications_ios_permissions_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_ios_permissions_sliver.dart @@ -1,9 +1,10 @@ import 'dart:io'; -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/push_notifications/push_notification_service.dart'; import 'package:ably_flutter_example/ui/bool_stream_button.dart'; import 'package:ably_flutter_example/ui/text_row.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -13,8 +14,8 @@ class PushNotificationsIOSNotificationSettingsSliver extends StatelessWidget { const PushNotificationsIOSNotificationSettingsSliver({ required PushNotificationService pushNotificationService, Key? key, - }) : _pushNotificationService = pushNotificationService, - super(key: key); + }) : _pushNotificationService = pushNotificationService, + super(key: key); Widget buildIOSPermissionSliver() { if (Platform.isIOS) { @@ -42,15 +43,18 @@ class PushNotificationsIOSNotificationSettingsSliver extends StatelessWidget { stream: _pushNotificationService.hasPushChannelStream, onPressed: () { _pushNotificationService.requestNotificationPermission( - provisional: true); + provisional: true, + ); Fluttertoast.showToast( - msg: 'Notifications will be delivered silently to ' - 'the notification center.', - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.CENTER, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16); + msg: + 'Notifications will be delivered silently to ' + 'the notification center.', + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); }, child: const Text('Request Provisional Permission (no alert)'), ); @@ -80,22 +84,32 @@ class PushNotificationsIOSNotificationSettingsSliver extends StatelessWidget { 'iOS Notification Settings', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), - TextRow('Authorization Status', - '${settings.authorizationStatus}'), + TextRow( + 'Authorization Status', + '${settings.authorizationStatus}', + ), TextRow('Sound', '${settings.soundSetting}'), TextRow('Badge', '${settings.badgeSetting}'), TextRow('Alert', '${settings.alertSetting}'), - TextRow('Notification Center', - '${settings.notificationCenterSetting}'), + TextRow( + 'Notification Center', + '${settings.notificationCenterSetting}', + ), TextRow('Lock Screen', '${settings.lockScreenSetting}'), TextRow('Alert Style', '${settings.alertStyle}'), TextRow('Shows Preview', '${settings.showPreviewsSetting}'), TextRow( - 'Critical Alerts', '${settings.criticalAlertSetting}'), - TextRow('providesAppNotificationSettings', - '${settings.providesAppNotificationSettings}'), + 'Critical Alerts', + '${settings.criticalAlertSetting}', + ), TextRow( - 'Siri announcements', '${settings.announcementSetting}'), + 'providesAppNotificationSettings', + '${settings.providesAppNotificationSettings}', + ), + TextRow( + 'Siri announcements', + '${settings.announcementSetting}', + ), TextButton( onPressed: _pushNotificationService.updateNotificationSettings, @@ -114,9 +128,6 @@ class PushNotificationsIOSNotificationSettingsSliver extends StatelessWidget { @override Widget build(BuildContext context) => Column( - children: [ - buildIOSPermissionSliver(), - buildIOSNotificationSettings(), - ], - ); + children: [buildIOSPermissionSliver(), buildIOSNotificationSettings()], + ); } diff --git a/example/lib/ui/push_notifications/push_notifications_publishing_sliver.dart b/example/lib/ui/push_notifications/push_notifications_publishing_sliver.dart index d29e2ee8b..788b146a4 100644 --- a/example/lib/ui/push_notifications/push_notifications_publishing_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_publishing_sliver.dart @@ -10,48 +10,49 @@ class PushNotificationsPublishingSliver extends StatelessWidget { const PushNotificationsPublishingSliver({ required PushNotificationService pushNotificationService, Key? key, - }) : _pushNotificationService = pushNotificationService, - super(key: key); + }) : _pushNotificationService = pushNotificationService, + super(key: key); @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + 'Publishing to ' + '"${Constants.channelNameForPushNotifications}"', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Text( + 'Each message sent contains a push payload, ' + 'in the ably.Message.extras field.', + ), + ), + Row( children: [ - const Text( - 'Publishing to ' - '"${Constants.channelNameForPushNotifications}"', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 8), - child: Text('Each message sent contains a push payload, ' - 'in the ably.Message.extras field.'), - ), - Row( - children: [ - BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: _pushNotificationService - .publishNotificationMessageToChannel, - child: const Text('Notification Message'), - ), - BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: - _pushNotificationService.publishDataMessageToChannel, - child: const Text('Data Message'), - ), - ], + BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: + _pushNotificationService.publishNotificationMessageToChannel, + child: const Text('Notification Message'), ), BoolStreamButton( stream: _pushNotificationService.hasPushChannelStream, - onPressed: _pushNotificationService - .publishDataNotificationMessageToChannel, - child: const Text('Data + Notification Message'), + onPressed: _pushNotificationService.publishDataMessageToChannel, + child: const Text('Data Message'), ), ], ), - ); + BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: + _pushNotificationService.publishDataNotificationMessageToChannel, + child: const Text('Data + Notification Message'), + ), + ], + ), + ); } diff --git a/example/lib/ui/push_notifications/push_notifications_received_sliver.dart b/example/lib/ui/push_notifications/push_notifications_received_sliver.dart index 1b5b9a663..d93522047 100644 --- a/example/lib/ui/push_notifications/push_notifications_received_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_received_sliver.dart @@ -1,63 +1,69 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/push_notifications/push_notification_handlers.dart'; import 'package:ably_flutter_example/ui/text_row.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; class PushNotificationsReceivedSliver extends StatelessWidget { - const PushNotificationsReceivedSliver({ - Key? key, - }) : super(key: key); + const PushNotificationsReceivedSliver({Key? key}) : super(key: key); @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Received messages', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - IconButton( - onPressed: PushNotificationHandlers.clearReceivedMessages, - icon: Icon(Icons.delete)) - ], + Text( + 'Received messages', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + IconButton( + onPressed: PushNotificationHandlers.clearReceivedMessages, + icon: Icon(Icons.delete), ), - StreamBuilder>( - stream: PushNotificationHandlers.receivedMessagesStream, - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Text('Widget not loaded yet.'); - } - final messages = snapshot.data; - if (messages!.isEmpty) { - return const Text('No messages yet'); - } else { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: messages - .map((e) => Padding( - padding: - const EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextRow('Notification Title', - e.notification?.title ?? 'NO TITLE'), - TextRow('Notification Body', - e.notification?.body ?? 'NO BODY'), - TextRow('Data', e.data.toString()), - ], - ), - )) - .toList(), - ); - } - }), ], ), - ); + StreamBuilder>( + stream: PushNotificationHandlers.receivedMessagesStream, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Text('Widget not loaded yet.'); + } + final messages = snapshot.data; + if (messages!.isEmpty) { + return const Text('No messages yet'); + } else { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: messages + .map( + (e) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextRow( + 'Notification Title', + e.notification?.title ?? 'NO TITLE', + ), + TextRow( + 'Notification Body', + e.notification?.body ?? 'NO BODY', + ), + TextRow('Data', e.data.toString()), + ], + ), + ), + ) + .toList(), + ); + } + }, + ), + ], + ), + ); } diff --git a/example/lib/ui/push_notifications/push_notifications_sliver.dart b/example/lib/ui/push_notifications/push_notifications_sliver.dart index 0653c4d62..2da18dcf1 100644 --- a/example/lib/ui/push_notifications/push_notifications_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_sliver.dart @@ -12,49 +12,51 @@ class PushNotificationsSliver extends StatelessWidget { final PushNotificationService _pushNotificationService; const PushNotificationsSliver(this._pushNotificationService, {Key? key}) - : super(key: key); + : super(key: key); @override Widget build(BuildContext context) => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 16), - child: Text( - 'Push Notifications', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - ), - buildSummaryText(), - PushNotificationsActivationSliver( - pushNotificationService: _pushNotificationService, - ), - PushNotificationsDeviceInformation( - pushNotificationService: _pushNotificationService, - ), - PushNotificationsIOSNotificationSettingsSliver( - pushNotificationService: _pushNotificationService, - ), - PushNotificationsSubscriptionsSliver( - pushNotificationService: _pushNotificationService, - ), - PushNotificationsPublishingSliver( - pushNotificationService: _pushNotificationService), - const PushNotificationsReceivedSliver(), - PushRealtimeClientReceivedSliver( - pushNotificationService: _pushNotificationService, - ), - ], - ); + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 16), + child: Text( + 'Push Notifications', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + ), + buildSummaryText(), + PushNotificationsActivationSliver( + pushNotificationService: _pushNotificationService, + ), + PushNotificationsDeviceInformation( + pushNotificationService: _pushNotificationService, + ), + PushNotificationsIOSNotificationSettingsSliver( + pushNotificationService: _pushNotificationService, + ), + PushNotificationsSubscriptionsSliver( + pushNotificationService: _pushNotificationService, + ), + PushNotificationsPublishingSliver( + pushNotificationService: _pushNotificationService, + ), + const PushNotificationsReceivedSliver(), + PushRealtimeClientReceivedSliver( + pushNotificationService: _pushNotificationService, + ), + ], + ); Widget buildSummaryText() => const Column( - children: [ - Text( - 'Activate your device, view your local device information, ' - 'subscribe to a push channel with either your device or ' - 'client ID, and then publish to the channel.', - style: TextStyle(color: Colors.black)), - SizedBox(height: 16), - ], - ); + children: [ + Text( + 'Activate your device, view your local device information, ' + 'subscribe to a push channel with either your device or ' + 'client ID, and then publish to the channel.', + style: TextStyle(color: Colors.black), + ), + SizedBox(height: 16), + ], + ); } diff --git a/example/lib/ui/push_notifications/push_notifications_subscriptions_sliver.dart b/example/lib/ui/push_notifications/push_notifications_subscriptions_sliver.dart index f8b395bc5..283e24756 100644 --- a/example/lib/ui/push_notifications/push_notifications_subscriptions_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_subscriptions_sliver.dart @@ -1,8 +1,9 @@ import 'dart:io'; -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/push_notifications/push_notification_service.dart'; import 'package:ably_flutter_example/ui/bool_stream_button.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; class PushNotificationsSubscriptionsSliver extends StatelessWidget { @@ -11,166 +12,186 @@ class PushNotificationsSubscriptionsSliver extends StatelessWidget { const PushNotificationsSubscriptionsSliver({ required PushNotificationService pushNotificationService, Key? key, - }) : _pushNotificationService = pushNotificationService, - super(key: key); + }) : _pushNotificationService = pushNotificationService, + super(key: key); Widget buildSubscriptionsList( - Stream> stream) => - StreamBuilder>( - stream: stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - final subscriptions = snapshot.data - as ably.PaginatedResult; + Stream> stream, + ) => StreamBuilder>( + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + final subscriptions = + snapshot.data as ably.PaginatedResult; - if (subscriptions.items.isEmpty) { - return const Text('No subscriptions'); - } + if (subscriptions.items.isEmpty) { + return const Text('No subscriptions'); + } - final theresMoreSubscriptionsWidget = subscriptions.hasNext() - ? const Text('and more...') - : const SizedBox.shrink(); + final theresMoreSubscriptionsWidget = subscriptions.hasNext() + ? const Text('and more...') + : const SizedBox.shrink(); - return Column(children: [ - ...subscriptions.items - .map( - (e) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Container( - color: Colors.grey[200], - child: Column(children: [ - Text('Channel: ${e.channel}'), - if (e.deviceId != null) - Text('DeviceId: ${e.deviceId!}') - else - Text('ClientId: ${e.clientId}'), - ]), - ), + return Column( + children: [ + ...subscriptions.items + .map( + (e) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Container( + color: Colors.grey[200], + child: Column( + children: [ + Text('Channel: ${e.channel}'), + if (e.deviceId != null) + Text('DeviceId: ${e.deviceId!}') + else + Text('ClientId: ${e.clientId}'), + ], ), - ) - .toList(), - theresMoreSubscriptionsWidget - ]); - } - return const Text('List subscription button not pressed.'); - }); + ), + ), + ) + .toList(), + theresMoreSubscriptionsWidget, + ], + ); + } + return const Text('List subscription button not pressed.'); + }, + ); @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 8), + child: Text( + 'Push channel subscriptions', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + const Text( + 'You need to use a token/ key with the Push Admin capability ' + 'to list subscriptions by client ID or device ID. ' + 'If you subscribe both device and client to the channel, ' + 'the device will receive 2 notifications.', + ), + Row( children: [ - const Padding( - padding: EdgeInsets.only(bottom: 8), - child: Text( - 'Push channel subscriptions', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + Expanded( + child: BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: () async { + try { + await _pushNotificationService.subscribeDevice(); + } on ably.AblyException catch (ablyException) { + await showPushActivationError(context, ablyException); + } + }, + child: const Text('Subscribe device'), ), ), - const Text( - 'You need to use a token/ key with the Push Admin capability ' - 'to list subscriptions by client ID or device ID. ' - 'If you subscribe both device and client to the channel, ' - 'the device will receive 2 notifications.'), - Row( - children: [ - Expanded( - child: BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: () async { - try { - await _pushNotificationService.subscribeDevice(); - } on ably.AblyException catch (ablyException) { - await showPushActivationError(context, ablyException); - } - }, - child: const Text('Subscribe device')), - ), - Expanded( - child: BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: () async { - try { - await _pushNotificationService.unsubscribeDevice(); - } on ably.AblyException catch (ablyException) { - await showPushActivationError(context, ablyException); - } - }, - child: const Text('Unsubscribe device'), - ), - ), - ], + Expanded( + child: BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: () async { + try { + await _pushNotificationService.unsubscribeDevice(); + } on ably.AblyException catch (ablyException) { + await showPushActivationError(context, ablyException); + } + }, + child: const Text('Unsubscribe device'), + ), ), - BoolStreamButton( + ], + ), + BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: () async { + final subscriptions = await _pushNotificationService + .listSubscriptionsWithDeviceId(); + print('Push subscriptions: ${subscriptions.items}'); + }, + child: const Text('List subscriptions with current device ID'), + ), + buildSubscriptionsList( + _pushNotificationService.pushChannelDeviceSubscriptionsStream, + ), + Row( + children: [ + Expanded( + child: BoolStreamButton( stream: _pushNotificationService.hasPushChannelStream, onPressed: () async { - final subscriptions = await _pushNotificationService - .listSubscriptionsWithDeviceId(); - print('Push subscriptions: ${subscriptions.items}'); + try { + await _pushNotificationService.subscribeClient(); + } on ably.AblyException catch (ablyException) { + await showPushActivationError(context, ablyException); + } }, - child: const Text('List subscriptions with current device ID')), - buildSubscriptionsList( - _pushNotificationService.pushChannelDeviceSubscriptionsStream), - Row( - children: [ - Expanded( - child: BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: () async { - try { - await _pushNotificationService.subscribeClient(); - } on ably.AblyException catch (ablyException) { - await showPushActivationError(context, ablyException); - } - }, - child: const Text('Subscribe client')), - ), - Expanded( - child: BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: () async { - try { - await _pushNotificationService.unsubscribeClient(); - } on ably.AblyException catch (ablyException) { - await showPushActivationError(context, ablyException); - } - }, - child: const Text('Unsubscribe client')), - ) - ], + child: const Text('Subscribe client'), + ), ), - BoolStreamButton( + Expanded( + child: BoolStreamButton( stream: _pushNotificationService.hasPushChannelStream, onPressed: () async { - final subscriptions = await _pushNotificationService - .listSubscriptionsWithClientId(); - print('Push subscriptions: ${subscriptions.items}'); + try { + await _pushNotificationService.unsubscribeClient(); + } on ably.AblyException catch (ablyException) { + await showPushActivationError(context, ablyException); + } }, - child: const Text('List subscriptions with current client ID')), - buildSubscriptionsList( - _pushNotificationService.pushChannelClientSubscriptionsStream), - buildWarningTextForListSubscriptionsOnAndroid(), + child: const Text('Unsubscribe client'), + ), + ), ], ), - ); + BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: () async { + final subscriptions = await _pushNotificationService + .listSubscriptionsWithClientId(); + print('Push subscriptions: ${subscriptions.items}'); + }, + child: const Text('List subscriptions with current client ID'), + ), + buildSubscriptionsList( + _pushNotificationService.pushChannelClientSubscriptionsStream, + ), + buildWarningTextForListSubscriptionsOnAndroid(), + ], + ), + ); Widget buildWarningTextForListSubscriptionsOnAndroid() { if (Platform.isAndroid) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: RichText( - text: - const TextSpan(style: TextStyle(color: Colors.black), children: [ - TextSpan( + text: const TextSpan( + style: TextStyle(color: Colors.black), + children: [ + TextSpan( text: 'Warning: ', - style: - TextStyle(color: Colors.red, fontWeight: FontWeight.bold)), - TextSpan( - text: 'A bug in ably-java means all client ID and device ' + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: + 'A bug in ably-java means all client ID and device ' 'ID subscriptions are returned when you call ' - 'push.listSubscriptions. Track ably-java issue #705'), - ]), + 'push.listSubscriptions. Track ably-java issue #705', + ), + ], + ), ), ); } @@ -180,16 +201,16 @@ class PushNotificationsSubscriptionsSliver extends StatelessWidget { Future showPushActivationError( BuildContext context, ably.AblyException exception, - ) async => - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Push notification error'), - content: - Text('Failed to perform operation on push notification service. ' - 'Please verify your push notification setup with Readme ' - 'files in the ably-flutter repository.\n\n' - '${exception.errorInfo?.message}'), - ), - ); + ) async => showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Push notification error'), + content: Text( + 'Failed to perform operation on push notification service. ' + 'Please verify your push notification setup with Readme ' + 'files in the ably-flutter repository.\n\n' + '${exception.errorInfo?.message}', + ), + ), + ); } diff --git a/example/lib/ui/push_notifications/push_realtime_client_received_sliver.dart b/example/lib/ui/push_notifications/push_realtime_client_received_sliver.dart index 84a66804e..cdf8539e7 100644 --- a/example/lib/ui/push_notifications/push_realtime_client_received_sliver.dart +++ b/example/lib/ui/push_notifications/push_realtime_client_received_sliver.dart @@ -9,35 +9,44 @@ class PushRealtimeClientReceivedSliver extends StatelessWidget { const PushRealtimeClientReceivedSliver({ required PushNotificationService pushNotificationService, Key? key, - }) : _pushNotificationService = pushNotificationService, - super(key: key); + }) : _pushNotificationService = pushNotificationService, + super(key: key); @override Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( - children: [ - const Text( - 'Debugging messages (via realtime client)', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - const Text('To validate messages were sent, you can subscribe to ' - 'the channel and view the device logs.'), - BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: _pushNotificationService - .subscribeToChannelWithPushChannelRule, - child: const Text('Subscribe to channel: ' - '"${Constants.channelNameForPushNotifications}"')), - const Text('To debug push notifications, ' - 'subscribe to the meta channel.'), - BoolStreamButton( - stream: _pushNotificationService.hasPushChannelStream, - onPressed: - _pushNotificationService.subscribeToPushLogMetachannel, - child: const Text('Subscribe to push metachannel: ' - '${Constants.pushMetaChannelName}')), - ], + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + children: [ + const Text( + 'Debugging messages (via realtime client)', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), - ); + const Text( + 'To validate messages were sent, you can subscribe to ' + 'the channel and view the device logs.', + ), + BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: + _pushNotificationService.subscribeToChannelWithPushChannelRule, + child: const Text( + 'Subscribe to channel: ' + '"${Constants.channelNameForPushNotifications}"', + ), + ), + const Text( + 'To debug push notifications, ' + 'subscribe to the meta channel.', + ), + BoolStreamButton( + stream: _pushNotificationService.hasPushChannelStream, + onPressed: _pushNotificationService.subscribeToPushLogMetachannel, + child: const Text( + 'Subscribe to push metachannel: ' + '${Constants.pushMetaChannelName}', + ), + ), + ], + ), + ); } diff --git a/example/lib/ui/realtime_presence_sliver.dart b/example/lib/ui/realtime_presence_sliver.dart index de5531e75..2472de803 100644 --- a/example/lib/ui/realtime_presence_sliver.dart +++ b/example/lib/ui/realtime_presence_sliver.dart @@ -1,15 +1,16 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/constants.dart'; import 'package:ably_flutter_example/ui/paginated_result_viewer.dart'; import 'package:ably_flutter_example/ui/text_row.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; // ignore: must_be_immutable class RealtimePresenceSliver extends HookWidget { - final ably.Realtime realtime; + final ably.PubSubClient realtime; final ably.RealtimeChannel channel; final List> _subscriptions = []; @@ -23,47 +24,48 @@ class RealtimePresenceSliver extends HookWidget { ValueNotifier latestMessage, ably.ChannelState? channelState, ValueNotifier?> - presenceSubscription, - ) => - TextButton( - onPressed: (channelState == ably.ChannelState.attached && - presenceSubscription.value == null) - ? () { - final presenceMessageStream = channel.presence.subscribe(); - final subscription = - presenceMessageStream.listen((presenceMessage) { - latestMessage.value = presenceMessage; - }); - presenceSubscription.value = subscription; - _subscriptions.add(subscription); - } - : null, - child: const Text('Subscribe'), - ); + presenceSubscription, + ) => TextButton( + onPressed: + (channelState == ably.ChannelState.attached && + presenceSubscription.value == null) + ? () { + final presenceMessageStream = channel.presence.subscribe(); + final subscription = presenceMessageStream.listen(( + presenceMessage, + ) { + latestMessage.value = presenceMessage; + }); + presenceSubscription.value = subscription; + _subscriptions.add(subscription); + } + : null, + child: const Text('Subscribe'), + ); Widget createChannelPresenceUnsubscribeButton( ValueNotifier?> - presenceSubscription, - ) => - TextButton( - onPressed: (presenceSubscription.value != null) - ? () async { - await presenceSubscription.value!.cancel(); - presenceSubscription.value = null; - } - : null, - child: const Text('Unsubscribe'), - ); + presenceSubscription, + ) => TextButton( + onPressed: (presenceSubscription.value != null) + ? () async { + await presenceSubscription.value!.cancel(); + presenceSubscription.value = null; + } + : null, + child: const Text('Unsubscribe'), + ); Widget getRealtimeChannelPresence( - ValueNotifier> presenceMembers) => - TextButton( - onPressed: () async { - presenceMembers.value = - await channel.presence.get(const ably.RealtimePresenceParams()); - }, - child: const Text('Get Realtime presence members'), + ValueNotifier> presenceMembers, + ) => TextButton( + onPressed: () async { + presenceMembers.value = await channel.presence.get( + const ably.RealtimePresenceParams(), ); + }, + child: const Text('Get Realtime presence members'), + ); final List _presenceData = [ null, @@ -74,10 +76,10 @@ class RealtimePresenceSliver extends HookWidget { 1, 'ably', null, - {'a': 'b'} + {'a': 'b'}, ], { - 'c': ['a', 'b'] + 'c': ['a', 'b'], }, ]; @@ -88,26 +90,28 @@ class RealtimePresenceSliver extends HookWidget { .toString(); Widget enterRealtimePresence() => TextButton( - onPressed: () async { - await channel.presence.enter(_nextPresenceData); - }, - child: const Text('Enter'), - ); + onPressed: () async { + await channel.presence.enter(_nextPresenceData); + }, + child: const Text('Enter'), + ); Widget updateRealtimePresence() => TextButton( - onPressed: () async { - await channel.presence - .updateClient(Constants.clientId, _nextPresenceData); - }, - child: const Text('Update'), + onPressed: () async { + await channel.presence.updateClient( + Constants.clientId, + _nextPresenceData, ); + }, + child: const Text('Update'), + ); Widget leaveRealtimePresence() => TextButton( - onPressed: () async { - await channel.presence.leave(_nextPresenceData); - }, - child: const Text('Leave'), - ); + onPressed: () async { + await channel.presence.leave(_nextPresenceData); + }, + child: const Text('Leave'), + ); @override Widget build(BuildContext context) { @@ -128,11 +132,17 @@ class RealtimePresenceSliver extends HookWidget { Row( children: [ Expanded( - child: createChannelPresenceSubscribeButton( - latestMessage, channel.state, presenceSubscription)), + child: createChannelPresenceSubscribeButton( + latestMessage, + channel.state, + presenceSubscription, + ), + ), Expanded( - child: createChannelPresenceUnsubscribeButton( - presenceSubscription)), + child: createChannelPresenceUnsubscribeButton( + presenceSubscription, + ), + ), ], ), Text( @@ -141,15 +151,9 @@ class RealtimePresenceSliver extends HookWidget { ), Row( children: [ - Expanded( - child: enterRealtimePresence(), - ), - Expanded( - child: updateRealtimePresence(), - ), - Expanded( - child: leaveRealtimePresence(), - ), + Expanded(child: enterRealtimePresence()), + Expanded(child: updateRealtimePresence()), + Expanded(child: leaveRealtimePresence()), ], ), getRealtimeChannelPresence(presenceMembers), @@ -157,11 +161,14 @@ class RealtimePresenceSliver extends HookWidget { .map((m) => Text('${m.id}:${m.clientId}:${m.data}')) .toList(), PaginatedResultViewer( - title: 'Presence history', - query: () => channel.presence - .history(ably.RealtimeHistoryParams(limit: 10)), - builder: (context, message, _) => TextRow('clientId', - '${message.id}:${message.clientId}:${message.data}')), + title: 'Presence history', + query: () => + channel.presence.history(ably.RealtimeHistoryParams(limit: 10)), + builder: (context, message, _) => TextRow( + 'clientId', + '${message.id}:${message.clientId}:${message.data}', + ), + ), ], ), ); diff --git a/example/lib/ui/realtime_sliver.dart b/example/lib/ui/realtime_sliver.dart index 1f02e0849..59be800de 100644 --- a/example/lib/ui/realtime_sliver.dart +++ b/example/lib/ui/realtime_sliver.dart @@ -1,25 +1,26 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/constants.dart'; import 'package:ably_flutter_example/ui/ably_service.dart'; import 'package:ably_flutter_example/ui/paginated_result_viewer.dart'; import 'package:ably_flutter_example/ui/realtime_presence_sliver.dart'; import 'package:ably_flutter_example/ui/text_row.dart'; import 'package:ably_flutter_example/ui/utilities.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; // ignore: must_be_immutable class RealtimeSliver extends HookWidget { final AblyService ablyService; - final ably.Realtime realtime; + final ably.PubSubClient realtime; late ably.RealtimeChannel channel; List> _subscriptions = []; RealtimeSliver(this.ablyService, {Key? key}) - : realtime = ablyService.realtime, - super(key: key) { + : realtime = ablyService.realtime, + super(key: key) { channel = realtime.channels.get(Constants.channelName); } @@ -30,70 +31,71 @@ class RealtimeSliver extends HookWidget { _subscriptions = []; } - Widget buildConnectButton() => TextButton( - onPressed: realtime.connect, - child: const Text('Connect'), - ); + Widget buildConnectButton() => + TextButton(onPressed: realtime.connect, child: const Text('Connect')); Widget buildDisconnectButton(ably.ConnectionState state) => TextButton( - onPressed: - (state == ably.ConnectionState.connected) ? realtime.close : null, - child: const Text('Disconnect'), - ); + onPressed: (state == ably.ConnectionState.connected) + ? realtime.close + : null, + child: const Text('Disconnect'), + ); - Widget buildChannelAttachButton(ably.ConnectionState connectionState, - ably.ChannelState channelState) => - TextButton( - onPressed: (connectionState == ably.ConnectionState.connected && - channelState != ably.ChannelState.attached) - ? () async { - try { - await channel.attach(); - } on ably.AblyException catch (e) { - print('Unable to attach to channel: ${e.errorInfo}'); - } - } - : null, - child: const Text('Attach'), - ); + Widget buildChannelAttachButton( + ably.ConnectionState connectionState, + ably.ChannelState channelState, + ) => TextButton( + onPressed: + (connectionState == ably.ConnectionState.connected && + channelState != ably.ChannelState.attached) + ? () async { + try { + await channel.attach(); + } on ably.AblyException catch (e) { + print('Unable to attach to channel: ${e.errorInfo}'); + } + } + : null, + child: const Text('Attach'), + ); Widget buildChannelDetachButton(ably.ChannelState channelState) => TextButton( - onPressed: (channelState == ably.ChannelState.attached) - ? channel.detach - : null, - child: const Text('Detach'), - ); + onPressed: (channelState == ably.ChannelState.attached) + ? channel.detach + : null, + child: const Text('Detach'), + ); Widget buildChannelSubscribeButton( - ably.ChannelState channelState, - ValueNotifier latestMessage, - ValueNotifier?> - channelSubscription) => - TextButton( - onPressed: (channelState == ably.ChannelState.attached && - channelSubscription.value == null) - ? () { - final subscription = channel.subscribe().listen((message) { - latestMessage.value = message; - }); - channelSubscription.value = subscription; - _subscriptions.add(subscription); - } - : null, - child: const Text('Subscribe'), - ); + ably.ChannelState channelState, + ValueNotifier latestMessage, + ValueNotifier?> channelSubscription, + ) => TextButton( + onPressed: + (channelState == ably.ChannelState.attached && + channelSubscription.value == null) + ? () { + final subscription = channel.subscribe().listen((message) { + latestMessage.value = message; + }); + channelSubscription.value = subscription; + _subscriptions.add(subscription); + } + : null, + child: const Text('Subscribe'), + ); Widget buildChannelUnsubscribeButton( - ValueNotifier?> channelSubscription) => - TextButton( - onPressed: (channelSubscription.value != null) - ? () async { - await channelSubscription.value!.cancel(); - channelSubscription.value = null; - } - : null, - child: const Text('Unsubscribe'), - ); + ValueNotifier?> channelSubscription, + ) => TextButton( + onPressed: (channelSubscription.value != null) + ? () async { + await channelSubscription.value!.cancel(); + channelSubscription.value = null; + } + : null, + child: const Text('Unsubscribe'), + ); int typeCounter = 0; int realtimePubCounter = 0; @@ -102,15 +104,20 @@ class RealtimeSliver extends HookWidget { TextButton( onPressed: (channelState == ably.ChannelState.attached) ? () async { - final data = _messagesToPublish[ - (realtimePubCounter++ % _messagesToPublish.length)]; + final data = + _messagesToPublish[(realtimePubCounter++ % + _messagesToPublish.length)]; final m = ably.Message( - name: 'Message $realtimePubCounter', data: data); + name: 'Message $realtimePubCounter', + data: data, + ); try { switch (typeCounter % 3) { case 0: await channel.publish( - name: 'Message $realtimePubCounter', data: data); + name: 'Message $realtimePubCounter', + data: data, + ); break; case 1: await channel.publish(message: m); @@ -127,9 +134,7 @@ class RealtimeSliver extends HookWidget { } } : null, - child: const Text( - 'Publish', - ), + child: const Text('Publish'), ); Widget buildReleaseRealtimeChannelButton( @@ -137,55 +142,53 @@ class RealtimeSliver extends HookWidget { ValueNotifier channelState, ValueNotifier connectionId, ValueNotifier recoveryKey, - ) => - TextButton( - onPressed: () async { - await channel.detach(); - realtime.channels.release(Constants.channelName); - channel = realtime.channels.get(Constants.channelName); - setupListeners( - connectionState, channelState, connectionId, recoveryKey); - }, - child: const Text('Release'), - ); + ) => TextButton( + onPressed: () async { + await channel.detach(); + await realtime.channels.release(Constants.channelName); + channel = realtime.channels.get(Constants.channelName); + setupListeners(connectionState, channelState, connectionId, recoveryKey); + }, + child: const Text('Release'), + ); Widget buildEncryptionSwitch(ValueNotifier isEnabled) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - 'Enable encryption', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - Switch( - onChanged: (switchedOn) async { - isEnabled.value = switchedOn; - if (switchedOn) { - await channel.setOptions( - await ably.RealtimeChannelOptions.withCipherKey( - keyFromPassword( - Constants.examplePasswordForEncryptedChannel, - ), - ), - ); - } else { - await channel.setOptions(const ably.RealtimeChannelOptions()); - } - }, - value: isEnabled.value, - ), - ], - ); + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Enable encryption', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + Switch( + onChanged: (switchedOn) async { + isEnabled.value = switchedOn; + if (switchedOn) { + await channel.setOptions( + await ably.RealtimeChannelOptions.withCipherKey( + keyFromPassword(Constants.examplePasswordForEncryptedChannel), + ), + ); + } else { + await channel.setOptions(const ably.RealtimeChannelOptions()); + } + }, + value: isEnabled.value, + ), + ], + ); @override Widget build(BuildContext context) { - final connectionState = - useState(realtime.connection.state); + final connectionState = useState( + realtime.connection.state, + ); final connectionId = useState(realtime.connection.id); final recoveryKey = useState(''); final channelState = useState(channel.state); final latestMessage = useState(null); - final channelSubscription = - useState?>(null); + final channelSubscription = useState?>( + null, + ); final realtimeTime = useState(null); final useEncryption = useState(false); @@ -209,12 +212,8 @@ class RealtimeSliver extends HookWidget { buildEncryptionSwitch(useEncryption), Row( children: [ - Expanded( - child: buildConnectButton(), - ), - Expanded( - child: buildDisconnectButton(connectionState.value), - ) + Expanded(child: buildConnectButton()), + Expanded(child: buildDisconnectButton(connectionState.value)), ], ), const Text( @@ -225,94 +224,107 @@ class RealtimeSliver extends HookWidget { Row( children: [ Expanded( - child: buildChannelAttachButton( - connectionState.value, channelState.value)), + child: buildChannelAttachButton( + connectionState.value, + channelState.value, + ), + ), Expanded(child: buildChannelDetachButton(channelState.value)), Expanded( - child: buildReleaseRealtimeChannelButton( - connectionState, channelState, connectionId, recoveryKey)), + child: buildReleaseRealtimeChannelButton( + connectionState, + channelState, + connectionId, + recoveryKey, + ), + ), ], ), Row( children: [ Expanded( - child: buildChannelSubscribeButton( - channelState.value, latestMessage, channelSubscription)), + child: buildChannelSubscribeButton( + channelState.value, + latestMessage, + channelSubscription, + ), + ), Expanded(child: buildChannelPublishButton(channelState.value)), Expanded(child: buildChannelUnsubscribeButton(channelSubscription)), ], ), TextRow( - 'Latest message received', latestMessage.value?.data.toString()), + 'Latest message received', + latestMessage.value?.data.toString(), + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const TextRow('Next message to be published:', null), TextRow(' Name', 'Message $realtimePubCounter'), TextRow( - ' Data', - _messagesToPublish[ - realtimePubCounter % _messagesToPublish.length] - .toString()), + ' Data', + _messagesToPublish[realtimePubCounter % _messagesToPublish.length] + .toString(), + ), ], ), - RealtimePresenceSliver( - realtime: realtime, - channel: channel, - ), + RealtimePresenceSliver(realtime: realtime, channel: channel), PaginatedResultViewer( - title: 'History', - subtitle: const Column( - children: [ - TextRow( - 'Hint', - 'Use realtime history as a way to get messages that were' - ' published before you are attached to the channel.'), - TextRow( - 'Warning', - 'If you are already attached to the channel, you must' - ' detach and re-attach to get the latest messages ' - 'published on the channel to get history whilst ' - 'connected to a realtime channel, use ' - 'RestChannel.history instead.'), - ], - ), - query: () => channel.history( - ably.RealtimeHistoryParams( - limit: 10, - untilAttach: true, - ), - ), - builder: (context, message, _) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextRow('Name', message.name), - TextRow('Data', message.data.toString()), - ], - )), + title: 'History', + subtitle: const Column( + children: [ + TextRow( + 'Hint', + 'Use realtime history as a way to get messages that were' + ' published before you are attached to the channel.', + ), + TextRow( + 'Warning', + 'If you are already attached to the channel, you must' + ' detach and re-attach to get the latest messages ' + 'published on the channel.', + ), + ], + ), + query: () => channel.history( + ably.RealtimeHistoryParams(limit: 10, untilAttach: true), + ), + builder: (context, message, _) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextRow('Name', message.name), + TextRow('Data', message.data.toString()), + ], + ), + ), ], ); } void setupListeners( - ValueNotifier connectionState, - ValueNotifier channelState, - ValueNotifier connectionId, - ValueNotifier recoveryKey) { + ValueNotifier connectionState, + ValueNotifier channelState, + ValueNotifier connectionId, + ValueNotifier recoveryKey, + ) { dispose(); - final connectionSubscription = - realtime.connection.on().listen((connectionStateChange) { + final connectionSubscription = realtime.connection.on().listen(( + connectionStateChange, + ) { if (connectionStateChange.current == ably.ConnectionState.failed) { logAndDisplayError(connectionStateChange.reason); } connectionState.value = connectionStateChange.current; connectionId.value = realtime.connection.id; - realtime.connection - .createRecoveryKey() - .then((value) => {recoveryKey.value = value}); - print('${DateTime.now()}:' - ' ConnectionStateChange event: ${connectionStateChange.event}' - '\nReason: ${connectionStateChange.reason}'); + realtime.connection.createRecoveryKey().then( + (value) => {recoveryKey.value = value}, + ); + print( + '${DateTime.now()}:' + ' ConnectionStateChange event: ${connectionStateChange.event}' + '\nReason: ${connectionStateChange.reason}', + ); }); _subscriptions.add(connectionSubscription); final channelSubscription = channel.on().listen((stateChange) { @@ -329,8 +341,8 @@ List _messagesToPublish = [ 'I am': null, 'and': { 'also': 'nested', - 'too': {'deep': true} - } + 'too': {'deep': true}, + }, }, [ 42, @@ -341,8 +353,8 @@ List _messagesToPublish = [ 'I am': null, 'and': { 'also': 'nested', - 'too': {'deep': true} - } - } - ] + 'too': {'deep': true}, + }, + }, + ], ]; diff --git a/example/lib/ui/rest_sliver.dart b/example/lib/ui/rest_sliver.dart deleted file mode 100644 index 3776057a9..000000000 --- a/example/lib/ui/rest_sliver.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; -import 'package:ably_flutter_example/constants.dart'; -import 'package:ably_flutter_example/ui/paginated_result_viewer.dart'; -import 'package:ably_flutter_example/ui/text_row.dart'; -import 'package:ably_flutter_example/ui/utilities.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; - -// ignore: must_be_immutable -class RestSliver extends HookWidget { - final ably.Rest rest; - late ably.RestChannel channel; - - RestSliver(this.rest, {Key? key}) : super(key: key) { - channel = rest.channels.get(Constants.channelName); - } - - Widget buildPublishButton( - ValueNotifier messageCount, String messageName) => - TextButton( - onPressed: () async { - try { - await channel.publish( - name: messageName, data: 'Some data for $messageName'); - messageCount.value += 1; - } on ably.AblyException catch (e) { - print('Rest message sending failed:: $e :: ${e.errorInfo}'); - } - }, - child: const Text('Publish'), - ); - - Widget buildReleaseChannelButton() => TextButton( - onPressed: () => rest.channels.release(Constants.channelName), - child: const Text('Release channel'), - ); - - Widget buildEncryptionSwitch(ValueNotifier isEnabled) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - 'Enable encryption', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - Switch( - onChanged: (switchedOn) async { - isEnabled.value = switchedOn; - if (switchedOn) { - await channel.setOptions( - await ably.RestChannelOptions.withCipherKey( - keyFromPassword( - Constants.examplePasswordForEncryptedChannel, - ), - ), - ); - } else { - await channel.setOptions(ably.RestChannelOptions()); - } - }, - value: isEnabled.value, - ), - ], - ); - - @override - Widget build(BuildContext context) { - final messageCount = useState(1); - final messageName = useState('Message ${messageCount.value}'); - final restTime = useState(null); - final useEncryption = useState(false); - - useEffect(() { - rest.time().then((value) => restTime.value = value); - messageName.value = 'Message ${messageCount.value}'; - return; - }, [messageCount]); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Rest', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - Text('Rest time: ${restTime.value}'), - buildEncryptionSwitch(useEncryption), - Row( - children: [ - Expanded( - child: buildPublishButton(messageCount, messageName.value)), - Expanded(child: buildReleaseChannelButton()), - ], - ), - TextRow('Next message', messageName.value), - PaginatedResultViewer( - title: 'History', - query: () => channel.history(ably.RestHistoryParams( - direction: 'forwards', - limit: 10, - )), - builder: (context, message, _) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextRow('Name', message.name), - TextRow('Data', message.data.toString()), - ], - )), - PaginatedResultViewer( - title: 'Presence members', - query: () => - channel.presence.get(ably.RestPresenceParams(limit: 10)), - builder: (context, message, _) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextRow('Message ID', '${message.id}'), - TextRow('Message client ID', '${message.clientId}'), - TextRow('Message data', '${message.data}'), - ], - )), - PaginatedResultViewer( - title: 'Presence history', - query: () => - channel.presence.history(ably.RestHistoryParams(limit: 10)), - builder: (context, message, _) => TextRow('Message name', - '${message.id}:${message.clientId}:${message.data}')), - ], - ); - } -} diff --git a/example/lib/ui/system_details_sliver.dart b/example/lib/ui/system_details_sliver.dart index eabe0ad88..3a5a27a9e 100644 --- a/example/lib/ui/system_details_sliver.dart +++ b/example/lib/ui/system_details_sliver.dart @@ -1,7 +1,8 @@ -import 'package:ably_flutter/ably_flutter.dart' as ably; import 'package:ably_flutter_example/constants.dart'; import 'package:ably_flutter_example/ui/api_key_service.dart'; import 'package:ably_flutter_example/ui/text_row.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -9,10 +10,8 @@ import 'package:flutter_hooks/flutter_hooks.dart'; class SystemDetailsSliver extends HookWidget { ApiKeyProvision apiKeyProvision; - SystemDetailsSliver({ - required this.apiKeyProvision, - Key? key, - }) : super(key: key); + SystemDetailsSliver({required this.apiKeyProvision, Key? key}) + : super(key: key); @override Widget build(BuildContext context) { @@ -39,19 +38,24 @@ class SystemDetailsSliver extends HookWidget { if (apiKeyProvision.source != ApiKeySource.env) RichText( text: const TextSpan( - style: TextStyle(color: Colors.black), - children: [ - TextSpan( - text: 'Warning: ', - style: TextStyle( - color: Colors.red, fontWeight: FontWeight.bold)), - TextSpan( - text: 'Ably API key is not configured! Application uses ' - 'auto-provisioned sandbox key. Please follow ' - 'instructions in the repository Readme file to ' - 'setup the sample with your API key.', - ) - ]), + style: TextStyle(color: Colors.black), + children: [ + TextSpan( + text: 'Warning: ', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: + 'Ably API key is not configured! Application uses ' + 'auto-provisioned sandbox key. Please follow ' + 'instructions in the repository Readme file to ' + 'setup the sample with your API key.', + ), + ], + ), ), ], ); diff --git a/example/lib/ui/text_row.dart b/example/lib/ui/text_row.dart index b70e7bf42..88da9fe45 100644 --- a/example/lib/ui/text_row.dart +++ b/example/lib/ui/text_row.dart @@ -10,8 +10,17 @@ class TextRow extends StatelessWidget { @override Widget build(BuildContext context) => RichText( - text: TextSpan(children: [ - TextSpan(text: '$label: ', style: const TextStyle(color: Colors.blue)), - TextSpan(text: text, style: const TextStyle(color: Colors.black)) - ])); + text: TextSpan( + children: [ + TextSpan( + text: '$label: ', + style: const TextStyle(color: Colors.blue), + ), + TextSpan( + text: text, + style: const TextStyle(color: Colors.black), + ), + ], + ), + ); } diff --git a/example/lib/ui/utilities.dart b/example/lib/ui/utilities.dart index c3926cfe6..37d778fc2 100644 --- a/example/lib/ui/utilities.dart +++ b/example/lib/ui/utilities.dart @@ -1,25 +1,30 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:ably_flutter/ably_flutter.dart' as ably; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:crypto/crypto.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; -void logAndDisplayError(ably.ErrorInfo? errorInfo, - {String prefixMessage = ' '}) { +void logAndDisplayError( + ably.ErrorInfo? errorInfo, { + String prefixMessage = ' ', +}) { if (errorInfo == null) { return; } print(errorInfo.message); Fluttertoast.showToast( - msg: 'Error: $prefixMessage. ' - '${errorInfo.message ?? 'No error message provided'}', - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.CENTER, - backgroundColor: Colors.red, - textColor: Colors.white, - fontSize: 16); + msg: + 'Error: $prefixMessage. ' + '${errorInfo.message ?? 'No error message provided'}', + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.red, + textColor: Colors.white, + fontSize: 16, + ); } // This is a quick way to create a key from a password. In production, diff --git a/example/pubspec.lock b/example/pubspec.lock index 8e79682cc..58f917ee3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,7 +1,7 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - ably_flutter: + ably_pubsub_device_flutter: dependency: "direct dev" description: path: ".." @@ -28,10 +28,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" clock: dependency: transitive description: @@ -190,18 +190,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.19.0" path: dependency: transitive description: @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.4.2" web: dependency: transitive description: @@ -344,5 +344,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.24.0" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index bedd473e4..6ecbdebea 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the Ably Flutter plugin. publish_to: "none" environment: - sdk: ">=3.3.0 <4.0.0" - flutter: "3.24.0" + sdk: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" dependencies: flutter: @@ -23,7 +23,7 @@ dependencies: flutter_hooks: ^0.18.2 dev_dependencies: - ably_flutter: + ably_pubsub_device_flutter: path: ../ flutter_lints: ^1.0.4 diff --git a/ios/.gitignore b/ios/.gitignore index aa479fd3c..526d275cf 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -34,4 +34,11 @@ Icon? .tags* /Flutter/Generated.xcconfig -/Flutter/flutter_export_environment.sh \ No newline at end of file +/Flutter/flutter_export_environment.sh +# Swift Package Manager +.build/ +.swiftpm/ +Package.resolved + +# Generated by run_unit_tests.sh +/FlutterFramework/ diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/ios/Classes/handlers/AuthHandlers.swift b/ios/Classes/handlers/AuthHandlers.swift deleted file mode 100644 index fdef4657a..000000000 --- a/ios/Classes/handlers/AuthHandlers.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// AuthHandlers.swift -// ably_flutter -// -// Created by Ikbal Kaya on 17/03/2023. -// - -import Foundation - -public class AuthHandlers: NSObject { - - - @objc - public static let realtimeCreateTokenRequest: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) - let dataMap = ablyMessage.message as! Dictionary - var authOptions: ARTAuthOptions? - var tokenParams: ARTTokenParams? - - if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams - } - realtime?.auth.createTokenRequest(tokenParams, options: authOptions, callback: { (tokenRequest: ARTTokenRequest?, error: Error?) in - if let error = error { - result(error) - return - } - result(tokenRequest) - }) - } - - @objc - public static let realtimeAuthClientId: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) - let clientId = realtime?.auth.clientId - result(clientId) - } - - @objc - public static let realtimeAuthorize: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) - let dataMap = ablyMessage.message as! Dictionary - var authOptions: ARTAuthOptions? - var tokenParams: ARTTokenParams? - - if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams - } - - realtime?.auth.authorize(tokenParams, options: authOptions, callback: { (details:ARTTokenDetails?, error: Error?) in - if let error = error { - result(error) - return - } - result(details) - - }); - } - - @objc - public static let realtimeRequestToken: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) - let dataMap = ablyMessage.message as! Dictionary - var authOptions: ARTAuthOptions? - var tokenParams: ARTTokenParams? - - if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams - } - realtime?.auth.requestToken(tokenParams, with: authOptions, callback: { (details: ARTTokenDetails?, error: Error?) in - if let error = error { - result(error) - return - } - result(details) - }) - - } - - - @objc - public static let restCreateTokenRequest: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let rest = ably.instanceStore.rest(from: ablyMessage.handle) - let dataMap = ablyMessage.message as! Dictionary - var authOptions: ARTAuthOptions? - var tokenParams: ARTTokenParams? - - if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams - } - rest?.auth.createTokenRequest(tokenParams, options: authOptions, callback: { (tokenRequest: ARTTokenRequest?, error: Error?) in - if let error = error { - result(error) - return - } - result(tokenRequest) - }) - } - - - @objc - public static let restAuthorize: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let rest = ably.instanceStore.rest(from: ablyMessage.handle) - let dataMap = ablyMessage.message as! Dictionary - var authOptions: ARTAuthOptions? - var tokenParams: ARTTokenParams? - - if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams - } - - rest?.auth.authorize(tokenParams, options: authOptions, callback: { (details:ARTTokenDetails?, error: Error?) in - if let error = error { - result(error) - return - } - result(details) - - }); - } - - @objc - public static let restRequestToken: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let rest = ably.instanceStore.rest(from: ablyMessage.handle) - let dataMap = ablyMessage.message as! Dictionary - var authOptions: ARTAuthOptions? - var tokenParams: ARTTokenParams? - - if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams - } - rest?.auth.requestToken(tokenParams, with: authOptions, callback: { (details: ARTTokenDetails?, error: Error?) in - if let error = error { - result(error) - return - } - result(details) - }) - - } - - @objc - public static let restAuthClientId: FlutterHandler = { ably, call, result in - let ablyMessage = call.arguments as! AblyFlutterMessage - let rest = ably.instanceStore.rest(from: ablyMessage.handle) - let clientId = rest?.auth.clientId - result(clientId) - } -} diff --git a/ios/Classes/handlers/FlutterHandler.swift b/ios/Classes/handlers/FlutterHandler.swift deleted file mode 100644 index 6a89256ba..000000000 --- a/ios/Classes/handlers/FlutterHandler.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Foundation - -public typealias FlutterHandler = (_ plugin: AblyFlutter, _ call: FlutterMethodCall, _ result: @escaping FlutterResult) -> Void - -import Foundation diff --git a/ios/ably_flutter.podspec b/ios/ably_flutter.podspec deleted file mode 100644 index 37f36835d..000000000 --- a/ios/ably_flutter.podspec +++ /dev/null @@ -1,37 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint ably_flutter.podspec` to validate before publishing. -# -require 'yaml' - -pubspec = YAML.load_file(File.join('..', 'pubspec.yaml')) -flutter_package_plugin_version = pubspec['version'].gsub('+', '-') - -Pod::Spec.new do |s| - s.name = 'ably_flutter' - s.version = flutter_package_plugin_version - s.summary = 'Ably Cocoa platform support for our Flutter plugin.' - s.homepage = 'https://www.ably.com/' - s.license = 'Apache 2.0' - s.author = { 'Ably' => 'support@ably.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - s.dependency 'Ably', '1.2.53' - s.platform = :ios - s.ios.deployment_target = '10.0' - - # Flutter 3.x ships both arm64 and x86_64 simulator slices; no arch restriction needed. - s.pod_target_xcconfig = { - 'DEFINES_MODULE' => 'YES', - 'GCC_PREPROCESSOR_DEFINITIONS' => "FLUTTER_PACKAGE_PLUGIN_VERSION=\\@\\\"#{flutter_package_plugin_version}\\\"" - } - s.resource_bundles = {'ably_flutter' => ['Resources/PrivacyInfo.xcprivacy']} - s.swift_version = '5.0' - - s.test_spec 'Tests' do |ts| - ts.source_files = 'Tests/**/*.{h,m}' - ts.framework = 'XCTest' - end -end diff --git a/ios/ably_pubsub_device_flutter/Package.swift b/ios/ably_pubsub_device_flutter/Package.swift new file mode 100644 index 000000000..32c7d1c9f --- /dev/null +++ b/ios/ably_pubsub_device_flutter/Package.swift @@ -0,0 +1,74 @@ +// swift-tools-version: 5.9 + +import PackageDescription + +// SwiftPM does not allow Objective-C and Swift in the same target, and this +// plugin's two languages call into each other. The cycle is broken by layering +// it into three targets: +// +// - AblyFlutterCommon (Objective-C): the types both languages share -- the +// instance store, message wrappers, platform constants and the +// `AblyFlutterPluginContext` protocol handlers receive. +// - AblyFlutterSwift (Swift): method-call handlers, push notification glue and +// encoders. Depends only on AblyFlutterCommon. +// - ably_pubsub_device_flutter (Objective-C): the `AblyFlutter` plugin class +// Flutter registers, and the codec. Depends on both of the above. +let package = Package( + name: "ably_pubsub_device_flutter", + platforms: [ + // ably-pubsub-cocoa 2.0 requires iOS 15. + .iOS("15.0") + ], + products: [ + .library(name: "ably-pubsub-device-flutter", targets: ["ably_pubsub_device_flutter"]) + ], + dependencies: [ + // Provided by the Flutter tool alongside this package when it builds an app. + .package(name: "FlutterFramework", path: "../FlutterFramework"), + .package(url: "https://github.com/ably/ably-pubsub-cocoa", exact: "2.0.0"), + ], + targets: [ + .target( + name: "ably_pubsub_device_flutter", + dependencies: [ + "AblyFlutterCommon", + "AblyFlutterSwift", + .product(name: "FlutterFramework", package: "FlutterFramework"), + .product(name: "AblyPubSubDevice", package: "ably-pubsub-cocoa"), + ], + resources: [ + .process("PrivacyInfo.xcprivacy") + ], + cSettings: [ + .headerSearchPath("include/ably_pubsub_device_flutter") + ] + ), + .target( + name: "AblyFlutterSwift", + dependencies: [ + "AblyFlutterCommon", + .product(name: "FlutterFramework", package: "FlutterFramework"), + .product(name: "AblyPubSubDevice", package: "ably-pubsub-cocoa"), + ] + ), + .target( + name: "AblyFlutterCommon", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework"), + .product(name: "AblyPubSubDevice", package: "ably-pubsub-cocoa"), + ], + cSettings: [ + // Lets the implementation files import their public headers by + // bare name, as the generated AblyPlatformConstants.m does. + .headerSearchPath("include/AblyFlutterCommon") + ] + ), + .testTarget( + name: "AblyFlutterCommonTests", + dependencies: [ + "AblyFlutterCommon", + .product(name: "FlutterFramework", package: "FlutterFramework"), + ] + ), + ] +) diff --git a/ios/Classes/types/AblyFlutterClientOptions.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyFlutterClientOptions.m similarity index 100% rename from ios/Classes/types/AblyFlutterClientOptions.m rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyFlutterClientOptions.m diff --git a/ios/Classes/AblyFlutterMessage.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyFlutterMessage.m similarity index 100% rename from ios/Classes/AblyFlutterMessage.m rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyFlutterMessage.m diff --git a/ios/Classes/AblyInstanceStore.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m similarity index 62% rename from ios/Classes/AblyInstanceStore.m rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m index 7ef35ab4d..31edc9f7d 100644 --- a/ios/Classes/AblyInstanceStore.m +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m @@ -1,11 +1,9 @@ -@import Ably; +@import AblyPubSubDevice; #import "AblyInstanceStore.h" -#import @implementation AblyInstanceStore { - NSMutableDictionary* _realtimeInstances; - NSMutableDictionary* _restInstances; + NSMutableDictionary* _realtimeInstances; NSMutableDictionary* _paginatedResults; long long _nextHandle; } @@ -30,26 +28,17 @@ -(instancetype)init { } _realtimeInstances = [NSMutableDictionary new]; - _restInstances = [NSMutableDictionary new]; _paginatedResults = [NSMutableDictionary new]; _nextHandle = 1; return self; } --(void)setRest:(ARTRest *const)rest with:(NSNumber *const)handle { - _restInstances[handle] = rest; -} - --(ARTRest *)restFrom:(NSNumber *)handle { - return _restInstances[handle]; -} - --(void)setRealtime:(ARTRealtime *const)realtime with:(NSNumber *const)handle { +-(void)setRealtime:(ARTPubSubClient *const)realtime with:(NSNumber *const)handle { _realtimeInstances[handle] = realtime; } --(ARTRealtime *)realtimeFrom:(NSNumber *)handle { +-(ARTPubSubClient *)realtimeFrom:(NSNumber *)handle { return _realtimeInstances[handle]; } @@ -69,22 +58,17 @@ -(ARTPaginatedResult *) getPaginatedResult:(NSNumber *const) handle { -(void) didRegisterForRemoteNotificationsWithDeviceToken:(NSData *const) deviceToken { _didRegisterForRemoteNotificationsWithDeviceToken_deviceToken = deviceToken; - for (id restHandle in _restInstances) { - ARTRest *const rest = _restInstances[restHandle]; - [ARTPush didRegisterForRemoteNotificationsWithDeviceToken:deviceToken rest:rest]; - } for (id realtimeHandle in _realtimeInstances) { - ARTRealtime *const realtime = _realtimeInstances[realtimeHandle]; - [ARTPush didRegisterForRemoteNotificationsWithDeviceToken:deviceToken realtime:realtime]; + ARTPubSubClient *const realtime = _realtimeInstances[realtimeHandle]; + [ARTPush didRegisterForRemoteNotificationsWithDeviceToken:deviceToken pubsub:realtime]; } } -(void)reset { - for (ARTRealtime *const r in _realtimeInstances.allValues) { + for (ARTPubSubClient *const r in _realtimeInstances.allValues) { [r close]; } [_realtimeInstances removeAllObjects]; - [_restInstances removeAllObjects]; } @end diff --git a/ios/Classes/codec/AblyPlatformConstants.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m similarity index 91% rename from ios/Classes/codec/AblyPlatformConstants.m rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m index 36061c488..9c9bab43a 100644 --- a/ios/Classes/codec/AblyPlatformConstants.m +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m @@ -10,19 +10,7 @@ NSString *const AblyPlatformMethod_getPlatformVersion= @"getPlatformVersion"; NSString *const AblyPlatformMethod_getVersion= @"getVersion"; NSString *const AblyPlatformMethod_resetAblyClients= @"resetAblyClients"; -NSString *const AblyPlatformMethod_authCallback= @"authCallback"; NSString *const AblyPlatformMethod_realtimeAuthCallback= @"realtimeAuthCallback"; -NSString *const AblyPlatformMethod_createRest= @"createRest"; -NSString *const AblyPlatformMethod_setRestChannelOptions= @"setRestChannelOptions"; -NSString *const AblyPlatformMethod_publish= @"publish"; -NSString *const AblyPlatformMethod_restHistory= @"restHistory"; -NSString *const AblyPlatformMethod_restPresenceGet= @"restPresenceGet"; -NSString *const AblyPlatformMethod_restPresenceHistory= @"restPresenceHistory"; -NSString *const AblyPlatformMethod_releaseRestChannel= @"releaseRestChannel"; -NSString *const AblyPlatformMethod_restAuthAuthorize= @"restAuthAuthorize"; -NSString *const AblyPlatformMethod_restAuthCreateTokenRequest= @"restAuthCreateTokenRequest"; -NSString *const AblyPlatformMethod_restAuthRequestToken= @"restAuthRequestToken"; -NSString *const AblyPlatformMethod_restAuthGetClientId= @"restAuthGetClientId"; NSString *const AblyPlatformMethod_createRealtime= @"createRealtime"; NSString *const AblyPlatformMethod_connectRealtime= @"connectRealtime"; NSString *const AblyPlatformMethod_closeRealtime= @"closeRealtime"; @@ -39,14 +27,11 @@ NSString *const AblyPlatformMethod_releaseRealtimeChannel= @"releaseRealtimeChannel"; NSString *const AblyPlatformMethod_realtimeHistory= @"realtimeHistory"; NSString *const AblyPlatformMethod_realtimeTime= @"realtimeTime"; -NSString *const AblyPlatformMethod_restTime= @"restTime"; NSString *const AblyPlatformMethod_realtimeAuthAuthorize= @"realtimeAuthAuthorize"; NSString *const AblyPlatformMethod_realtimeAuthCreateTokenRequest= @"realtimeAuthCreateTokenRequest"; NSString *const AblyPlatformMethod_realtimeAuthRequestToken= @"realtimeAuthRequestToken"; NSString *const AblyPlatformMethod_realtimeAuthGetClientId= @"realtimeAuthGetClientId"; - -NSString *const AblyPlatformMethod_connectionRecoveryKey = @"connectionRecoveryKey"; - +NSString *const AblyPlatformMethod_connectionRecoveryKey= @"connectionRecoveryKey"; NSString *const AblyPlatformMethod_pushActivate= @"pushActivate"; NSString *const AblyPlatformMethod_pushDeactivate= @"pushDeactivate"; NSString *const AblyPlatformMethod_pushReset= @"pushReset"; @@ -127,8 +112,7 @@ NSString *const TxClientOptions_clientId = @"clientId"; NSString *const TxClientOptions_logLevel = @"logLevel"; NSString *const TxClientOptions_tls = @"tls"; -NSString *const TxClientOptions_restHost = @"restHost"; -NSString *const TxClientOptions_realtimeHost = @"realtimeHost"; +NSString *const TxClientOptions_endpoint = @"endpoint"; NSString *const TxClientOptions_port = @"port"; NSString *const TxClientOptions_tlsPort = @"tlsPort"; NSString *const TxClientOptions_autoConnect = @"autoConnect"; @@ -136,23 +120,21 @@ NSString *const TxClientOptions_queueMessages = @"queueMessages"; NSString *const TxClientOptions_echoMessages = @"echoMessages"; NSString *const TxClientOptions_recover = @"recover"; -NSString *const TxClientOptions_environment = @"environment"; NSString *const TxClientOptions_idempotentRestPublishing = @"idempotentRestPublishing"; +NSString *const TxClientOptions_disconnectedRetryTimeout = @"disconnectedRetryTimeout"; +NSString *const TxClientOptions_suspendedRetryTimeout = @"suspendedRetryTimeout"; +NSString *const TxClientOptions_httpMaxRetryDuration = @"httpMaxRetryDuration"; NSString *const TxClientOptions_httpOpenTimeout = @"httpOpenTimeout"; NSString *const TxClientOptions_httpRequestTimeout = @"httpRequestTimeout"; NSString *const TxClientOptions_httpMaxRetryCount = @"httpMaxRetryCount"; NSString *const TxClientOptions_realtimeRequestTimeout = @"realtimeRequestTimeout"; NSString *const TxClientOptions_fallbackHosts = @"fallbackHosts"; -NSString *const TxClientOptions_fallbackHostsUseDefault = @"fallbackHostsUseDefault"; NSString *const TxClientOptions_fallbackRetryTimeout = @"fallbackRetryTimeout"; NSString *const TxClientOptions_defaultTokenParams = @"defaultTokenParams"; NSString *const TxClientOptions_channelRetryTimeout = @"channelRetryTimeout"; NSString *const TxClientOptions_transportParams = @"transportParams"; NSString *const TxClientOptions_dartVersion = @"dartVersion"; -// key constants for RestChannelOptions -NSString *const TxRestChannelOptions_cipherParams = @"cipherParams"; - // key constants for RealtimeChannelOptions NSString *const TxRealtimeChannelOptions_params = @"params"; NSString *const TxRealtimeChannelOptions_modes = @"modes"; @@ -290,12 +272,6 @@ NSString *const TxPaginatedResult_type = @"type"; NSString *const TxPaginatedResult_hasNext = @"hasNext"; -// key constants for RestHistoryParams -NSString *const TxRestHistoryParams_start = @"start"; -NSString *const TxRestHistoryParams_end = @"end"; -NSString *const TxRestHistoryParams_direction = @"direction"; -NSString *const TxRestHistoryParams_limit = @"limit"; - // key constants for RealtimeHistoryParams NSString *const TxRealtimeHistoryParams_start = @"start"; NSString *const TxRealtimeHistoryParams_end = @"end"; @@ -303,11 +279,6 @@ NSString *const TxRealtimeHistoryParams_limit = @"limit"; NSString *const TxRealtimeHistoryParams_untilAttach = @"untilAttach"; -// key constants for RestPresenceParams -NSString *const TxRestPresenceParams_limit = @"limit"; -NSString *const TxRestPresenceParams_clientId = @"clientId"; -NSString *const TxRestPresenceParams_connectionId = @"connectionId"; - // key constants for RealtimePresenceParams NSString *const TxRealtimePresenceParams_waitForSync = @"waitForSync"; NSString *const TxRealtimePresenceParams_clientId = @"clientId"; diff --git a/ios/Classes/AblyStreamsChannel.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyStreamsChannel.m similarity index 100% rename from ios/Classes/AblyStreamsChannel.m rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyStreamsChannel.m diff --git a/ios/Classes/types/AblyFlutterClientOptions.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterClientOptions.h similarity index 93% rename from ios/Classes/types/AblyFlutterClientOptions.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterClientOptions.h index 0a79fd86e..e93b13a70 100644 --- a/ios/Classes/types/AblyFlutterClientOptions.h +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterClientOptions.h @@ -1,4 +1,4 @@ -@import Ably; +@import AblyPubSubDevice; NS_ASSUME_NONNULL_BEGIN diff --git a/ios/Classes/AblyFlutterMessage.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterMessage.h similarity index 100% rename from ios/Classes/AblyFlutterMessage.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterMessage.h diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterPluginContext.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterPluginContext.h new file mode 100644 index 000000000..5a352b017 --- /dev/null +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterPluginContext.h @@ -0,0 +1,22 @@ +@import Foundation; +@import Flutter; + +#import "AblyInstanceStore.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + The plugin state a method-call handler needs. + + `AblyFlutter` conforms to this. Handlers written in Swift take it instead of + `AblyFlutter` itself, because the Swift target cannot depend on the + Objective-C target that defines the plugin class -- that target depends on it. + */ +@protocol AblyFlutterPluginContext + +@property(nonatomic, readonly) AblyInstanceStore *instanceStore; +@property(nonatomic, readonly) FlutterMethodChannel *channel; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Classes/AblyInstanceStore.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h similarity index 71% rename from ios/Classes/AblyInstanceStore.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h index 8af0edbc1..b738f9374 100644 --- a/ios/Classes/AblyInstanceStore.h +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h @@ -1,8 +1,7 @@ @import Foundation; @import Flutter; -@class ARTRest; -@class ARTRealtime; +@class ARTPubSubClient; #import "AblyFlutterClientOptions.h" NS_ASSUME_NONNULL_BEGIN @@ -13,13 +12,9 @@ NS_ASSUME_NONNULL_BEGIN -(NSNumber *) getNextHandle; --(void)setRest:(ARTRest *const)rest with:(NSNumber *const)handle; +-(void)setRealtime:(ARTPubSubClient *const)realtime with:(NSNumber *const)handle; --(nullable ARTRest *)restFrom:(NSNumber *)handle; - --(void)setRealtime:(ARTRealtime *const)realtime with:(NSNumber *const)handle; - --(nullable ARTRealtime *)realtimeFrom:(NSNumber *)handle; +-(nullable ARTPubSubClient *)realtimeFrom:(NSNumber *)handle; -(NSNumber *)setPaginatedResult:(ARTPaginatedResult *const)result handle:(nullable NSNumber *)handle; diff --git a/ios/Classes/codec/AblyPlatformConstants.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h similarity index 88% rename from ios/Classes/codec/AblyPlatformConstants.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h index b733bcefd..ab333649e 100644 --- a/ios/Classes/codec/AblyPlatformConstants.h +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h @@ -16,24 +16,21 @@ typedef NS_ENUM(UInt8, CodecType) { CodecTypeTokenParams = 135, CodecTypeTokenDetails = 136, CodecTypeTokenRequest = 137, - CodecTypeRestChannelOptions = 138, - CodecTypeRealtimeChannelOptions = 139, - CodecTypePaginatedResult = 140, - CodecTypeRestHistoryParams = 141, - CodecTypeRealtimeHistoryParams = 142, - CodecTypeRestPresenceParams = 143, - CodecTypePresenceMessage = 144, - CodecTypeRealtimePresenceParams = 145, - CodecTypeDeviceDetails = 146, - CodecTypeLocalDevice = 147, - CodecTypePushChannelSubscription = 148, - CodecTypeUnNotificationSettings = 149, - CodecTypeRemoteMessage = 150, - CodecTypeErrorInfo = 151, - CodecTypeLogLevel = 152, - CodecTypeConnectionStateChange = 153, - CodecTypeChannelStateChange = 154, - CodecTypeCipherParams = 155, + CodecTypeRealtimeChannelOptions = 138, + CodecTypePaginatedResult = 139, + CodecTypeRealtimeHistoryParams = 140, + CodecTypePresenceMessage = 141, + CodecTypeRealtimePresenceParams = 142, + CodecTypeDeviceDetails = 143, + CodecTypeLocalDevice = 144, + CodecTypePushChannelSubscription = 145, + CodecTypeUnNotificationSettings = 146, + CodecTypeRemoteMessage = 147, + CodecTypeErrorInfo = 148, + CodecTypeLogLevel = 149, + CodecTypeConnectionStateChange = 150, + CodecTypeChannelStateChange = 151, + CodecTypeCipherParams = 152, }; @@ -41,19 +38,7 @@ typedef NS_ENUM(UInt8, CodecType) { extern NSString *const AblyPlatformMethod_getPlatformVersion; extern NSString *const AblyPlatformMethod_getVersion; extern NSString *const AblyPlatformMethod_resetAblyClients; -extern NSString *const AblyPlatformMethod_authCallback; extern NSString *const AblyPlatformMethod_realtimeAuthCallback; -extern NSString *const AblyPlatformMethod_createRest; -extern NSString *const AblyPlatformMethod_setRestChannelOptions; -extern NSString *const AblyPlatformMethod_publish; -extern NSString *const AblyPlatformMethod_restHistory; -extern NSString *const AblyPlatformMethod_restPresenceGet; -extern NSString *const AblyPlatformMethod_restPresenceHistory; -extern NSString *const AblyPlatformMethod_releaseRestChannel; -extern NSString *const AblyPlatformMethod_restAuthAuthorize; -extern NSString *const AblyPlatformMethod_restAuthCreateTokenRequest; -extern NSString *const AblyPlatformMethod_restAuthRequestToken; -extern NSString *const AblyPlatformMethod_restAuthGetClientId; extern NSString *const AblyPlatformMethod_createRealtime; extern NSString *const AblyPlatformMethod_connectRealtime; extern NSString *const AblyPlatformMethod_closeRealtime; @@ -70,14 +55,11 @@ extern NSString *const AblyPlatformMethod_publishRealtimeChannelMessage; extern NSString *const AblyPlatformMethod_releaseRealtimeChannel; extern NSString *const AblyPlatformMethod_realtimeHistory; extern NSString *const AblyPlatformMethod_realtimeTime; -extern NSString *const AblyPlatformMethod_restTime; extern NSString *const AblyPlatformMethod_realtimeAuthAuthorize; extern NSString *const AblyPlatformMethod_realtimeAuthCreateTokenRequest; extern NSString *const AblyPlatformMethod_realtimeAuthRequestToken; extern NSString *const AblyPlatformMethod_realtimeAuthGetClientId; - extern NSString *const AblyPlatformMethod_connectionRecoveryKey; - extern NSString *const AblyPlatformMethod_pushActivate; extern NSString *const AblyPlatformMethod_pushDeactivate; extern NSString *const AblyPlatformMethod_pushReset; @@ -158,8 +140,7 @@ extern NSString *const TxClientOptions_hasAuthCallback; extern NSString *const TxClientOptions_clientId; extern NSString *const TxClientOptions_logLevel; extern NSString *const TxClientOptions_tls; -extern NSString *const TxClientOptions_restHost; -extern NSString *const TxClientOptions_realtimeHost; +extern NSString *const TxClientOptions_endpoint; extern NSString *const TxClientOptions_port; extern NSString *const TxClientOptions_tlsPort; extern NSString *const TxClientOptions_autoConnect; @@ -167,23 +148,21 @@ extern NSString *const TxClientOptions_useBinaryProtocol; extern NSString *const TxClientOptions_queueMessages; extern NSString *const TxClientOptions_echoMessages; extern NSString *const TxClientOptions_recover; -extern NSString *const TxClientOptions_environment; extern NSString *const TxClientOptions_idempotentRestPublishing; +extern NSString *const TxClientOptions_disconnectedRetryTimeout; +extern NSString *const TxClientOptions_suspendedRetryTimeout; +extern NSString *const TxClientOptions_httpMaxRetryDuration; extern NSString *const TxClientOptions_httpOpenTimeout; extern NSString *const TxClientOptions_httpRequestTimeout; extern NSString *const TxClientOptions_httpMaxRetryCount; extern NSString *const TxClientOptions_realtimeRequestTimeout; extern NSString *const TxClientOptions_fallbackHosts; -extern NSString *const TxClientOptions_fallbackHostsUseDefault; extern NSString *const TxClientOptions_fallbackRetryTimeout; extern NSString *const TxClientOptions_defaultTokenParams; extern NSString *const TxClientOptions_channelRetryTimeout; extern NSString *const TxClientOptions_transportParams; extern NSString *const TxClientOptions_dartVersion; -// key constants for RestChannelOptions -extern NSString *const TxRestChannelOptions_cipherParams; - // key constants for RealtimeChannelOptions extern NSString *const TxRealtimeChannelOptions_params; extern NSString *const TxRealtimeChannelOptions_modes; @@ -321,12 +300,6 @@ extern NSString *const TxPaginatedResult_items; extern NSString *const TxPaginatedResult_type; extern NSString *const TxPaginatedResult_hasNext; -// key constants for RestHistoryParams -extern NSString *const TxRestHistoryParams_start; -extern NSString *const TxRestHistoryParams_end; -extern NSString *const TxRestHistoryParams_direction; -extern NSString *const TxRestHistoryParams_limit; - // key constants for RealtimeHistoryParams extern NSString *const TxRealtimeHistoryParams_start; extern NSString *const TxRealtimeHistoryParams_end; @@ -334,11 +307,6 @@ extern NSString *const TxRealtimeHistoryParams_direction; extern NSString *const TxRealtimeHistoryParams_limit; extern NSString *const TxRealtimeHistoryParams_untilAttach; -// key constants for RestPresenceParams -extern NSString *const TxRestPresenceParams_limit; -extern NSString *const TxRestPresenceParams_clientId; -extern NSString *const TxRestPresenceParams_connectionId; - // key constants for RealtimePresenceParams extern NSString *const TxRealtimePresenceParams_waitForSync; extern NSString *const TxRealtimePresenceParams_clientId; diff --git a/ios/Classes/AblyStreamsChannel.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyStreamsChannel.h similarity index 100% rename from ios/Classes/AblyStreamsChannel.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyStreamsChannel.h diff --git a/ios/Classes/Notification.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/Notification.swift similarity index 100% rename from ios/Classes/Notification.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/Notification.swift diff --git a/ios/Classes/PushNotificationEventHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/PushNotificationEventHandlers.swift similarity index 98% rename from ios/Classes/PushNotificationEventHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/PushNotificationEventHandlers.swift index 6d85bebd1..4f854c135 100644 --- a/ios/Classes/PushNotificationEventHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/PushNotificationEventHandlers.swift @@ -1,5 +1,8 @@ -import Foundation +import AblyFlutterCommon import Flutter +import Foundation +import UIKit +import UserNotifications // This class is used to replace the UNUserNotificationCenterDelegate set by the user. // diff --git a/ios/Classes/RemoteMessage.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/RemoteMessage.swift similarity index 96% rename from ios/Classes/RemoteMessage.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/RemoteMessage.swift index 7a9e78b29..97641263c 100644 --- a/ios/Classes/RemoteMessage.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/RemoteMessage.swift @@ -1,5 +1,5 @@ import Foundation -import UIKit +import UserNotifications public class RemoteMessage: NSObject { @objc diff --git a/ios/Classes/codec/CryptoCodec.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift similarity index 53% rename from ios/Classes/codec/CryptoCodec.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift index 106336d95..b15ee2843 100644 --- a/ios/Classes/codec/CryptoCodec.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift @@ -1,17 +1,19 @@ +import AblyPubSubDevice +import AblyFlutterCommon +import Flutter import Foundation -import Ably public class CryptoCodec: NSObject { @objc - public static let readCipherParams: (Dictionary) -> ARTCipherParams = { dictionary in + public static let readCipherParams: (Dictionary) -> CipherParams = { dictionary in let algorithm = dictionary[TxCipherParams_iosAlgorithm] as! String let key = dictionary[TxCipherParams_iosKey] as! FlutterStandardTypedData - return ARTCipherParams(algorithm: algorithm, key: key.data as NSData) + return CipherParams(algorithm: algorithm, key: key.data as NSData) } @objc - public static let encodeCipherParams: (ARTCipherParams) -> Dictionary = { cipherParams in + public static let encodeCipherParams: (CipherParams) -> Dictionary = { cipherParams in return [ TxCipherParams_iosKey: cipherParams.key, TxCipherParams_iosAlgorithm: cipherParams.algorithm, @@ -19,62 +21,53 @@ public class CryptoCodec: NSObject { } @objc - public static let readRealtimeChannelOptions: (Dictionary) -> ARTRealtimeChannelOptions = { dictionary in - var channelOptions: ARTRealtimeChannelOptions; + public static let readRealtimeChannelOptions: (Dictionary) -> RealtimeChannelOptions = { dictionary in + var channelOptions: RealtimeChannelOptions; if let cipherParamsDictionary = dictionary[TxRealtimeChannelOptions_cipherParams] as? Dictionary { - channelOptions = ARTRealtimeChannelOptions(cipher: readCipherParams(cipherParamsDictionary)) + channelOptions = RealtimeChannelOptions(cipher: readCipherParams(cipherParamsDictionary)) } else { - channelOptions = ARTRealtimeChannelOptions() + channelOptions = RealtimeChannelOptions() } channelOptions.params = dictionary[TxRealtimeChannelOptions_params] as? Dictionary let modesStrings = dictionary[TxRealtimeChannelOptions_modes] as? [String] channelOptions.modes = [] modesStrings?.forEach { mode in - if let mode = ARTChannelMode.from(mode: mode) { + if let mode = ChannelMode.from(mode: mode) { channelOptions.modes.insert(mode) } } return channelOptions } - - @objc - public static let readRestChannelOptions: (Dictionary) -> ARTChannelOptions = { dictionary in - if let cipherParamsDictionary = dictionary[TxRestChannelOptions_cipherParams] as? Dictionary { - return ARTChannelOptions(cipher: readCipherParams(cipherParamsDictionary)) - } else { - return ARTChannelOptions() - } - } } -extension ARTChannelMode { - static func from(mode: String) -> ARTChannelMode? { +extension ChannelMode { + static func from(mode: String) -> ChannelMode? { switch(mode) { case TxEnumConstants_presence: - return ARTChannelMode.presence + return ChannelMode.presence case TxEnumConstants_subscribe: - return ARTChannelMode.subscribe + return ChannelMode.subscribe case TxEnumConstants_publish: - return ARTChannelMode.publish + return ChannelMode.publish case TxEnumConstants_presenceSubscribe: - return ARTChannelMode.presenceSubscribe + return ChannelMode.presenceSubscribe default: return nil } } func toString() -> [String] { var modes: [String] = []; - if self.contains(ARTChannelMode.presence) { + if self.contains(ChannelMode.presence) { modes.append(TxEnumConstants_presence) } - if self.contains(ARTChannelMode.subscribe) { + if self.contains(ChannelMode.subscribe) { modes.append(TxEnumConstants_subscribe) } - if self.contains(ARTChannelMode.publish) { + if self.contains(ChannelMode.publish) { modes.append(TxEnumConstants_publish) } - if self.contains(ARTChannelMode.presenceSubscribe) { + if self.contains(ChannelMode.presenceSubscribe) { modes.append(TxEnumConstants_presenceSubscribe) } return modes diff --git a/ios/Classes/codec/encoders/ErrorInfo.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift similarity index 71% rename from ios/Classes/codec/encoders/ErrorInfo.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift index a57f0d014..09974e826 100644 --- a/ios/Classes/codec/encoders/ErrorInfo.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift @@ -1,8 +1,10 @@ +import AblyPubSubDevice +import AblyFlutterCommon import Foundation public class Encoders: NSObject { @objc - public static let encodeErrorInfo: (ARTErrorInfo) -> Dictionary = { errorInfo in + public static let encodeErrorInfo: (ErrorInfo) -> Dictionary = { errorInfo in return [ TxErrorInfo_message: errorInfo.message, TxErrorInfo_statusCode: errorInfo.statusCode as Any diff --git a/ios/Classes/codec/encoders/PushNotificationEncoders.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift similarity index 84% rename from ios/Classes/codec/encoders/PushNotificationEncoders.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift index 630be4236..e129be189 100644 --- a/ios/Classes/codec/encoders/PushNotificationEncoders.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift @@ -1,11 +1,12 @@ -import Ably +import AblyPubSubDevice +import AblyFlutterCommon import Foundation import UserNotifications public class PushNotificationEncoders: NSObject { @objc - public static let encodeDeviceDetails: (ARTDeviceDetails) -> Dictionary = { device in + public static let encodeDeviceDetails: (DeviceDetails) -> Dictionary = { device in [ TxDeviceDetails_id: device.id, TxDeviceDetails_clientId: device.clientId as Any, @@ -17,16 +18,16 @@ public class PushNotificationEncoders: NSObject { } @objc - public static let encodeDevicePushDetails: (ARTDevicePushDetails) -> Dictionary = { devicePushDetails in + public static let encodeDevicePushDetails: (DevicePushDetails) -> Dictionary = { devicePushDetails in [ TxDevicePushDetails_recipient: devicePushDetails.recipient, - TxDevicePushDetails_state: devicePushDetails.state as Any, + TxDevicePushDetails_state: devicePushDetails.state.toString() as Any, TxDevicePushDetails_errorReason: (devicePushDetails.errorReason != nil ? Encoders.encodeErrorInfo(devicePushDetails.errorReason!) : nil) as Any ]; } @objc - public static let encodeLocalDevice: (ARTLocalDevice) -> Dictionary = { device in + public static let encodeLocalDevice: (LocalDevice) -> Dictionary = { device in [ TxLocalDevice_deviceSecret: device.secret as Any, TxLocalDevice_deviceIdentityToken: device.identityTokenDetails?.token as Any, @@ -41,7 +42,7 @@ public class PushNotificationEncoders: NSObject { } @objc - public static let encodePushChannelSubscription: (ARTPushChannelSubscription) -> Dictionary = { subscription in + public static let encodePushChannelSubscription: (PushChannelSubscription) -> Dictionary = { subscription in [ TxPushChannelSubscription_channel: subscription.channel, TxPushChannelSubscription_clientId: subscription.clientId as Any, @@ -164,3 +165,21 @@ extension UNShowPreviewsSetting { } } } + +extension DevicePushState { + /// `nil` for `.unknown`, which stands for a state Ably did not report. + func toString() -> String? { + switch self { + case .active: + return TxDevicePushStateEnum_active + case .failing: + return TxDevicePushStateEnum_failing + case .failed: + return TxDevicePushStateEnum_failed + case .unknown: + return nil + @unknown default: + return nil + } + } +} diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift new file mode 100644 index 000000000..31803ca33 --- /dev/null +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift @@ -0,0 +1,89 @@ +// +// AuthHandlers.swift +// ably_pubsub_device_flutter +// +// Created by Ikbal Kaya on 17/03/2023. +// + +import AblyPubSubDevice +import AblyFlutterCommon +import Flutter +import Foundation + +public class AuthHandlers: NSObject { + + + @objc + public static let realtimeCreateTokenRequest: FlutterHandler = { ably, call, result in + let ablyMessage = call.arguments as! AblyFlutterMessage + let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) + let dataMap = ablyMessage.message as! Dictionary + var authOptions: AuthOptions? + var tokenParams: TokenParams? + + if let dataMap = ablyMessage.message as? Dictionary { + authOptions = dataMap[TxTransportKeys_options] as? AuthOptions + tokenParams = dataMap[TxTransportKeys_params] as? TokenParams + } + realtime?.auth.createTokenRequest(tokenParams, options: authOptions, callback: { (tokenRequest: TokenRequest?, error: Error?) in + if let error = error { + result(error) + return + } + result(tokenRequest) + }) + } + + @objc + public static let realtimeAuthClientId: FlutterHandler = { ably, call, result in + let ablyMessage = call.arguments as! AblyFlutterMessage + let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) + let clientId = realtime?.auth.clientId + result(clientId) + } + + @objc + public static let realtimeAuthorize: FlutterHandler = { ably, call, result in + let ablyMessage = call.arguments as! AblyFlutterMessage + let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) + let dataMap = ablyMessage.message as! Dictionary + var authOptions: AuthOptions? + var tokenParams: TokenParams? + + if let dataMap = ablyMessage.message as? Dictionary { + authOptions = dataMap[TxTransportKeys_options] as? AuthOptions + tokenParams = dataMap[TxTransportKeys_params] as? TokenParams + } + + realtime?.auth.authorize(tokenParams, options: authOptions, callback: { (details:TokenDetails?, error: Error?) in + if let error = error { + result(error) + return + } + result(details) + + }); + } + + @objc + public static let realtimeRequestToken: FlutterHandler = { ably, call, result in + let ablyMessage = call.arguments as! AblyFlutterMessage + let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) + let dataMap = ablyMessage.message as! Dictionary + var authOptions: AuthOptions? + var tokenParams: TokenParams? + + if let dataMap = ablyMessage.message as? Dictionary { + authOptions = dataMap[TxTransportKeys_options] as? AuthOptions + tokenParams = dataMap[TxTransportKeys_params] as? TokenParams + } + realtime?.auth.requestToken(tokenParams, with: authOptions, callback: { (details: TokenDetails?, error: Error?) in + if let error = error { + result(error) + return + } + result(details) + }) + + } +} diff --git a/ios/Classes/handlers/CryptoHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift similarity index 81% rename from ios/Classes/handlers/CryptoHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift index 33812ac8c..4eb8583fd 100644 --- a/ios/Classes/handlers/CryptoHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift @@ -1,5 +1,7 @@ +import AblyPubSubDevice +import AblyFlutterCommon +import Flutter import Foundation -import Ably public class CryptoHandlers: NSObject { @objc @@ -10,10 +12,10 @@ public class CryptoHandlers: NSObject { let key = message[TxCryptoGetParams_key] if let key = key as? NSString { - result(ARTCipherParams(algorithm: algorithm, key: key)) + result(CipherParams(algorithm: algorithm, key: key)) return } else if let key = key as? FlutterStandardTypedData { - result(ARTCipherParams(algorithm: algorithm, key: key.data as NSData)) + result(CipherParams(algorithm: algorithm, key: key.data as NSData)) return } else if let key = key { result(FlutterError(code: "CryptoHandlers_getParams", message: "Key must be a String or FlutterStandardTypedData, it is \(type(of: key))", details: nil)) @@ -27,6 +29,6 @@ public class CryptoHandlers: NSObject { let ablyMessage = call.arguments as! AblyFlutterMessage let message = ablyMessage.message as! Dictionary let keyLength = message[TxCryptoGenerateRandomKey_keyLength] as! Int; - result(ARTCrypto.generateRandomKey(UInt(keyLength))); + result(Crypto.generateRandomKey(UInt(keyLength))); } } diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/FlutterHandler.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/FlutterHandler.swift new file mode 100644 index 000000000..496016a90 --- /dev/null +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/FlutterHandler.swift @@ -0,0 +1,7 @@ +import AblyFlutterCommon +import Flutter +import Foundation + +public typealias FlutterHandler = (_ plugin: AblyFlutterPluginContext, _ call: FlutterMethodCall, _ result: @escaping FlutterResult) -> Void + +import Foundation diff --git a/ios/Classes/handlers/PushActivationEventHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift similarity index 80% rename from ios/Classes/handlers/PushActivationEventHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift index aeb242c38..d67dd309d 100644 --- a/ios/Classes/handlers/PushActivationEventHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift @@ -1,11 +1,13 @@ +import AblyPubSubDevice +import AblyFlutterCommon +import Flutter import Foundation -import Ably // This class provide results back to Dart side in 2 ways, simultaneously: // 1. Return value to activate(). To do this, we need to give it `result`. Users can await method() and expect the result to throw or succeed. // 2. Send the value to the handlers on the dart side which the user set. Users set a callback // on the dart side when the application launches. -public class PushActivationEventHandlers: NSObject, ARTPushRegistererDelegate { +public class PushActivationEventHandlers: NSObject, PushRegistererDelegate { init(methodChannel: FlutterMethodChannel) { self.methodChannel = methodChannel @@ -23,7 +25,7 @@ public class PushActivationEventHandlers: NSObject, ARTPushRegistererDelegate { // These fields are set in [PushHandlers.swift] when the method call is invoked public var flutterResultForActivate: FlutterResult? = nil; public var flutterResultForDeactivate: FlutterResult? = nil; - // There is no result stored for didAblyPushRegistrationFail, because there is no dart side method call to return values to. + // There is no result stored for didUpdateAblyPush, because there is no dart side method call to return values to. // MethodChannel to send result to handlers implemented in dart side. This provides values to the listeners // implement on the dart side. We need this for activate and deactivate because the new token can be provided. @@ -31,7 +33,7 @@ public class PushActivationEventHandlers: NSObject, ARTPushRegistererDelegate { // and the return values will be sufficient. private var methodChannel: FlutterMethodChannel - public func didActivateAblyPush(_ error: ARTErrorInfo?) { + public func didActivateAblyPush(_ error: ErrorInfo?) { defer { flutterResultForActivate = nil } @@ -44,7 +46,7 @@ public class PushActivationEventHandlers: NSObject, ARTPushRegistererDelegate { } } - public func didDeactivateAblyPush(_ error: ARTErrorInfo?) { + public func didDeactivateAblyPush(_ error: ErrorInfo?) { defer { flutterResultForDeactivate = nil } @@ -57,7 +59,10 @@ public class PushActivationEventHandlers: NSObject, ARTPushRegistererDelegate { } } - public func didAblyPushRegistrationFail(_ error: ARTErrorInfo?) { + // Called after every update of the device registration, with `nil` when it + // succeeded. Only failures are reported to the Dart side. + public func didUpdateAblyPush(_ error: ErrorInfo?) { + guard let error else { return } methodChannel.invokeMethod(AblyPlatformMethod_pushOnUpdateFailed, arguments: error, result: nil) } } diff --git a/ios/Classes/handlers/PushHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift similarity index 75% rename from ios/Classes/handlers/PushHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift index 7ffdc13ef..287cd5484 100644 --- a/ios/Classes/handlers/PushHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift @@ -1,3 +1,6 @@ +import AblyPubSubDevice +import AblyFlutterCommon +import Flutter import Foundation import UserNotifications @@ -26,7 +29,7 @@ public class PushHandlers: NSObject { } @objc - public static let getNotificationSettings: (_ ably: AblyFlutter, _ call: FlutterMethodCall, _ result: @escaping (_ result: Any?) -> Void) -> Void = { ably, call, result in + public static let getNotificationSettings: (_ ably: AblyFlutterPluginContext, _ call: FlutterMethodCall, _ result: @escaping (_ result: Any?) -> Void) -> Void = { ably, call, result in UNUserNotificationCenter.current().getNotificationSettings { settings in result(settings) } @@ -85,12 +88,9 @@ public class PushHandlers: NSObject { public static let device: FlutterHandler = { ably, call, result in let ablyMessage = call.arguments as! AblyFlutterMessage let realtime = ably.instanceStore.realtime(from: ablyMessage.handle) - let rest = ably.instanceStore.rest(from: ablyMessage.handle) if let realtime = realtime { result(realtime.device) - } else if let rest = rest { - result(rest.device) } } @@ -100,19 +100,22 @@ public class PushHandlers: NSObject { let dataMap = ablyMessage.message as! Dictionary let params = dataMap[TxTransportKeys_params] as! Dictionary + // ably-pubsub-cocoa raises NSInvalidArgumentException, which Swift cannot + // catch, unless exactly one of deviceId and clientId is given. + guard (params["deviceId"] != nil) != (params["clientId"] != nil) else { + result(FlutterError(code: "listSubscriptions_error", message: "Exactly one of deviceId and clientId must be given to list push channel subscriptions", details: nil)) + return + } + if let pushChannel = getPushChannel(ably: ably, call: call, result: result) { - do { - try pushChannel.listSubscriptions(params, callback: { paginatedSubscription, errorInfo in - if let errorInfo = errorInfo { - result(FlutterError(code: String(errorInfo.code), message: "Error listing subscriptions from push Channel \(pushChannel); err = \(errorInfo.message)", details: nil)) - return - } - let handle = ably.instanceStore.setPaginatedResult(paginatedSubscription as! ARTPaginatedResult, handle: nil) - result(AblyFlutterMessage(message: paginatedSubscription as Any, handle: handle)) - }) - } catch { - result(FlutterError(code: "listSubscriptions_error", message: "Error listing subscriptions from push Channel \(pushChannel); err = \(error.localizedDescription)", details: nil)) - } + pushChannel.listSubscriptions(params, callback: { paginatedSubscription, errorInfo in + if let errorInfo = errorInfo { + result(FlutterError(code: String(errorInfo.code), message: "Error listing subscriptions from push Channel \(pushChannel); err = \(errorInfo.message)", details: nil)) + return + } + let handle = ably.instanceStore.setPaginatedResult(paginatedSubscription as! PaginatedResult, handle: nil) + result(AblyFlutterMessage(message: paginatedSubscription as Any, handle: handle)) + }) } } @@ -150,35 +153,31 @@ public class PushHandlers: NSObject { @objc public static let pushNotificationTapLaunchedAppFromTerminated: FlutterHandler = { ably, call, result in - if let data = pushNotificationTapLaunchedAppFromTerminatedData { - result(pushNotificationTapLaunchedAppFromTerminatedData) - } + // Returned once, like on Android, and nil when the app was not + // launched by a notification tap. + result(pushNotificationTapLaunchedAppFromTerminatedData) + pushNotificationTapLaunchedAppFromTerminatedData = nil } - /// Gets the client.push property from ARTRealtime or ARTRest when the call contains a handle. - private static func getPush(instanceStore: AblyInstanceStore, call: FlutterMethodCall, result: @escaping FlutterResult) -> ARTPush? { + /// Gets the client.push property from PubSubClient when the call contains a handle. + private static func getPush(instanceStore: AblyInstanceStore, call: FlutterMethodCall, result: @escaping FlutterResult) -> Push? { let ablyMessage = call.arguments as! AblyFlutterMessage let realtime = instanceStore.realtime(from: ablyMessage.handle) - let rest = instanceStore.rest(from: ablyMessage.handle) if let realtime = realtime { return realtime.push; } - if let rest = rest { - return rest.push; - } - - result(FlutterError(code: String(40000), message: "No ably client exists (rest or realtime)", details: nil)) + result(FlutterError(code: String(40000), message: "No ably client exists", details: nil)) return nil; } - /// Gets the client.channels.get(channelName).push property from ARTRealtime or ARTRest + /// Gets the client.channels.get(channelName).push property from PubSubClient /// when the call contains the clients handle and a channelName. /// - /// The dart side can provide a handle (Int) which gets a ARTRealtime or ARTRest Ably client. + /// The dart side can provide a handle (Int) which gets a PubSubClient Ably client. /// This function will callback the with the push channel for the channelName and client handle you provide. - private static func getPushChannel(ably: AblyFlutter, call: FlutterMethodCall, result: @escaping FlutterResult) -> ARTPushChannel? { + private static func getPushChannel(ably: AblyFlutterPluginContext, call: FlutterMethodCall, result: @escaping FlutterResult) -> PushChannel? { let ablyMessage = call.arguments as! AblyFlutterMessage /// Using `guard let handle` in that case caused compilation errors for some users @@ -200,13 +199,10 @@ public class PushHandlers: NSObject { } let instanceStore = ably.instanceStore - let realtime = instanceStore.realtime(from: handle) - if let realtime = realtime { + if let realtime = instanceStore.realtime(from: handle) { return realtime.channels.get(channelName).push - } else if let rest = instanceStore.rest(from: handle) { - return rest.channels.get(channelName).push } else { - result(FlutterError(code: "getAblyPushChannel_error", message: "No ably client (rest or realtime) exists for that handle.", details: nil)) + result(FlutterError(code: "getAblyPushChannel_error", message: "No ably client exists for that handle.", details: nil)) return nil } } diff --git a/ios/Classes/AblyFlutter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m similarity index 69% rename from ios/Classes/AblyFlutter.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m index d060fe5a0..43d65743a 100644 --- a/ios/Classes/AblyFlutter.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m @@ -1,15 +1,11 @@ -@import Ably; +@import AblyPubSubDevice; +@import AblyFlutterCommon; +@import AblyFlutterSwift; #import "AblyFlutter.h" -#import #import "codec/AblyFlutterReaderWriter.h" -#import "AblyFlutterMessage.h" -#import "AblyInstanceStore.h" #import "AblyFlutterStreamHandler.h" -#import "AblyStreamsChannel.h" -#import "codec/AblyPlatformConstants.h" -#import "ARTLog.h" #define LOG(fmt, ...) NSLog((@"%@:%d " fmt), [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, ##__VA_ARGS__) @@ -33,7 +29,7 @@ -(void)reset; }; static const FlutterHandler _getVersion = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - result([@"CocoaPod " stringByAppendingString:[ARTDefault libraryVersion]]); + result([@"ably-pubsub-cocoa " stringByAppendingString:[ARTDefault libraryVersion]]); }; static const FlutterHandler _resetAblyClients = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { @@ -41,192 +37,6 @@ -(void)reset; result(nil); }; -static const FlutterHandler _createRest = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - NSMutableDictionary *const message = ablyMessage.message; - AblyFlutterClientOptions *const options = (AblyFlutterClientOptions*) message[TxTransportKeys_options]; - - options.clientOptions.pushRegistererDelegate = [PushActivationEventHandlers getInstanceWithMethodChannel: ably.channel]; - ARTLog *const logger = [[ARTLog alloc] init]; - logger.logLevel = options.clientOptions.logLevel; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - NSNumber *const handle = [instanceStore getNextHandle]; - - if(options.hasAuthCallback){ - options.clientOptions.authCallback = - ^(ARTTokenParams *tokenParams, void(^callback)(id, NSError *)){ - AblyFlutterMessage *const message = [[AblyFlutterMessage alloc] initWithMessage:tokenParams handle: handle]; - [ably.channel invokeMethod:AblyPlatformMethod_authCallback - arguments:message - result:^(id tokenData){ - if (!tokenData) { - [logger log:[NSString stringWithFormat:@"No token data received %@", tokenData] withLevel: ARTLogLevelWarn]; - callback(nil, [NSError errorWithDomain:ARTAblyErrorDomain - code:ARTErrorAuthConfiguredProviderFailure userInfo:nil]); - } if ([tokenData isKindOfClass:[FlutterError class]]) { - [logger log:[NSString stringWithFormat:@"Error getting token data %@", tokenData] withLevel: ARTLogLevelError]; - callback(nil, tokenData); - } else { - callback(tokenData, nil); - } - }]; - }; - } - ARTRest *const rest = [[ARTRest alloc] initWithOptions:options.clientOptions]; - [instanceStore setRest:rest with: handle]; - - NSData *const apnsDeviceToken = ably.instanceStore.didRegisterForRemoteNotificationsWithDeviceToken_deviceToken; - NSError *const error = ably.instanceStore.didFailToRegisterForRemoteNotificationsWithError_error; - if (apnsDeviceToken != nil) { - [ARTPush didRegisterForRemoteNotificationsWithDeviceToken:apnsDeviceToken rest:rest]; - } else if (error != nil) { - [ARTPush didFailToRegisterForRemoteNotificationsWithError:error rest:rest]; - } - - result(handle); -}; - -static const FlutterHandler _setRestChannelOptions = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - NSMutableDictionary *const message = ablyMessage.message; - NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; - ARTChannelOptions *const channelOptions = (ARTChannelOptions*) message[TxTransportKeys_options]; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRest *const rest = [instanceStore restFrom:ablyMessage.handle]; - ARTRestChannel *const channel = [rest.channels get:channelName]; - - [channel setOptions:channelOptions]; - result(nil); -}; - -static const FlutterHandler _publishRestMessage = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - NSMutableDictionary *const message = ablyMessage.message; - NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; - NSArray *const messages = (NSArray*) message[TxTransportKeys_messages]; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRest *const rest = [instanceStore restFrom:ablyMessage.handle]; - ARTRestChannel *const channel = [rest.channels get:channelName]; - - [channel publish:messages callback:^(ARTErrorInfo *_Nullable error){ - if(error){ - result([ - FlutterError - errorWithCode:[NSString stringWithFormat: @"%ld", (long)error.code] - message:[NSString stringWithFormat:@"Error publishing rest message; err = %@", [error message]] - details:error - ]); - }else{ - result(nil); - } - }]; -}; - -static const FlutterHandler _getRestHistory = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - NSMutableDictionary *const message = ablyMessage.message; - NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; - ARTDataQuery *const dataQuery = (ARTDataQuery*) message[TxTransportKeys_params]; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRest *const rest = [instanceStore restFrom:ablyMessage.handle]; - ARTRestChannel *const channel = [rest.channels get:channelName]; - - const id callback = ^(ARTPaginatedResult * _Nullable paginatedResult, ARTErrorInfo * _Nullable error) { - if(error){ - result([ - FlutterError - errorWithCode:[NSString stringWithFormat: @"%ld", (long)error.code] - message:[NSString stringWithFormat:@"Error getting rest channel history; err = %@", [error message]] - details:error - ]); - }else{ - NSNumber *const paginatedResultHandle = [instanceStore setPaginatedResult:paginatedResult handle:nil]; - result([[AblyFlutterMessage alloc] initWithMessage:paginatedResult handle: paginatedResultHandle]); - } - }; - if (dataQuery) { - [channel history:dataQuery callback:callback error: nil]; - } else { - [channel history:callback]; - } -}; - -static const FlutterHandler _getRestPresence = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - NSMutableDictionary *const message = ablyMessage.message; - NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; - ARTPresenceQuery *const dataQuery = (ARTPresenceQuery*) message[TxTransportKeys_params]; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRest *const rest = [instanceStore restFrom:ablyMessage.handle]; - ARTRestChannel *const channel = [rest.channels get:channelName]; - - const id callback = ^(ARTPaginatedResult * _Nullable paginatedResult, ARTErrorInfo * _Nullable error) { - if(error){ - result([ - FlutterError - errorWithCode:[NSString stringWithFormat: @"%ld", (long)error.code] - message:[NSString stringWithFormat:@"Error getting rest channel presence; err = %@", [error message]] - details:error - ]); - }else{ - NSNumber *const paginatedResultHandle = [instanceStore setPaginatedResult:paginatedResult handle:nil]; - result([[AblyFlutterMessage alloc] initWithMessage:paginatedResult handle: paginatedResultHandle]); - } - }; - if (dataQuery) { - [[channel presence] get:dataQuery callback:callback error:nil]; - } else { - [[channel presence] get:callback]; - } -}; - -static const FlutterHandler _getRestPresenceHistory = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - NSMutableDictionary *const message = ablyMessage.message; - NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; - ARTPresenceQuery *const dataQuery = (ARTPresenceQuery*) message[TxTransportKeys_params]; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRest *const rest = [instanceStore restFrom:ablyMessage.handle]; - ARTRestChannel *const channel = [rest.channels get:channelName]; - - const id callback = ^(ARTPaginatedResult * _Nullable paginatedResult, ARTErrorInfo * _Nullable error) { - if(error){ - result([ - FlutterError - errorWithCode:[NSString stringWithFormat: @"%ld", (long)error.code] - message:[NSString stringWithFormat:@"Error getting rest channel presence; err = %@", [error message]] - details:error - ]); - }else{ - NSNumber *const paginatedResultHandle = [instanceStore setPaginatedResult:paginatedResult handle:nil]; - result([[AblyFlutterMessage alloc] initWithMessage:paginatedResult handle: paginatedResultHandle]); - } - }; - if (dataQuery) { - [[channel presence] history:dataQuery callback:callback error:nil]; - } else { - [[channel presence] history:callback]; - } -}; - -static const FlutterHandler _releaseRestChannel = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - NSDictionary *const message = ablyMessage.message; - NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRest *const rest = [instanceStore restFrom:ablyMessage.handle]; - - [rest.channels release:channelName]; - result(nil); -}; - static const FlutterHandler _createRealtime = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { AblyFlutterMessage *const ablyMessage = call.arguments; NSMutableDictionary *const message = ablyMessage.message; @@ -251,7 +61,7 @@ -(void)reset; [logger log:[NSString stringWithFormat:@"No token data received %@", tokenData] withLevel: ARTLogLevelWarn]; callback(nil, [NSError errorWithDomain:ARTAblyErrorDomain code:ARTErrorAuthConfiguredProviderFailure userInfo:nil]); - } if ([tokenData isKindOfClass:[FlutterError class]]) { + } else if ([tokenData isKindOfClass:[FlutterError class]]) { [logger log:[NSString stringWithFormat:@"Error getting token data %@", tokenData] withLevel: ARTLogLevelError]; callback(nil, tokenData); } else { @@ -260,20 +70,19 @@ -(void)reset; }]; }; } - ARTRealtime *const realtime = [[ARTRealtime alloc] initWithOptions:options.clientOptions]; + ARTPubSubClient *const realtime = [ARTPubSubDevice createClientWithOptions:options.clientOptions]; [instanceStore setRealtime:realtime with:handle]; // Giving Ably client the deviceToken registered at device launch (didRegisterForRemoteNotificationsWithDeviceToken). // This is not an ideal solution. We save the deviceToken given in didRegisterForRemoteNotificationsWithDeviceToken and the // error in didFailToRegisterForRemoteNotificationsWithError and pass it to Ably in the first client that is first created. // Ideally, the Ably client doesn't need to be created, and we can pass the deviceToken to Ably like in Ably Java. - // This is similarly repeated for in _createRest NSData *const apnsDeviceToken = ably.instanceStore.didRegisterForRemoteNotificationsWithDeviceToken_deviceToken; NSError *const error = ably.instanceStore.didFailToRegisterForRemoteNotificationsWithError_error; if (apnsDeviceToken != nil) { - [ARTPush didRegisterForRemoteNotificationsWithDeviceToken:apnsDeviceToken realtime:realtime]; + [ARTPush didRegisterForRemoteNotificationsWithDeviceToken:apnsDeviceToken pubsub:realtime]; } else if (error != nil) { - [ARTPush didFailToRegisterForRemoteNotificationsWithError:error realtime:realtime]; + [ARTPush didFailToRegisterForRemoteNotificationsWithError:error pubsub:realtime]; } result(handle); @@ -303,7 +112,7 @@ -(void)reset; NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; [channel attach:^(ARTErrorInfo *_Nullable error){ @@ -326,7 +135,7 @@ -(void)reset; NSString *const channelName = (NSString*) realtimePayload[TxTransportKeys_channelName]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; [channel detach:^(ARTErrorInfo *_Nullable error){ @@ -350,7 +159,7 @@ -(void)reset; NSArray *const messages = (NSArray*) realtimePayload[TxTransportKeys_messages]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; void (^callback)(ARTErrorInfo *_Nullable) = ^(ARTErrorInfo *_Nullable error){ @@ -375,7 +184,7 @@ -(void)reset; ARTRealtimeChannelOptions *const channelOptions = (ARTRealtimeChannelOptions*) realtimePayload[TxTransportKeys_options]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; [channel setOptions:channelOptions callback:^(ARTErrorInfo * _Nullable error) { @@ -399,7 +208,7 @@ -(void)reset; ARTRealtimeHistoryQuery *const dataQuery = (ARTRealtimeHistoryQuery*) message[TxTransportKeys_params]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; const id callback = ^(ARTPaginatedResult * _Nullable paginatedResult, ARTErrorInfo * _Nullable error) { @@ -417,7 +226,7 @@ -(void)reset; }; if (dataQuery) { - [channel history:dataQuery callback:callback error: nil]; + [channel history:dataQuery callback:callback]; } else { [channel history:callback]; } @@ -430,7 +239,7 @@ -(void)reset; ARTRealtimePresenceQuery *const dataQuery = (ARTRealtimePresenceQuery*) message[TxTransportKeys_params]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; const id callback = ^(NSArray * _Nullable presenceMembers, ARTErrorInfo * _Nullable error) { @@ -459,7 +268,7 @@ -(void)reset; ARTRealtimeHistoryQuery *const dataQuery = (ARTRealtimeHistoryQuery*) message[TxTransportKeys_params]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; const id callback = ^(ARTPaginatedResult * _Nullable paginatedResult, ARTErrorInfo * _Nullable error) { @@ -476,7 +285,7 @@ -(void)reset; } }; if (dataQuery) { - [[channel presence] history:dataQuery callback:callback error:nil]; + [[channel presence] history:dataQuery callback:callback]; } else { [[channel presence] history:callback]; } @@ -490,7 +299,7 @@ -(void)reset; const id data = message[TxTransportKeys_data]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; [[channel presence] enterClient:clientId data:data callback:^(ARTErrorInfo * _Nullable error) { @@ -515,7 +324,7 @@ -(void)reset; const id data = message[TxTransportKeys_data]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; [[channel presence] updateClient:clientId data:data callback:^(ARTErrorInfo * _Nullable error) { @@ -540,7 +349,7 @@ -(void)reset; const id data = message[TxTransportKeys_data]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; [[channel presence] leaveClient:clientId data:data callback:^(ARTErrorInfo * _Nullable error) { @@ -563,17 +372,30 @@ -(void)reset; NSString *const channelName = (NSString*) message[TxTransportKeys_channelName]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + if (!realtime) { + result([FlutterError errorWithCode:@"release_error" + message:[NSString stringWithFormat:@"No Ably client with handle %@", ablyMessage.handle] + details:nil]); + return; + } - [realtime.channels release:channelName]; - result(nil); + NSError *error; + if ([realtime.channels release:channelName error:&error]) { + result(nil); + } else { + // 90011 when the channel is not initialized, detached or failed. + result([FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", (long)error.code] + message:error.localizedDescription + details:error]); + } }; static const FlutterHandler _realtimeTime = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { AblyFlutterMessage *const ablyMessage = call.arguments; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; [realtime time:^(NSDate * _Nullable dateTimeResult, NSError * _Nullable error) { if(error){ @@ -584,25 +406,10 @@ -(void)reset; }]; }; -static const FlutterHandler _restTime = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - AblyFlutterMessage *const ablyMessage = call.arguments; - - AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRest *const rest = [instanceStore restFrom:ablyMessage.handle]; - - [rest time:^(NSDate * _Nullable dateTimeResult, NSError * _Nullable error) { - if(error){ - result(error); - }else{ - result(@([@(dateTimeResult.timeIntervalSince1970 *1000) longValue])); - } - }]; -}; - static const FlutterHandler _connectionRecoveryKey = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { AblyFlutterMessage *const ablyMessage = call.arguments; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; NSString *const connectionRecoveryKey = [realtime.connection createRecoveryKey]; result(connectionRecoveryKey); }; @@ -657,7 +464,7 @@ -(void)reset; ARTAuthOptions *const authOptions = (ARTAuthOptions *) message[TxTransportKeys_options]; AblyInstanceStore *const instanceStore = [ably instanceStore]; - ARTRealtime *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; + ARTPubSubClient *const realtime = [instanceStore realtimeFrom:ablyMessage.handle]; [realtime.auth requestToken:tokenParams withOptions:authOptions callback:^(ARTTokenDetails * _Nullable tokenDetails, NSError * _Nullable error) { if (error) { result([ @@ -673,11 +480,19 @@ -(void)reset; }]; }; +// Receives `UISceneDelegate` calls, for apps that adopt the UIScene life cycle. +@interface AblyFlutter () +@end + @implementation AblyFlutter { NSDictionary* _handlers; AblyStreamsChannel* _streamsChannel; FlutterMethodChannel* _channel; PushNotificationEventHandlers* _pushNotificationEventHandlers; + // Set once the launch-time setup in `-setUpAfterLaunch` has run. + BOOL _launchSetUpDone; + // Set once `scene:willConnectToSession:options:` has been called. + BOOL _sceneConnected; } @synthesize instanceStore = _instanceStore; @@ -703,6 +518,16 @@ +(void)registerWithRegistrar:(NSObject*)registrar { // registering method channel with registrar [registrar addMethodCallDelegate:ably channel:methodChannel]; + + // An app that adopts the UIScene life cycle registers plugins once a scene + // has connected, after `application:didFinishLaunchingWithOptions:`. + // Flutter replays that and `scene:willConnectToSession:options:` to the + // plugin, but an app whose scene delegate does not forward them to Flutter + // never delivers them, so run the launch-time setup now. It runs once + // however many of these paths reach it. + if (UIApplication.sharedApplication.connectedScenes.count > 0) { + [ably setUpAfterLaunch]; + } // setting up stream handler factory for eventChannel to handle multiple listeners [streamsChannel setStreamHandlerFactory:^NSObject *(id arguments) { @@ -732,13 +557,6 @@ -(instancetype)initWithChannel:(FlutterMethodChannel *const)channel AblyPlatformMethod_getPlatformVersion: _getPlatformVersion, AblyPlatformMethod_getVersion: _getVersion, AblyPlatformMethod_resetAblyClients: _resetAblyClients, - AblyPlatformMethod_createRest: _createRest, - AblyPlatformMethod_setRestChannelOptions: _setRestChannelOptions, - AblyPlatformMethod_publish: _publishRestMessage, - AblyPlatformMethod_restHistory: _getRestHistory, - AblyPlatformMethod_restPresenceGet: _getRestPresence, - AblyPlatformMethod_restPresenceHistory: _getRestPresenceHistory, - AblyPlatformMethod_releaseRestChannel: _releaseRestChannel, AblyPlatformMethod_createRealtime: _createRealtime, AblyPlatformMethod_setRealtimeChannelOptions: _setRealtimeChannelOptions, AblyPlatformMethod_connectRealtime: _connectRealtime, @@ -756,7 +574,6 @@ -(instancetype)initWithChannel:(FlutterMethodChannel *const)channel AblyPlatformMethod_realtimePresenceLeave: _leaveRealtimePresence, AblyPlatformMethod_releaseRealtimeChannel: _releaseRealtimeChannel, AblyPlatformMethod_realtimeTime:_realtimeTime, - AblyPlatformMethod_restTime:_restTime, // Connection fields AblyPlatformMethod_connectionRecoveryKey:_connectionRecoveryKey, // Push Notifications @@ -779,13 +596,10 @@ -(instancetype)initWithChannel:(FlutterMethodChannel *const)channel AblyPlatformMethod_realtimeAuthCreateTokenRequest: AuthHandlers.realtimeCreateTokenRequest, AblyPlatformMethod_realtimeAuthRequestToken: AuthHandlers.realtimeRequestToken, AblyPlatformMethod_realtimeAuthGetClientId: AuthHandlers.realtimeAuthClientId, - AblyPlatformMethod_restAuthAuthorize: AuthHandlers.restAuthorize, - AblyPlatformMethod_restAuthCreateTokenRequest: AuthHandlers.restCreateTokenRequest, - AblyPlatformMethod_restAuthRequestToken: AuthHandlers.restRequestToken, - AblyPlatformMethod_restAuthGetClientId: AuthHandlers.restAuthClientId }; [registrar addApplicationDelegate:self]; + [registrar addSceneDelegate:self]; return self; } @@ -808,10 +622,26 @@ -(void)reset { [self->_streamsChannel reset]; } -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { +/// Registers for remote notifications. Runs once, from whichever launch +/// callback reaches the plugin first. +/// +/// This runs even when the app sets `AblyFlutterHandlePushNotifications` to +/// `NO`. That flag only stops the plugin from handling received notifications; +/// Ably still needs the APNs device token to activate push. +-(void)setUpAfterLaunch { + if (_launchSetUpDone) { + return; + } + _launchSetUpDone = YES; [[UIApplication sharedApplication] registerForRemoteNotifications]; - // Check if application was launched from a notification tap. - +} + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [self setUpAfterLaunch]; + + // Check if application was launched from a notification tap. An app that + // adopts the UIScene life cycle gets the tap in + // `scene:willConnectToSession:options:` instead. // https://stackoverflow.com/a/21611009/7365866 NSDictionary *notification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; if (notification) { @@ -821,14 +651,38 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( return YES; } +#pragma mark - UISceneDelegate + +/// Returns `NO` even when it reads a notification tap from the connection +/// options: `YES` would tell Flutter the connection was consumed, which skips +/// its cold-start deep link handling and hides the options from other plugins. +- (BOOL)scene:(UIScene *)scene + willConnectToSession:(UISceneSession *)session + options:(nullable UISceneConnectionOptions *)connectionOptions { + [self setUpAfterLaunch]; + + // Only the first scene connects at launch. A scene that connects later, + // such as after iOS discarded a background scene, belongs to an app that + // is already running, so a tap that opens it is delivered as an ordinary + // tap through `userNotificationCenter:didReceiveNotificationResponse:`. + const BOOL isLaunchScene = !_sceneConnected; + _sceneConnected = YES; + UNNotificationResponse *const response = connectionOptions.notificationResponse; + if (isLaunchScene && response) { + PushHandlers.pushNotificationTapLaunchedAppFromTerminatedData = + response.notification.request.content.userInfo; + } + return NO; +} + #pragma mark - Push Notifications Registration - UIApplicationDelegate -/// Save the deviceToken provided so we can pass it to the first Ably client which gets created, in createRealtime or createRest. +/// Save the deviceToken provided so we can pass it to the first Ably client which gets created, in createRealtime. -(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // Set deviceToken on all existing Ably clients, and a property which used for all future Ably clients. [_instanceStore didRegisterForRemoteNotificationsWithDeviceToken: deviceToken]; } -/// Save the error if it occurred during APNs device registration provided so we can pass it to the first Ably client which gets created, in createRealtime or createRest. +/// Save the error if it occurred during APNs device registration provided so we can pass it to the first Ably client which gets created, in createRealtime. - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { // This error will be used when the first Ably client is made. _instanceStore.didFailToRegisterForRemoteNotificationsWithError_error = error; diff --git a/ios/Classes/AblyFlutterStreamHandler.h b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.h similarity index 97% rename from ios/Classes/AblyFlutterStreamHandler.h rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.h index 739c0a55c..f856acee0 100644 --- a/ios/Classes/AblyFlutterStreamHandler.h +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.h @@ -1,5 +1,6 @@ @import Foundation; @import Flutter; +@import AblyFlutterCommon; #import "AblyFlutter.h" @class ARTConnectionStateChange; diff --git a/ios/Classes/AblyFlutterStreamHandler.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m similarity index 93% rename from ios/Classes/AblyFlutterStreamHandler.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m index a8d0cd4db..7cb3f86a9 100644 --- a/ios/Classes/AblyFlutterStreamHandler.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m @@ -1,9 +1,8 @@ -@import Ably.ARTRealtime; +@import AblyPubSubDevice; +@import AblyFlutterCommon; #import "AblyFlutterStreamHandler.h" #import "AblyFlutter.h" -#import "AblyFlutterMessage.h" -#import "codec/AblyPlatformConstants.h" @implementation _AblyConnectionStateChange @@ -48,7 +47,7 @@ - (void) startListening:(AblyFlutterMessage *const)message emitter:(FlutterEvent NSString *const eventName = eventMessage.eventName; @try { if ([AblyPlatformMethod_onRealtimeConnectionStateChanged isEqual: eventName]) { - ARTRealtime *const realtime = [_instanceStore realtimeFrom:handle]; + ARTPubSubClient *const realtime = [_instanceStore realtimeFrom:handle]; listener = [realtime.connection on: ^(ARTConnectionStateChange * const stateChange) { if(![stateChange isKindOfClass:[ARTConnectionStateChange class]]) { emitter([FlutterError errorWithCode:@"AblyFlutterStreamHandler.startListening" @@ -63,7 +62,7 @@ - (void) startListening:(AblyFlutterMessage *const)message emitter:(FlutterEvent } else if ([AblyPlatformMethod_onRealtimeChannelStateChanged isEqual: eventName]) { NSAssert(eventMessage.message, @"event message is missing"); NSMutableDictionary* eventPayload = eventMessage.message; - ARTRealtime *const realtime = [_instanceStore realtimeFrom:handle]; + ARTPubSubClient *const realtime = [_instanceStore realtimeFrom:handle]; NSString *const channelName = (NSString*) eventPayload[TxTransportKeys_channelName]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; @@ -81,7 +80,7 @@ - (void) startListening:(AblyFlutterMessage *const)message emitter:(FlutterEvent } else if ([AblyPlatformMethod_onRealtimeChannelMessage isEqual: eventName]) { NSAssert(eventMessage.message, @"event message is missing"); NSMutableDictionary* eventPayload = eventMessage.message; - ARTRealtime *const realtime = [_instanceStore realtimeFrom:handle]; + ARTPubSubClient *const realtime = [_instanceStore realtimeFrom:handle]; NSString *const channelName = (NSString*) eventPayload[TxTransportKeys_channelName]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; @@ -99,7 +98,7 @@ - (void) startListening:(AblyFlutterMessage *const)message emitter:(FlutterEvent } else if ([AblyPlatformMethod_onRealtimePresenceMessage isEqual: eventName]) { NSAssert(eventMessage.message, @"event message is missing"); NSMutableDictionary* eventPayload = eventMessage.message; - ARTRealtime *const realtime = [_instanceStore realtimeFrom:handle]; + ARTPubSubClient *const realtime = [_instanceStore realtimeFrom:handle]; NSString *const channelName = (NSString*) eventPayload[TxTransportKeys_channelName]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; @@ -139,7 +138,7 @@ - (void) cancelListening:(AblyFlutterMessage *const)message { // left as is as there is no way of propagating this error to flutter side NSAssert(eventMessage.message, @"event message is missing"); NSMutableDictionary* eventPayload = eventMessage.message; - ARTRealtime *const realtime = [_instanceStore realtimeFrom:handle]; + ARTPubSubClient *const realtime = [_instanceStore realtimeFrom:handle]; NSString *const channelName = (NSString*) eventPayload[TxTransportKeys_channelName]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; @@ -147,7 +146,7 @@ - (void) cancelListening:(AblyFlutterMessage *const)message { } else if([AblyPlatformMethod_onRealtimeConnectionStateChanged isEqual: eventName]) { NSAssert(eventMessage.message, @"event message is missing"); NSMutableDictionary* eventPayload = eventMessage.message; - ARTRealtime *const realtime = [_instanceStore realtimeFrom:handle]; + ARTPubSubClient *const realtime = [_instanceStore realtimeFrom:handle]; NSString *const channelName = (NSString*) eventPayload[TxTransportKeys_channelName]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; @@ -155,7 +154,7 @@ - (void) cancelListening:(AblyFlutterMessage *const)message { } else if ([AblyPlatformMethod_onRealtimePresenceMessage isEqual: eventName]) { NSAssert(eventMessage.message, @"event message is missing"); NSMutableDictionary* eventPayload = eventMessage.message; - ARTRealtime *const realtime = [_instanceStore realtimeFrom:handle]; + ARTPubSubClient *const realtime = [_instanceStore realtimeFrom:handle]; NSString *const channelName = (NSString*) eventPayload[TxTransportKeys_channelName]; ARTRealtimeChannel *const channel = [realtime.channels get:channelName]; diff --git a/ios/Resources/PrivacyInfo.xcprivacy b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/PrivacyInfo.xcprivacy similarity index 100% rename from ios/Resources/PrivacyInfo.xcprivacy rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/PrivacyInfo.xcprivacy diff --git a/ios/Classes/codec/AblyFlutterReader.h b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h similarity index 87% rename from ios/Classes/codec/AblyFlutterReader.h rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h index 4a4dbaf9f..ff7c14c5a 100644 --- a/ios/Classes/codec/AblyFlutterReader.h +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h @@ -1,7 +1,6 @@ @import Foundation; @import Flutter; -@import Ably.ARTTokenDetails; -@import Ably.ARTTokenParams; +@import AblyPubSubDevice; NS_ASSUME_NONNULL_BEGIN diff --git a/ios/Classes/codec/AblyFlutterReader.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m similarity index 87% rename from ios/Classes/codec/AblyFlutterReader.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m index 49cdc5a84..c53feed0a 100644 --- a/ios/Classes/codec/AblyFlutterReader.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m @@ -1,9 +1,8 @@ -@import Ably; +@import AblyPubSubDevice; +@import AblyFlutterCommon; +@import AblyFlutterSwift; #import "AblyFlutterReader.h" -#import "AblyFlutterClientOptions.h" -#import "AblyFlutterMessage.h" -#import "AblyPlatformConstants.h" -#import +#import "AblyFlutterVersion.h" static ARTLogLevel _logLevel(NSString *const logLevelString) { if ([logLevelString isEqualToString:TxLogLevelEnum_none]) return ARTLogLevelNone; @@ -32,11 +31,8 @@ + (AblyCodecDecoder) getDecoder:(const NSString*)type { [NSString stringWithFormat:@"%d", CodecTypeMessage]: readChannelMessage, [NSString stringWithFormat:@"%d", CodecTypeTokenDetails]: readTokenDetails, [NSString stringWithFormat:@"%d", CodecTypeTokenRequest]: readTokenRequest, - [NSString stringWithFormat:@"%d", CodecTypeRestChannelOptions]: CryptoCodec.readRestChannelOptions, [NSString stringWithFormat:@"%d", CodecTypeRealtimeChannelOptions]: CryptoCodec.readRealtimeChannelOptions, - [NSString stringWithFormat:@"%d", CodecTypeRestHistoryParams]: readRestHistoryParams, [NSString stringWithFormat:@"%d", CodecTypeRealtimeHistoryParams]: readRealtimeHistoryParams, - [NSString stringWithFormat:@"%d", CodecTypeRestPresenceParams]: readRestPresenceParams, [NSString stringWithFormat:@"%d", CodecTypeRealtimePresenceParams]: readRealtimePresenceParams, [NSString stringWithFormat:@"%d", CodecTypeMessageData]: readMessageData, [NSString stringWithFormat:@"%d", CodecTypeCipherParams]: CryptoCodec.readCipherParams, @@ -129,17 +125,14 @@ use an explicitly received null from Dart (manifesting this side as a nil id) ON_VALUE(^(const id value) { clientOptions.logLevel = _logLevel(value); }, dictionary, TxClientOptions_logLevel); // TODO log handler READ_VALUE(clientOptions, tls, dictionary, TxClientOptions_tls); - READ_VALUE(clientOptions, restHost, dictionary, TxClientOptions_restHost); - READ_VALUE(clientOptions, realtimeHost, dictionary, TxClientOptions_realtimeHost); + READ_VALUE(clientOptions, endpoint, dictionary, TxClientOptions_endpoint); READ_BOOL(clientOptions, autoConnect, dictionary, TxClientOptions_autoConnect); READ_VALUE(clientOptions, useBinaryProtocol, dictionary, TxClientOptions_useBinaryProtocol); READ_VALUE(clientOptions, queueMessages, dictionary, TxClientOptions_queueMessages); READ_VALUE(clientOptions, echoMessages, dictionary, TxClientOptions_echoMessages); READ_VALUE(clientOptions, recover, dictionary, TxClientOptions_recover); - READ_VALUE(clientOptions, environment, dictionary, TxClientOptions_environment); READ_VALUE(clientOptions, idempotentRestPublishing, dictionary, TxClientOptions_idempotentRestPublishing); READ_VALUE(clientOptions, fallbackHosts, dictionary, TxClientOptions_fallbackHosts); - READ_VALUE(clientOptions, fallbackHostsUseDefault, dictionary, TxClientOptions_fallbackHostsUseDefault); ON_VALUE(^(const id value) { clientOptions.transportParams = [AblyFlutterReader transportParamsFromDictionary: value]; }, dictionary, TxClientOptions_transportParams); @@ -260,7 +253,7 @@ +(ARTTokenParams *)tokenParamsFromDictionary: (NSDictionary *) dictionary { return [result copy]; } -static AblyCodecDecoder readChannelMessageExtras = ^id(NSDictionary *const dictionary) { +static AblyCodecDecoder readChannelMessageExtras = ^NSDictionary *(NSDictionary *const dictionary) { return dictionary[TxMessageExtras_extras]; }; @@ -295,27 +288,6 @@ +(ARTTokenParams *)tokenParamsFromDictionary: (NSDictionary *) dictionary { mac:mac]; }; -static AblyCodecDecoder readRestHistoryParams = ^ARTDataQuery*(NSDictionary *const dictionary) { - ARTDataQuery *const query = [ARTDataQuery new]; - ON_VALUE(^(const id value) { - query.start = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]/1000]; - }, dictionary, TxRestHistoryParams_start); - ON_VALUE(^(const id value) { - query.end = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]/1000]; - }, dictionary, TxRestHistoryParams_end); - ON_VALUE(^(NSString const *value) { - query.limit = [value integerValue]; - }, dictionary, TxRestHistoryParams_limit); - ON_VALUE(^(NSString const *value) { - if([@"forwards" isEqual: value]){ - query.direction = ARTQueryDirectionForwards; - } else { - query.direction = ARTQueryDirectionBackwards; - } - }, dictionary, TxRestHistoryParams_direction); - return query; -}; - static AblyCodecDecoder readRealtimeHistoryParams = ^ARTRealtimeHistoryQuery*(NSDictionary *const dictionary) { ARTRealtimeHistoryQuery *const query = [ARTRealtimeHistoryQuery new]; ON_VALUE(^(const id value) { @@ -338,17 +310,6 @@ +(ARTTokenParams *)tokenParamsFromDictionary: (NSDictionary *) dictionary { return query; }; -static AblyCodecDecoder readRestPresenceParams = ^ARTPresenceQuery*(NSDictionary *const dictionary) { - ARTPresenceQuery *const query = [ARTPresenceQuery new]; - ON_VALUE(^(NSString const *value) { - query.limit = [value integerValue]; - }, dictionary, TxRestPresenceParams_limit); - READ_VALUE(query, clientId, dictionary, TxRestPresenceParams_clientId); - READ_VALUE(query, connectionId, dictionary, TxRestPresenceParams_connectionId); - - return query; -}; - static AblyCodecDecoder readRealtimePresenceParams = ^ARTRealtimePresenceQuery*(NSDictionary *const dictionary) { ARTRealtimePresenceQuery *const query = [ARTRealtimePresenceQuery new]; READ_VALUE(query, clientId, dictionary, TxRealtimePresenceParams_clientId); diff --git a/ios/Classes/codec/AblyFlutterReaderWriter.h b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReaderWriter.h similarity index 100% rename from ios/Classes/codec/AblyFlutterReaderWriter.h rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReaderWriter.h diff --git a/ios/Classes/codec/AblyFlutterReaderWriter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReaderWriter.m similarity index 100% rename from ios/Classes/codec/AblyFlutterReaderWriter.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReaderWriter.m diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterVersion.h b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterVersion.h new file mode 100644 index 000000000..81bc7e4b4 --- /dev/null +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterVersion.h @@ -0,0 +1,6 @@ +// The version of this plugin, reported to Ably in the `ably-flutter` agent. +// +// SwiftPM cannot read `pubspec.yaml` the way the podspec used to, so this is +// kept in step with the `version` there by hand, at release time. +// `test/ios_plugin_version_test.dart` fails if the two drift apart. +#define FLUTTER_PACKAGE_PLUGIN_VERSION @"1.2.44" diff --git a/ios/Classes/codec/AblyFlutterWriter.h b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.h similarity index 100% rename from ios/Classes/codec/AblyFlutterWriter.h rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.h diff --git a/ios/Classes/codec/AblyFlutterWriter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m similarity index 98% rename from ios/Classes/codec/AblyFlutterWriter.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m index b2427091a..4bbae55ed 100644 --- a/ios/Classes/codec/AblyFlutterWriter.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m @@ -1,11 +1,10 @@ -@import Ably; +@import AblyPubSubDevice; @import UserNotifications; -#import +@import AblyFlutterCommon; +@import AblyFlutterSwift; #import "AblyFlutterWriter.h" -#import "AblyFlutterMessage.h" #import "AblyFlutterReader.h" -#import "AblyPlatformConstants.h" -#import "AblyFlutterStreamHandler.h" +#import "../AblyFlutterStreamHandler.h" NS_ASSUME_NONNULL_BEGIN @@ -48,10 +47,8 @@ + (UInt8) getType:(id)value{ return CodecTypeUnNotificationSettings; } else if ([value isKindOfClass:[RemoteMessage class]]) { return CodecTypeRemoteMessage; - } else if ([value isKindOfClass:[ARTRealtimeChannelOptions class]]) { - return CodecTypeRealtimeChannelOptions; } else if ([value isKindOfClass:[ARTChannelOptions class]]) { - return CodecTypeRestChannelOptions; + return CodecTypeRealtimeChannelOptions; } else if ([value isKindOfClass:[ARTCipherParams class]]) { return CodecTypeCipherParams; } else if ([value isKindOfClass:[ARTTokenDetails class]]) { diff --git a/ios/Classes/AblyFlutter.h b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/include/ably_pubsub_device_flutter/AblyFlutter.h similarity index 82% rename from ios/Classes/AblyFlutter.h rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/include/ably_pubsub_device_flutter/AblyFlutter.h index db0a1c2ca..67a226d2e 100644 --- a/ios/Classes/AblyFlutter.h +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/include/ably_pubsub_device_flutter/AblyFlutter.h @@ -1,9 +1,9 @@ #import -#import "AblyInstanceStore.h" +@import AblyFlutterCommon; NS_ASSUME_NONNULL_BEGIN -@interface AblyFlutter : NSObject +@interface AblyFlutter : NSObject +(instancetype)new NS_UNAVAILABLE; +(instancetype)init NS_UNAVAILABLE; diff --git a/ios/Tests/AblyStreamsChannelTests.m b/ios/ably_pubsub_device_flutter/Tests/AblyFlutterCommonTests/AblyStreamsChannelTests.m similarity index 99% rename from ios/Tests/AblyStreamsChannelTests.m rename to ios/ably_pubsub_device_flutter/Tests/AblyFlutterCommonTests/AblyStreamsChannelTests.m index 7ef7d6d52..e05d36520 100644 --- a/ios/Tests/AblyStreamsChannelTests.m +++ b/ios/ably_pubsub_device_flutter/Tests/AblyFlutterCommonTests/AblyStreamsChannelTests.m @@ -1,6 +1,6 @@ #import #import -#import "AblyStreamsChannel.h" +@import AblyFlutterCommon; // Mock binary messenger: captures the binaryMessageHandler registered by AblyStreamsChannel, // and optionally throws NSInternalInconsistencyException on sendOnChannel:message: to simulate diff --git a/ios/run_unit_tests.sh b/ios/run_unit_tests.sh new file mode 100755 index 000000000..8f69d7a66 --- /dev/null +++ b/ios/run_unit_tests.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# Runs the plugin's XCTest suite on an iOS simulator. +# +# The plugin's Package.swift depends on a `FlutterFramework` package at +# `../FlutterFramework`. When an app builds the plugin, the Flutter tool puts an +# empty stub there and the app's own build settings supply Flutter.framework. To +# run the tests standalone, this script generates that sibling package itself, +# wrapping the engine's Flutter.xcframework from the active Flutter SDK. +# +# Usage: ios/run_unit_tests.sh [simulator-udid] +# With no UDID, the first available iPhone simulator is used. + +set -euo pipefail + +ios_dir="$(cd "$(dirname "$0")" && pwd)" +framework_package="$ios_dir/FlutterFramework" + +flutter precache --ios >/dev/null +flutter_root="$(flutter --version --machine | python3 -c 'import json, sys; print(json.load(sys.stdin)["flutterRoot"])')" +engine_xcframework="$flutter_root/bin/cache/artifacts/engine/ios/Flutter.xcframework" +if [[ ! -d "$engine_xcframework" ]]; then + echo "error: Flutter engine not found at $engine_xcframework" >&2 + exit 1 +fi + +rm -rf "$framework_package" +mkdir -p "$framework_package/Sources/FlutterFramework" +ln -s "$engine_xcframework" "$framework_package/Flutter.xcframework" +cat > "$framework_package/Sources/FlutterFramework/FlutterFramework.swift" <<'EOF' +@_exported import Flutter +EOF +cat > "$framework_package/Package.swift" <<'EOF' +// swift-tools-version: 5.9 +// Generated by run_unit_tests.sh. Do not edit or commit. +import PackageDescription + +let package = Package( + name: "FlutterFramework", + platforms: [.iOS("15.0")], + products: [ + .library(name: "FlutterFramework", targets: ["FlutterFramework"]) + ], + targets: [ + .target(name: "FlutterFramework", dependencies: ["Flutter"]), + .binaryTarget(name: "Flutter", path: "Flutter.xcframework"), + ] +) +EOF + +simulator_id="${1:-}" +if [[ -z "$simulator_id" ]]; then + # awk reads all of its input rather than exiting early, so simctl cannot be + # killed by SIGPIPE and fail the pipeline. + simulator_id="$(xcrun simctl list devices available | awk ' + /iPhone/ && !id && match($0, /[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}/) { id = substr($0, RSTART, RLENGTH) } + END { print id }')" + if [[ -z "$simulator_id" ]]; then + echo "error: no available iPhone simulator found; pass a simulator UDID as the first argument" >&2 + exit 1 + fi +fi + +cd "$ios_dir/ably_pubsub_device_flutter" +xcodebuild test \ + -scheme ably_pubsub_device_flutter \ + -destination "id=$simulator_id" \ + CODE_SIGNING_ALLOWED=NO diff --git a/lib/ably_flutter.dart b/lib/ably_pubsub_device_flutter.dart similarity index 88% rename from lib/ably_flutter.dart rename to lib/ably_pubsub_device_flutter.dart index 0e4edbc30..72504eb38 100644 --- a/lib/ably_flutter.dart +++ b/lib/ably_pubsub_device_flutter.dart @@ -1,4 +1,4 @@ -library ably_flutter; +library ably_pubsub_device_flutter; export 'src/authentication/authentication.dart'; export 'src/common/common.dart'; @@ -9,5 +9,4 @@ export 'src/message/message.dart'; export 'src/platform/platform.dart'; export 'src/push_notifications/push_notifications.dart'; export 'src/realtime/realtime.dart'; -export 'src/rest/rest.dart'; export 'src/stats/stats.dart'; diff --git a/lib/src/authentication/src/auth.dart b/lib/src/authentication/src/auth.dart index 2bbf9884a..e60e34fc4 100644 --- a/lib/src/authentication/src/auth.dart +++ b/lib/src/authentication/src/auth.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Creates Ably [TokenRequest] objects and obtains Ably Tokens from Ably to /// subsequently issue to less trusted clients. diff --git a/lib/src/authentication/src/auth_options.dart b/lib/src/authentication/src/auth_options.dart index feb3bce91..d3a4f2915 100644 --- a/lib/src/authentication/src/auth_options.dart +++ b/lib/src/authentication/src/auth_options.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Passes authentication-specific properties in authentication requests to /// Ably. @@ -82,8 +82,8 @@ class AuthOptions { /// then the Ably Token issued is [anonymous](https://ably.com/docs/core-features/authentication#identified-clients). bool? useTokenAuth; -// TODO(tiholic) missing token attribute here -// see: https://docs.ably.com/client-lib-development-guide/features/#AO2h + // TODO(tiholic) missing token attribute here + // see: https://docs.ably.com/client-lib-development-guide/features/#AO2h /// @nodoc /// Initializes an instance without any defaults @@ -103,19 +103,6 @@ class AuthOptions { key = null; } } - - /// @nodoc - /// Convenience constructor used to create an AuthOptions object based - /// on the key string obtained from the application dashboard. - /// param [key] - the full key string as obtained from the dashboard - @Deprecated("Use AuthOptions constructor with named 'key' parameter instead") - AuthOptions.fromKey(String key) { - if (key.contains(':')) { - this.key = key; - } else { - tokenDetails = TokenDetails(key); - } - } } /// @nodoc diff --git a/lib/src/authentication/src/client_options.dart b/lib/src/authentication/src/client_options.dart index a0bcf46cc..7c4cdfb70 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -1,7 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; -/// Passes additional client-specific properties to the REST -/// [Rest.new] or the Realtime [Realtime.new] +/// Passes additional client-specific properties to [createClient]. class ClientOptions extends AuthOptions { /// A client ID, used for identifying this client when publishing messages or /// for presence purposes. @@ -14,26 +13,22 @@ class ClientOptions extends AuthOptions { /// with the `clientId` implicit in the token. String? clientId; - /// Controls the log output of the library. This is a function to handle each - /// line of log output. - @Deprecated( - 'Not used, as log messages are handled by the default mechanism ' - 'in the underlying SDK. This instance variable will be removed ' - 'in a future release.', - ) - LogHandler? logHandler; - /// Controls the verbosity of the logs output from the library. Levels include /// `verbose`, `debug`, `info`, `warn` and `error`. LogLevel logLevel = LogLevel.info; - /// Enables a non-default Ably host to be specified. For development - /// environments only. - String? restHost; - - /// Enables a non-default Ably host to be specified for realtime connections. - /// For development environments only. - String? realtimeHost; + /// The Ably endpoint the client connects to, for both REST requests and the + /// realtime connection. + /// + /// - Unset: the default, `main.realtime.ably.net`. + /// - A routing policy name, such as `acme`: `acme.realtime.ably.net`. + /// - A non-production routing policy name, such as `nonprod:sandbox`: + /// `sandbox.realtime.ably-nonprod.net`. + /// - A hostname, such as `localhost` or `ably.example.com`: that host, with + /// no default fallback hosts. + /// + /// Do not put a port in the endpoint; set [port] or [tlsPort] instead. + String? endpoint; /// Enables a non-default Ably port to be specified. For development /// environments only. @@ -82,10 +77,6 @@ class ClientOptions extends AuthOptions { /// [connection state recovery](https://ably.com/docs/realtime/connection/#connection-state-recovery) for further information. String? recover; - /// Enables a [custom environment](https://ably.com/docs/platform-customization) - /// to be used with the Ably service. - String? environment; - /// A list of fallback hosts to be used in the case of an error necessitating /// the use of an alternative host. /// @@ -93,11 +84,6 @@ class ClientOptions extends AuthOptions { /// specify them here. List? fallbackHosts; - /// DEPRECATED: this property is deprecated and will be removed in a future - /// version. Enables default fallback hosts to be used. - @Deprecated('no alternative to this') - bool? fallbackHostsUseDefault; - /// When a [TokenParams] object is provided, it overrides the client library /// defaults when issuing new Ably Tokens or Ably [TokenRequest]s. TokenParams? defaultTokenParams; @@ -173,24 +159,20 @@ class ClientOptions extends AuthOptions { this.defaultTokenParams, int? disconnectedRetryTimeout, bool? echoMessages, - this.environment, + this.endpoint, this.fallbackHosts, int? fallbackRetryTimeout, - this.fallbackHostsUseDefault, int? httpMaxRetryCount, int? httpOpenTimeout, int? httpRequestTimeout, this.idempotentRestPublishing, String? key, - this.logHandler, LogLevel? logLevel, this.port, bool? queryTime, bool? queueMessages, - this.realtimeHost, this.realtimeRequestTimeout, this.recover, - this.restHost, int? suspendedRetryTimeout, bool? tls, this.tlsPort, @@ -199,20 +181,16 @@ class ClientOptions extends AuthOptions { bool? useBinaryProtocol, bool? useTokenAuth, }) : super( - authCallback: authCallback, - authUrl: authUrl, - authMethod: authMethod, - key: key, - tokenDetails: tokenDetails, - authHeaders: authHeaders, - authParams: authParams, - queryTime: queryTime, - useTokenAuth: useTokenAuth, - ) { - /// @nodoc - /// These default value assignments are only required until - /// [ClientOptions.fromKey] is removed, because then defaults can be set - /// directly in the constructor invocation + authCallback: authCallback, + authUrl: authUrl, + authMethod: authMethod, + key: key, + tokenDetails: tokenDetails, + authHeaders: authHeaders, + authParams: authParams, + queryTime: queryTime, + useTokenAuth: useTokenAuth, + ) { this.autoConnect = autoConnect ?? this.autoConnect; this.channelRetryTimeout = channelRetryTimeout ?? this.channelRetryTimeout; this.disconnectedRetryTimeout = @@ -231,21 +209,13 @@ class ClientOptions extends AuthOptions { this.useBinaryProtocol = useBinaryProtocol ?? this.useBinaryProtocol; } - /// @nodoc - /// initializes [ClientOptions] with a key and log level set to info - /// - /// See [AuthOptions.fromKey] for more details - @Deprecated( - "Use ClientOptions constructor with named 'key' parameter instead") - ClientOptions.fromKey(String key) : super.fromKey(key); - -// TODO(tiholic) unimplemented: -// -// (TO3m) logExceptionReportingUrl -// (TO3l6) httpMaxRetryDuration -// (TO3l8) maxMessageSize -// (TO3l9) maxFrameSize -// (TO3o) plugins -// (TO3p) addRequestIds -// (DF1a) connectionStateTtl + // TODO(tiholic) unimplemented: + // + // (TO3m) logExceptionReportingUrl + // (TO3l6) httpMaxRetryDuration + // (TO3l8) maxMessageSize + // (TO3l9) maxFrameSize + // (TO3o) plugins + // (TO3p) addRequestIds + // (DF1a) connectionStateTtl } diff --git a/lib/src/authentication/src/token_details.dart b/lib/src/authentication/src/token_details.dart index b5f2c4f5b..e73bdc59c 100644 --- a/lib/src/authentication/src/token_details.dart +++ b/lib/src/authentication/src/token_details.dart @@ -56,9 +56,9 @@ class TokenDetails { /// Ably ensures that all fields are consistently serialized and deserialized /// across platforms. TokenDetails.fromMap(Map map) - : capability = map['capability'] as String?, - clientId = map['clientId'] as String?, - expires = map['expires'] as int?, - issued = map['issued'] as int?, - token = map['token'] as String?; + : capability = map['capability'] as String?, + clientId = map['clientId'] as String?, + expires = map['expires'] as int?, + issued = map['issued'] as int?, + token = map['token'] as String?; } diff --git a/lib/src/authentication/src/token_params.dart b/lib/src/authentication/src/token_params.dart index df6e6e89d..4c74f1fbe 100644 --- a/lib/src/authentication/src/token_params.dart +++ b/lib/src/authentication/src/token_params.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Defines the properties of an Ably Token. diff --git a/lib/src/authentication/src/token_request.dart b/lib/src/authentication/src/token_request.dart index fdb1cae65..47f5aa38c 100644 --- a/lib/src/authentication/src/token_request.dart +++ b/lib/src/authentication/src/token_request.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:flutter/foundation.dart'; /// Contains the properties of a request for a token to Ably. Tokens are @@ -67,12 +67,11 @@ class TokenRequest { /// Ably ensures that all fields are consistently serialized and deserialized /// across platforms. TokenRequest.fromMap(Map map) - : capability = map['capability'] as String?, - clientId = map['clientId'] as String?, - mac = map['mac'] as String?, - nonce = map['nonce'] as String?, - timestamp = - DateTime.fromMillisecondsSinceEpoch(map['timestamp'] as int), - ttl = map['ttl'] as int?, - keyName = map['keyName'] as String?; + : capability = map['capability'] as String?, + clientId = map['clientId'] as String?, + mac = map['mac'] as String?, + nonce = map['nonce'] as String?, + timestamp = DateTime.fromMillisecondsSinceEpoch(map['timestamp'] as int), + ttl = map['ttl'] as int?, + keyName = map['keyName'] as String?; } diff --git a/lib/src/common/src/channels.dart b/lib/src/common/src/channels.dart index 980cf13d4..be893fc40 100644 --- a/lib/src/common/src/channels.dart +++ b/lib/src/common/src/channels.dart @@ -1,7 +1,7 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; -/// Creates and destroys [RestChannel] and [RealtimeChannel] objects. +/// Creates and destroys [RealtimeChannel] objects. abstract class Channels extends Iterable { /// @nodoc /// stores channel name vs instance of [ChannelType] @@ -14,7 +14,7 @@ abstract class Channels extends Iterable { @protected ChannelType createChannel(String name); - /// Creates a new [RestChannel] or [RealtimeChannel] object, or returns the + /// Creates a new [RealtimeChannel] object, or returns the /// existing channel object, using the channel [name] parameter. ChannelType get(String name) { if (_channels[name] == null) { @@ -37,13 +37,12 @@ abstract class Channels extends Iterable { Iterator get iterator => _ChannelIterator(_channels.values.toList()); - /// Releases a [RestChannel] or [RealtimeChannel] object with a specified + /// Releases a [RealtimeChannel] object with a specified /// [name], deleting it. /// /// It also removes any listeners associated with the channel. A realtime - /// channel should only be released when it is in the `INITIALIZED`, - /// `DETACHED`, or `FAILED` state; releasing a realtime channel in any other - /// state is deprecated and will throw an error in the next major version. + /// channel can only be released when it is in the `INITIALIZED`, + /// `DETACHED`, or `FAILED` state. void release(String name) { _channels.remove(name); } diff --git a/lib/src/common/src/http_paginated_response.dart b/lib/src/common/src/http_paginated_response.dart index 0b9567de9..1d805048e 100644 --- a/lib/src/common/src/http_paginated_response.dart +++ b/lib/src/common/src/http_paginated_response.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// A superset of [PaginatedResult] which represents a page of results plus /// metadata indicating the relative queries available to it. diff --git a/lib/src/crypto/src/cipher_params.dart b/lib/src/crypto/src/cipher_params.dart index d057225bd..d8ffc7698 100644 --- a/lib/src/crypto/src/cipher_params.dart +++ b/lib/src/crypto/src/cipher_params.dart @@ -1,8 +1,8 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Params to configure encryption for a channel. /// -/// Pass this as the cipher constructor argument of [RestChannelOptions.new] or +/// Pass this as the cipher constructor argument of /// [RealtimeChannelOptions.new]. Do not construct [CipherParams] yourself, as /// it internally refers to an instance of [CipherParams] on the platform side /// (Android / iOS). diff --git a/lib/src/crypto/src/crypto.dart b/lib/src/crypto/src/crypto.dart index e0b619b80..602288dd8 100644 --- a/lib/src/crypto/src/crypto.dart +++ b/lib/src/crypto/src/crypto.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Contains the properties required to configure the encryption of [Message] /// payloads. @@ -34,17 +34,17 @@ class Crypto { } else if (key is Uint8List) { ensureSupportedKeyLength(key); } else { - throw AblyException( - message: 'key must be a String or Uint8List.', - ); + throw AblyException(message: 'key must be a String or Uint8List.'); } return Platform().invokePlatformMethodNonNull( PlatformMethod.cryptoGetParams, - AblyMessage(message: { - TxCryptoGetParams.algorithm: defaultAlgorithm, - TxCryptoGetParams.key: key, - }), + AblyMessage( + message: { + TxCryptoGetParams.algorithm: defaultAlgorithm, + TxCryptoGetParams.key: key, + }, + ), ); } @@ -54,9 +54,7 @@ class Crypto { static void ensureSupportedKeyLength(Uint8List key) { if (key.length != keyLength256bits / 8 && key.length != keyLength128bits / 8) { - throw AblyException( - message: 'Key must be 256 bits or 128 bits long.', - ); + throw AblyException(message: 'Key must be 256 bits or 128 bits long.'); } } @@ -68,11 +66,8 @@ class Crypto { /// default algorithm: for AES this is 256 bits. static Future generateRandomKey({ int keyLength = defaultKeyLengthInBits, - }) => - Platform().invokePlatformMethodNonNull( - PlatformMethod.cryptoGenerateRandomKey, - AblyMessage(message: { - TxCryptoGenerateRandomKey.keyLength: keyLength, - }), - ); + }) => Platform().invokePlatformMethodNonNull( + PlatformMethod.cryptoGenerateRandomKey, + AblyMessage(message: {TxCryptoGenerateRandomKey.keyLength: keyLength}), + ); } diff --git a/lib/src/error/src/ably_exception.dart b/lib/src/error/src/ably_exception.dart index 99cb21a45..a886cbed7 100644 --- a/lib/src/error/src/ably_exception.dart +++ b/lib/src/error/src/ably_exception.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:flutter/services.dart'; /// An exception generated by the native client library called by this plugin @@ -17,17 +17,13 @@ class AblyException implements Exception { final ErrorInfo? errorInfo; /// initializes with no defaults - AblyException({ - this.code, - this.errorInfo, - this.message, - }); + AblyException({this.code, this.errorInfo, this.message}); /// create AblyException from [PlatformException] AblyException.fromPlatformException(PlatformException exception) - : code = exception.code, - errorInfo = exception.details as ErrorInfo?, - message = exception.message; + : code = exception.code, + errorInfo = exception.details as ErrorInfo?, + message = exception.message; @override String toString() { diff --git a/lib/src/error/src/error_info.dart b/lib/src/error/src/error_info.dart index c5686b90c..190ba5897 100644 --- a/lib/src/error/src/error_info.dart +++ b/lib/src/error/src/error_info.dart @@ -36,7 +36,8 @@ class ErrorInfo { }); @override - String toString() => 'ErrorInfo' + String toString() => + 'ErrorInfo' ' message=$message' ' code=$code' ' statusCode=$statusCode' diff --git a/lib/src/generated/platform_constants.dart b/lib/src/generated/platform_constants.dart index 3c3e2d67e..1da98d7fb 100644 --- a/lib/src/generated/platform_constants.dart +++ b/lib/src/generated/platform_constants.dart @@ -16,43 +16,28 @@ class CodecTypes { static const int tokenParams = 135; static const int tokenDetails = 136; static const int tokenRequest = 137; - static const int restChannelOptions = 138; - static const int realtimeChannelOptions = 139; - static const int paginatedResult = 140; - static const int restHistoryParams = 141; - static const int realtimeHistoryParams = 142; - static const int restPresenceParams = 143; - static const int presenceMessage = 144; - static const int realtimePresenceParams = 145; - static const int deviceDetails = 146; - static const int localDevice = 147; - static const int pushChannelSubscription = 148; - static const int unNotificationSettings = 149; - static const int remoteMessage = 150; - static const int errorInfo = 151; - static const int logLevel = 152; - static const int connectionStateChange = 153; - static const int channelStateChange = 154; - static const int cipherParams = 155; + static const int realtimeChannelOptions = 138; + static const int paginatedResult = 139; + static const int realtimeHistoryParams = 140; + static const int presenceMessage = 141; + static const int realtimePresenceParams = 142; + static const int deviceDetails = 143; + static const int localDevice = 144; + static const int pushChannelSubscription = 145; + static const int unNotificationSettings = 146; + static const int remoteMessage = 147; + static const int errorInfo = 148; + static const int logLevel = 149; + static const int connectionStateChange = 150; + static const int channelStateChange = 151; + static const int cipherParams = 152; } class PlatformMethod { static const String getPlatformVersion = 'getPlatformVersion'; static const String getVersion = 'getVersion'; static const String resetAblyClients = 'resetAblyClients'; - static const String authCallback = 'authCallback'; static const String realtimeAuthCallback = 'realtimeAuthCallback'; - static const String createRest = 'createRest'; - static const String setRestChannelOptions = 'setRestChannelOptions'; - static const String publish = 'publish'; - static const String restHistory = 'restHistory'; - static const String restPresenceGet = 'restPresenceGet'; - static const String restPresenceHistory = 'restPresenceHistory'; - static const String releaseRestChannel = 'releaseRestChannel'; - static const String restAuthAuthorize = 'restAuthAuthorize'; - static const String restAuthCreateTokenRequest = 'restAuthCreateTokenRequest'; - static const String restAuthRequestToken = 'restAuthRequestToken'; - static const String restAuthGetClientId = 'restAuthGetClientId'; static const String createRealtime = 'createRealtime'; static const String connectRealtime = 'connectRealtime'; static const String closeRealtime = 'closeRealtime'; @@ -70,15 +55,12 @@ class PlatformMethod { static const String releaseRealtimeChannel = 'releaseRealtimeChannel'; static const String realtimeHistory = 'realtimeHistory'; static const String realtimeTime = 'realtimeTime'; - static const String restTime = 'restTime'; static const String realtimeAuthAuthorize = 'realtimeAuthAuthorize'; static const String realtimeAuthCreateTokenRequest = 'realtimeAuthCreateTokenRequest'; static const String realtimeAuthRequestToken = 'realtimeAuthRequestToken'; static const String realtimeAuthGetClientId = 'realtimeAuthGetClientId'; - static const String connectionRecoveryKey = 'connectionRecoveryKey'; - static const String pushActivate = 'pushActivate'; static const String pushDeactivate = 'pushDeactivate'; static const String pushReset = 'pushReset'; @@ -173,8 +155,7 @@ class TxClientOptions { static const String clientId = 'clientId'; static const String logLevel = 'logLevel'; static const String tls = 'tls'; - static const String restHost = 'restHost'; - static const String realtimeHost = 'realtimeHost'; + static const String endpoint = 'endpoint'; static const String port = 'port'; static const String tlsPort = 'tlsPort'; static const String autoConnect = 'autoConnect'; @@ -182,14 +163,15 @@ class TxClientOptions { static const String queueMessages = 'queueMessages'; static const String echoMessages = 'echoMessages'; static const String recover = 'recover'; - static const String environment = 'environment'; static const String idempotentRestPublishing = 'idempotentRestPublishing'; + static const String disconnectedRetryTimeout = 'disconnectedRetryTimeout'; + static const String suspendedRetryTimeout = 'suspendedRetryTimeout'; + static const String httpMaxRetryDuration = 'httpMaxRetryDuration'; static const String httpOpenTimeout = 'httpOpenTimeout'; static const String httpRequestTimeout = 'httpRequestTimeout'; static const String httpMaxRetryCount = 'httpMaxRetryCount'; static const String realtimeRequestTimeout = 'realtimeRequestTimeout'; static const String fallbackHosts = 'fallbackHosts'; - static const String fallbackHostsUseDefault = 'fallbackHostsUseDefault'; static const String fallbackRetryTimeout = 'fallbackRetryTimeout'; static const String defaultTokenParams = 'defaultTokenParams'; static const String channelRetryTimeout = 'channelRetryTimeout'; @@ -197,10 +179,6 @@ class TxClientOptions { static const String dartVersion = 'dartVersion'; } -class TxRestChannelOptions { - static const String cipherParams = 'cipherParams'; -} - class TxRealtimeChannelOptions { static const String params = 'params'; static const String modes = 'modes'; @@ -354,13 +332,6 @@ class TxPaginatedResult { static const String hasNext = 'hasNext'; } -class TxRestHistoryParams { - static const String start = 'start'; - static const String end = 'end'; - static const String direction = 'direction'; - static const String limit = 'limit'; -} - class TxRealtimeHistoryParams { static const String start = 'start'; static const String end = 'end'; @@ -369,12 +340,6 @@ class TxRealtimeHistoryParams { static const String untilAttach = 'untilAttach'; } -class TxRestPresenceParams { - static const String limit = 'limit'; - static const String clientId = 'clientId'; - static const String connectionId = 'connectionId'; -} - class TxRealtimePresenceParams { static const String waitForSync = 'waitForSync'; static const String clientId = 'clientId'; diff --git a/lib/src/logging/logging.dart b/lib/src/logging/logging.dart index 17777c582..6b798f619 100644 --- a/lib/src/logging/logging.dart +++ b/lib/src/logging/logging.dart @@ -1,2 +1 @@ -export 'src/log_handler.dart'; export 'src/log_level.dart'; diff --git a/lib/src/logging/src/log_handler.dart b/lib/src/logging/src/log_handler.dart deleted file mode 100644 index 2b4d003e9..000000000 --- a/lib/src/logging/src/log_handler.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; - -/// Custom handler to handle SDK log messages -/// -/// For discussion about removing this component see -/// https://github.com/ably/ably-flutter/issues/238 -/// -/// https://docs.ably.com/client-lib-development-guide/features/#TO3c -@Deprecated( - 'All usages of this type are marked as deprecated ' - 'so it will be removed in a future release.', -) -typedef LogHandler = void Function({ - String? msg, - AblyException? exception, -}); diff --git a/lib/src/logging/src/log_level.dart b/lib/src/logging/src/log_level.dart index 347086736..29c9110bb 100644 --- a/lib/src/logging/src/log_level.dart +++ b/lib/src/logging/src/log_level.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Log levels - control verbosity of log messages /// diff --git a/lib/src/message/src/delta_extras.dart b/lib/src/message/src/delta_extras.dart index 12ddeceb3..a85ff7dfe 100644 --- a/lib/src/message/src/delta_extras.dart +++ b/lib/src/message/src/delta_extras.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/src/common/src/object_hash.dart'; -import 'package:ably_flutter/src/generated/platform_constants.dart'; +import 'package:ably_pubsub_device_flutter/src/common/src/object_hash.dart'; +import 'package:ably_pubsub_device_flutter/src/generated/platform_constants.dart'; import 'package:meta/meta.dart'; /// Contains any arbitrary key-value pairs, which may also contain other @@ -17,16 +17,13 @@ class DeltaExtras with ObjectHash { /// create instance from a map @protected DeltaExtras.fromMap(Map value) - : format = value[TxDeltaExtras.format] as String?, - from = value[TxDeltaExtras.from] as String?; + : format = value[TxDeltaExtras.format] as String?, + from = value[TxDeltaExtras.from] as String?; @override bool operator ==(Object other) => other is DeltaExtras && other.from == from && other.format == format; @override - int get hashCode => objectHash([ - from, - format, - ]); + int get hashCode => objectHash([from, format]); } diff --git a/lib/src/message/src/message.dart b/lib/src/message/src/message.dart index d63cae59a..71493ca35 100644 --- a/lib/src/message/src/message.dart +++ b/lib/src/message/src/message.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/common/src/object_hash.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/common/src/object_hash.dart'; import 'package:meta/meta.dart'; /// Contains an individual message that is sent to, or received from, Ably. @@ -61,15 +61,15 @@ class Message with ObjectHash { @override int get hashCode => objectHash([ - id, - name, - encoding, - clientId, - timestamp, - connectionId, - data, - extras, - ]); + id, + name, + encoding, + clientId, + timestamp, + connectionId, + data, + extras, + ]); // https://docs.ably.com/client-lib-development-guide/features/#TM3 // @@ -83,23 +83,21 @@ class Message with ObjectHash { /// data if you have an encrypted channel. Message.fromEncoded( Map jsonObject, [ - RestChannelOptions? channelOptions, - ]) : clientId = jsonObject['clientId'] as String?, - connectionId = jsonObject['connectionId'] as String?, - _data = MessageData.fromValue(jsonObject['data']), - encoding = jsonObject['encoding'] as String?, - extras = MessageExtras.fromMap( - Map.castFrom( - jsonObject['extras'] as Map, - ), - ), - id = jsonObject['id'] as String?, - name = jsonObject['name'] as String?, - timestamp = jsonObject['timestamp'] != null - ? DateTime.fromMillisecondsSinceEpoch( - jsonObject['timestamp'] as int, - ) - : null; + RealtimeChannelOptions? channelOptions, + ]) : clientId = jsonObject['clientId'] as String?, + connectionId = jsonObject['connectionId'] as String?, + _data = MessageData.fromValue(jsonObject['data']), + encoding = jsonObject['encoding'] as String?, + extras = MessageExtras.fromMap( + Map.castFrom( + jsonObject['extras'] as Map, + ), + ), + id = jsonObject['id'] as String?, + name = jsonObject['name'] as String?, + timestamp = jsonObject['timestamp'] != null + ? DateTime.fromMillisecondsSinceEpoch(jsonObject['timestamp'] as int) + : null; /// A static factory method to create an array of Message objects from an /// [jsonArray] of deserialized Message-like object encoded using Ably's wire @@ -107,12 +105,12 @@ class Message with ObjectHash { /// allow the library to decrypt the data if you have an encrypted channel. static List fromEncodedArray( List> jsonArray, [ - RestChannelOptions? channelOptions, - ]) => - jsonArray.map((e) => Message.fromEncoded(e, channelOptions)).toList(); + RealtimeChannelOptions? channelOptions, + ]) => jsonArray.map((e) => Message.fromEncoded(e, channelOptions)).toList(); @override - String toString() => 'Message' + String toString() => + 'Message' ' id=$id' ' name=$name' ' data=$data' @@ -122,5 +120,5 @@ class Message with ObjectHash { ' timestamp=$timestamp' ' connectionId=$connectionId'; -// TODO(tiholic) add support for fromEncoded and fromEncodedArray (TM3) + // TODO(tiholic) add support for fromEncoded and fromEncodedArray (TM3) } diff --git a/lib/src/message/src/message_data.dart b/lib/src/message/src/message_data.dart index 327e81ce3..e6ffdfadc 100644 --- a/lib/src/message/src/message_data.dart +++ b/lib/src/message/src/message_data.dart @@ -8,7 +8,7 @@ class MessageData { /// /// Only Map, List, string and Buffer types are supported. MessageData(this._data) - : assert(T == Map || T == List || T == String || T == Uint8List); + : assert(T == Map || T == List || T == String || T == Uint8List); /// A getter for the [_data] object. T get data => _data; diff --git a/lib/src/message/src/message_extras.dart b/lib/src/message/src/message_extras.dart index 59c139047..179164447 100644 --- a/lib/src/message/src/message_extras.dart +++ b/lib/src/message/src/message_extras.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/common/src/object_hash.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/common/src/object_hash.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; @@ -37,10 +37,7 @@ class MessageExtras with ObjectHash { ? null : DeltaExtras.fromMap(Map.from(deltaMap)); - return MessageExtras._withDelta( - mutableExtrasMap, - deltaExtras, - ); + return MessageExtras._withDelta(mutableExtrasMap, deltaExtras); } @override @@ -53,8 +50,5 @@ class MessageExtras with ObjectHash { other.delta == delta; @override - int get hashCode => objectHash([ - map, - delta, - ]); + int get hashCode => objectHash([map, delta]); } diff --git a/lib/src/message/src/presence_message.dart b/lib/src/message/src/presence_message.dart index e7b25c526..d08d0d4a0 100644 --- a/lib/src/message/src/presence_message.dart +++ b/lib/src/message/src/presence_message.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/common/src/object_hash.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/common/src/object_hash.dart'; import 'package:meta/meta.dart'; /// Contains an individual presence update sent to, or received from, Ably. @@ -68,15 +68,15 @@ class PresenceMessage with ObjectHash { @override int get hashCode => objectHash([ - id, - encoding, - clientId, - timestamp, - connectionId, - data, - action, - extras, - ]); + id, + encoding, + clientId, + timestamp, + connectionId, + data, + action, + extras, + ]); // https://docs.ably.com/client-lib-development-guide/features/#TP4 // @@ -92,24 +92,23 @@ class PresenceMessage with ObjectHash { /// having to parse the encoding string. PresenceMessage.fromEncoded( Map jsonObject, [ - RestChannelOptions? channelOptions, - ]) : id = jsonObject['id'] as String?, - action = PresenceAction.values.firstWhere((e) => - e.toString().split('.')[1] == jsonObject['action'] as String?), - clientId = jsonObject['clientId'] as String?, - connectionId = jsonObject['connectionId'] as String?, - _data = MessageData.fromValue(jsonObject['data']), - encoding = jsonObject['encoding'] as String?, - extras = MessageExtras.fromMap( - Map.castFrom( - jsonObject['extras'] as Map, - ), - ), - timestamp = jsonObject['timestamp'] != null - ? DateTime.fromMillisecondsSinceEpoch( - jsonObject['timestamp'] as int, - ) - : null; + RealtimeChannelOptions? channelOptions, + ]) : id = jsonObject['id'] as String?, + action = PresenceAction.values.firstWhere( + (e) => e.toString().split('.')[1] == jsonObject['action'] as String?, + ), + clientId = jsonObject['clientId'] as String?, + connectionId = jsonObject['connectionId'] as String?, + _data = MessageData.fromValue(jsonObject['data']), + encoding = jsonObject['encoding'] as String?, + extras = MessageExtras.fromMap( + Map.castFrom( + jsonObject['extras'] as Map, + ), + ), + timestamp = jsonObject['timestamp'] != null + ? DateTime.fromMillisecondsSinceEpoch(jsonObject['timestamp'] as int) + : null; /// Decodes and decrypts a [jsonArray] of deserialized PresenceMessage-like /// object using the cipher in [channelOptions]. @@ -120,17 +119,16 @@ class PresenceMessage with ObjectHash { /// having to parse the encoding string. static List fromEncodedArray( List> jsonArray, [ - RestChannelOptions? channelOptions, - ]) => - jsonArray - .map((jsonObject) => PresenceMessage.fromEncoded( - jsonObject, - channelOptions, - )) - .toList(); + RealtimeChannelOptions? channelOptions, + ]) => jsonArray + .map( + (jsonObject) => PresenceMessage.fromEncoded(jsonObject, channelOptions), + ) + .toList(); @override - String toString() => 'PresenceMessage' + String toString() => + 'PresenceMessage' ' id=$id' ' data=$data' ' action=$action' diff --git a/lib/src/platform/platform.dart b/lib/src/platform/platform.dart index 2ba998cf3..e97b41ee9 100644 --- a/lib/src/platform/platform.dart +++ b/lib/src/platform/platform.dart @@ -8,11 +8,7 @@ export 'src/platform.dart'; export 'src/platform_object.dart'; export 'src/realtime/connection.dart'; export 'src/realtime/presence.dart'; -export 'src/realtime/realtime.dart'; +export 'src/realtime/pub_sub_client.dart'; export 'src/realtime/realtime_channel.dart'; export 'src/realtime/realtime_channels.dart'; -export 'src/rest/rest.dart'; -export 'src/rest/rest_channel.dart'; -export 'src/rest/rest_channels.dart'; -export 'src/rest/rest_presence.dart'; export 'src/streams_channel.dart'; diff --git a/lib/src/platform/platform_internal.dart b/lib/src/platform/platform_internal.dart index 101669fda..4d44a1edd 100644 --- a/lib/src/platform/platform_internal.dart +++ b/lib/src/platform/platform_internal.dart @@ -1,5 +1,6 @@ -// This is a file which `ably_flutter.dart` should not export to library users, -// but other library files can import to gain access to internal APIs +// This is a file which `ably_pubsub_device_flutter.dart` should not export +// to library users, but other library files can import to gain access to +// internal APIs export '../generated/platform_constants.dart'; export 'src/background_android_isolate_platform.dart'; diff --git a/lib/src/platform/src/ably_event_message.dart b/lib/src/platform/src/ably_event_message.dart index 607233642..4086c76fb 100644 --- a/lib/src/platform/src/ably_event_message.dart +++ b/lib/src/platform/src/ably_event_message.dart @@ -16,8 +16,5 @@ class AblyEventMessage { /// creates an instance with non-nul [eventName] /// /// [message] is optional - const AblyEventMessage({ - required this.eventName, - this.message, - }); + const AblyEventMessage({required this.eventName, this.message}); } diff --git a/lib/src/platform/src/ably_message.dart b/lib/src/platform/src/ably_message.dart index 6588d80ba..bea8d5171 100644 --- a/lib/src/platform/src/ably_message.dart +++ b/lib/src/platform/src/ably_message.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:meta/meta.dart'; /// @nodoc @@ -6,7 +6,7 @@ import 'package:meta/meta.dart'; @immutable class AblyMessage { /// @nodoc - /// handle of rest/realtime instance + /// handle of realtime instance final int? handle; /// @nodoc @@ -21,31 +21,20 @@ class AblyMessage { /// creates instance with a non-null [message] /// /// [handle] and [type] are optional - const AblyMessage({ - required this.message, - this.handle, - this.type, - }); + const AblyMessage({required this.message, this.handle, this.type}); /// @nodoc /// creates instance with [message] set to empty map /// /// [handle] and [type] are optional - static AblyMessage> empty({ - int? handle, - int? type, - }) => - AblyMessage( - message: const {}, - handle: handle, - type: type, - ); + static AblyMessage> empty({int? handle, int? type}) => + AblyMessage(message: const {}, handle: handle, type: type); /// @nodoc /// Cast ably message from [G] to [T] static AblyMessage castFrom(AblyMessage source) => AblyMessage( - message: source.message as T, - handle: source.handle, - type: source.type, - ); + message: source.message as T, + handle: source.handle, + type: source.type, + ); } diff --git a/lib/src/platform/src/background_android_isolate_platform.dart b/lib/src/platform/src/background_android_isolate_platform.dart index cf743b3b5..6a872bf41 100644 --- a/lib/src/platform/src/background_android_isolate_platform.dart +++ b/lib/src/platform/src/background_android_isolate_platform.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:flutter/services.dart'; /// @nodoc @@ -23,7 +23,9 @@ class BackgroundIsolateAndroidPlatform { return _onPushBackgroundMessage(call.arguments as RemoteMessage); default: throw PlatformException( - code: 'invalid_method', message: 'No such method ${call.method}'); + code: 'invalid_method', + message: 'No such method ${call.method}', + ); } }); } @@ -40,7 +42,9 @@ class BackgroundIsolateAndroidPlatform { /// we explicitly launched when a RemoteMessage is received. /// Used only on Android. final MethodChannel _methodChannel = MethodChannel( - 'io.ably.flutter.plugin.background', StandardMethodCodec(Codec())); + 'io.ably.flutter.plugin.background', + StandardMethodCodec(Codec()), + ); final PushNotificationEventsInternal _pushNotificationEvents = Push.notificationEvents as PushNotificationEventsInternal; diff --git a/lib/src/platform/src/cipher_params_internal.dart b/lib/src/platform/src/cipher_params_internal.dart index 6543a864e..5a778dfbc 100644 --- a/lib/src/platform/src/cipher_params_internal.dart +++ b/lib/src/platform/src/cipher_params_internal.dart @@ -1,7 +1,7 @@ import 'dart:io' as io show Platform; import 'dart:typed_data'; -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// @nodoc /// The internal (non-public) representation of CipherParams. @@ -33,18 +33,14 @@ class CipherParamsInternal implements CipherParams { /// @nodoc /// Create a Dart side representation of CipherParams for iOS devices - CipherParamsInternal.forIOS({ - required this.algorithm, - required this.key, - }) { + CipherParamsInternal.forIOS({required this.algorithm, required this.key}) { if (!io.Platform.isIOS) { - throw AblyException( - message: 'This method is for iOS platforms only', - ); + throw AblyException(message: 'This method is for iOS platforms only'); } if (key == null) { throw AblyException( - message: 'CipherParamsNative on iOS must have a key, as ' + message: + 'CipherParamsNative on iOS must have a key, as ' 'this key is used to regenerate the ARTCipherParams on the ' 'iOS side.', ); @@ -53,15 +49,14 @@ class CipherParamsInternal implements CipherParams { /// @nodoc /// Create a Dart side representation of CipherParams for Android devices - CipherParamsInternal.forAndroid({ - required this.androidHandle, - }) { + CipherParamsInternal.forAndroid({required this.androidHandle}) { if (!io.Platform.isAndroid) { throw AblyException(message: 'This method is for Android platforms only'); } if (androidHandle == null) { throw AblyException( - message: 'CipherParamsNative on Android must have a handle, ' + message: + 'CipherParamsNative on Android must have a handle, ' 'as this key is used to get the previously generated ' 'CipherParams on the Android side', ); diff --git a/lib/src/platform/src/codec.dart b/lib/src/platform/src/codec.dart index 953d7d5b7..c57092d49 100644 --- a/lib/src/platform/src/codec.dart +++ b/lib/src/platform/src/codec.dart @@ -1,6 +1,6 @@ import 'dart:io' as io show Platform; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:flutter/services.dart'; /// @nodoc @@ -27,12 +27,10 @@ class _CodecPair { /// This method will throw an [AblyException] if encoder is null. Map? encode(final Object? value) { if (_encoder == null) { - throw AblyException( - message: 'Codec encoder is null', - ); + throw AblyException(message: 'Codec encoder is null'); } if (value == null) return null; - return _encoder!(value as T); + return _encoder(value as T); } /// @nodoc @@ -41,12 +39,10 @@ class _CodecPair { /// This method will throw an [AblyException] if decoder is null. T? decode(Map? jsonMap) { if (_decoder == null) { - throw AblyException( - message: 'Codec decoder is null', - ); + throw AblyException(message: 'Codec decoder is null'); } if (jsonMap == null) return null; - return _decoder!(jsonMap); + return _decoder(jsonMap); } } @@ -66,52 +62,72 @@ class Codec extends StandardMessageCodec { codecMap = { // Ably flutter plugin protocol message CodecTypes.ablyMessage: _CodecPair>( - _encodeAblyMessage, _decodeAblyMessage), - CodecTypes.ablyEventMessage: - _CodecPair(_encodeAblyEventMessage, null), + _encodeAblyMessage, + _decodeAblyMessage, + ), + CodecTypes.ablyEventMessage: _CodecPair( + _encodeAblyEventMessage, + null, + ), // Other ably objects - CodecTypes.clientOptions: - _CodecPair(_encodeClientOptions, _decodeClientOptions), - CodecTypes.tokenParams: - _CodecPair(_encodeTokenParams, _decodeTokenParams), - CodecTypes.tokenDetails: - _CodecPair(_encodeTokenDetails, _decodeTokenDetails), - CodecTypes.tokenRequest: - _CodecPair(_encodeTokenRequest, _decodeTokenRequest), - CodecTypes.restChannelOptions: - _CodecPair(_encodeRestChannelOptions, null), + CodecTypes.clientOptions: _CodecPair( + _encodeClientOptions, + _decodeClientOptions, + ), + CodecTypes.tokenParams: _CodecPair( + _encodeTokenParams, + _decodeTokenParams, + ), + CodecTypes.tokenDetails: _CodecPair( + _encodeTokenDetails, + _decodeTokenDetails, + ), + CodecTypes.tokenRequest: _CodecPair( + _encodeTokenRequest, + _decodeTokenRequest, + ), CodecTypes.realtimeChannelOptions: _CodecPair( _encodeRealtimeChannelOptions, null, ), CodecTypes.authOptions: _CodecPair(_encodeAuthOptions, null), - CodecTypes.paginatedResult: - _CodecPair>(null, _decodePaginatedResult), - CodecTypes.realtimeHistoryParams: - _CodecPair(_encodeRealtimeHistoryParams, null), - CodecTypes.restHistoryParams: - _CodecPair(_encodeRestHistoryParams, null), - CodecTypes.restPresenceParams: - _CodecPair(_encodeRestPresenceParams, null), + CodecTypes.paginatedResult: _CodecPair>( + null, + _decodePaginatedResult, + ), + CodecTypes.realtimeHistoryParams: _CodecPair( + _encodeRealtimeHistoryParams, + null, + ), CodecTypes.realtimePresenceParams: _CodecPair( _encodeRealtimePresenceParams, null, ), // Push Notifications - CodecTypes.deviceDetails: - _CodecPair(null, _decodeDeviceDetails), + CodecTypes.deviceDetails: _CodecPair( + null, + _decodeDeviceDetails, + ), CodecTypes.localDevice: _CodecPair(null, _decodeLocalDevice), CodecTypes.pushChannelSubscription: _CodecPair( - null, _decodePushChannelSubscription), + null, + _decodePushChannelSubscription, + ), CodecTypes.unNotificationSettings: _CodecPair( - null, _decodeUNNotificationSettings), - CodecTypes.remoteMessage: - _CodecPair(null, _decodeRemoteMessage), + null, + _decodeUNNotificationSettings, + ), + CodecTypes.remoteMessage: _CodecPair( + null, + _decodeRemoteMessage, + ), - CodecTypes.errorInfo: - _CodecPair(_encodeErrorInfo, _decodeErrorInfo), + CodecTypes.errorInfo: _CodecPair( + _encodeErrorInfo, + _decodeErrorInfo, + ), CodecTypes.messageData: _CodecPair?>( _encodeChannelMessageData, _decodeChannelMessageData, @@ -120,23 +136,33 @@ class Codec extends StandardMessageCodec { _encodeChannelMessageExtras, _decodeChannelMessageExtras, ), - CodecTypes.message: - _CodecPair(_encodeChannelMessage, _decodeChannelMessage), - CodecTypes.presenceMessage: - _CodecPair(null, _decodePresenceMessage), + CodecTypes.message: _CodecPair( + _encodeChannelMessage, + _decodeChannelMessage, + ), + CodecTypes.presenceMessage: _CodecPair( + null, + _decodePresenceMessage, + ), // Events - Connection CodecTypes.connectionStateChange: _CodecPair( - null, _decodeConnectionStateChange), + null, + _decodeConnectionStateChange, + ), // Events - Channel - CodecTypes.channelStateChange: - _CodecPair(null, _decodeChannelStateChange), + CodecTypes.channelStateChange: _CodecPair( + null, + _decodeChannelStateChange, + ), // Encryption - CodecTypes.cipherParams: - _CodecPair(_encodeCipherParams, _decodeCipherParams), + CodecTypes.cipherParams: _CodecPair( + _encodeCipherParams, + _decodeCipherParams, + ), }; } @@ -173,18 +199,12 @@ class Codec extends StandardMessageCodec { return CodecTypes.messageData; } else if (value is RealtimeChannelOptions) { return CodecTypes.realtimeChannelOptions; - } else if (value is RestChannelOptions) { - return CodecTypes.restChannelOptions; } else if (value is MessageExtras) { return CodecTypes.messageExtras; } else if (value is Message) { return CodecTypes.message; } else if (value is RealtimeHistoryParams) { return CodecTypes.realtimeHistoryParams; - } else if (value is RestHistoryParams) { - return CodecTypes.restHistoryParams; - } else if (value is RestPresenceParams) { - return CodecTypes.restPresenceParams; } else if (value is RealtimePresenceParams) { return CodecTypes.realtimePresenceParams; } else if (value is ErrorInfo) { @@ -248,8 +268,11 @@ class Codec extends StandardMessageCodec { _writeToJson(jsonMap, TxClientOptions.authUrl, v.authUrl); _writeToJson(jsonMap, TxClientOptions.authMethod, v.authMethod); _writeToJson(jsonMap, TxClientOptions.key, v.key); - _writeToJson(jsonMap, TxClientOptions.tokenDetails, - _encodeTokenDetails(v.tokenDetails)); + _writeToJson( + jsonMap, + TxClientOptions.tokenDetails, + _encodeTokenDetails(v.tokenDetails), + ); _writeToJson(jsonMap, TxClientOptions.authHeaders, v.authHeaders); _writeToJson(jsonMap, TxClientOptions.authParams, v.authParams); _writeToJson(jsonMap, TxClientOptions.queryTime, v.queryTime); @@ -259,41 +282,60 @@ class Codec extends StandardMessageCodec { // ClientOptions _writeToJson(jsonMap, TxClientOptions.clientId, v.clientId); _writeToJson( - jsonMap, TxClientOptions.logLevel, _encodeLogLevel(v.logLevel)); - //TODO handle logHandler + jsonMap, + TxClientOptions.logLevel, + _encodeLogLevel(v.logLevel), + ); _writeToJson(jsonMap, TxClientOptions.tls, v.tls); - _writeToJson(jsonMap, TxClientOptions.restHost, v.restHost); - _writeToJson(jsonMap, TxClientOptions.realtimeHost, v.realtimeHost); + _writeToJson(jsonMap, TxClientOptions.endpoint, v.endpoint); _writeToJson(jsonMap, TxClientOptions.port, v.port); _writeToJson(jsonMap, TxClientOptions.tlsPort, v.tlsPort); _writeToJson(jsonMap, TxClientOptions.autoConnect, v.autoConnect); _writeToJson( - jsonMap, TxClientOptions.useBinaryProtocol, v.useBinaryProtocol); + jsonMap, + TxClientOptions.useBinaryProtocol, + v.useBinaryProtocol, + ); _writeToJson(jsonMap, TxClientOptions.queueMessages, v.queueMessages); _writeToJson(jsonMap, TxClientOptions.echoMessages, v.echoMessages); _writeToJson(jsonMap, TxClientOptions.recover, v.recover); - _writeToJson(jsonMap, TxClientOptions.environment, v.environment); - _writeToJson(jsonMap, TxClientOptions.idempotentRestPublishing, - v.idempotentRestPublishing); + _writeToJson( + jsonMap, + TxClientOptions.idempotentRestPublishing, + v.idempotentRestPublishing, + ); _writeToJson(jsonMap, TxClientOptions.httpOpenTimeout, v.httpOpenTimeout); _writeToJson( - jsonMap, TxClientOptions.httpRequestTimeout, v.httpRequestTimeout); + jsonMap, + TxClientOptions.httpRequestTimeout, + v.httpRequestTimeout, + ); + _writeToJson( + jsonMap, + TxClientOptions.httpMaxRetryCount, + v.httpMaxRetryCount, + ); _writeToJson( - jsonMap, TxClientOptions.httpMaxRetryCount, v.httpMaxRetryCount); - _writeToJson(jsonMap, TxClientOptions.realtimeRequestTimeout, - v.realtimeRequestTimeout); + jsonMap, + TxClientOptions.realtimeRequestTimeout, + v.realtimeRequestTimeout, + ); _writeToJson(jsonMap, TxClientOptions.fallbackHosts, v.fallbackHosts); _writeToJson( - jsonMap, - TxClientOptions.fallbackHostsUseDefault, - // ignore: deprecated_member_use_from_same_package - v.fallbackHostsUseDefault); + jsonMap, + TxClientOptions.fallbackRetryTimeout, + v.fallbackRetryTimeout, + ); _writeToJson( - jsonMap, TxClientOptions.fallbackRetryTimeout, v.fallbackRetryTimeout); - _writeToJson(jsonMap, TxClientOptions.defaultTokenParams, - _encodeTokenParams(v.defaultTokenParams)); + jsonMap, + TxClientOptions.defaultTokenParams, + _encodeTokenParams(v.defaultTokenParams), + ); _writeToJson( - jsonMap, TxClientOptions.channelRetryTimeout, v.channelRetryTimeout); + jsonMap, + TxClientOptions.channelRetryTimeout, + v.channelRetryTimeout, + ); _writeToJson(jsonMap, TxClientOptions.transportParams, v.transportParams); _writeToJson(jsonMap, TxClientOptions.dartVersion, dartVersion()); return jsonMap; @@ -338,21 +380,14 @@ class Codec extends StandardMessageCodec { _writeToJson(jsonMap, TxTokenRequest.mac, v.mac); _writeToJson(jsonMap, TxTokenRequest.nonce, v.nonce); _writeToJson( - jsonMap, TxTokenRequest.timestamp, v.timestamp?.millisecondsSinceEpoch); + jsonMap, + TxTokenRequest.timestamp, + v.timestamp?.millisecondsSinceEpoch, + ); _writeToJson(jsonMap, TxTokenRequest.ttl, v.ttl); return jsonMap; } - /// @nodoc - /// Encodes [RestChannelOptions] to a Map - /// returns null if [v] is null - Map _encodeRestChannelOptions(final RestChannelOptions v) { - final jsonMap = {}; - jsonMap[TxRestChannelOptions.cipherParams] = - _encodeCipherParams(v.cipherParams); - return jsonMap; - } - /// @nodoc /// Encodes [ChannelMode] to a string constant String _encodeChannelMode(ChannelMode mode) { @@ -372,10 +407,12 @@ class Codec extends StandardMessageCodec { /// Encodes [RealtimeChannelOptions] to a Map /// returns null if [v] is null Map _encodeRealtimeChannelOptions( - final RealtimeChannelOptions v) { + final RealtimeChannelOptions v, + ) { final jsonMap = {}; - jsonMap[TxRealtimeChannelOptions.cipherParams] = - _encodeCipherParams(v.cipherParams); + jsonMap[TxRealtimeChannelOptions.cipherParams] = _encodeCipherParams( + v.cipherParams, + ); _writeToJson(jsonMap, TxRealtimeChannelOptions.params, v.params); _writeToJson( jsonMap, @@ -387,8 +424,9 @@ class Codec extends StandardMessageCodec { Map _encodeAuthOptions(final AuthOptions authOptions) { final jsonMap = {}; - jsonMap[TxAuthOptions.tokenDetails] = - _encodeTokenDetails(authOptions.tokenDetails); + jsonMap[TxAuthOptions.tokenDetails] = _encodeTokenDetails( + authOptions.tokenDetails, + ); jsonMap[TxAuthOptions.authUrl] = authOptions.authUrl; jsonMap[TxAuthOptions.authMethod] = authOptions.authMethod; jsonMap[TxAuthOptions.key] = authOptions.key; @@ -418,55 +456,27 @@ class Codec extends StandardMessageCodec { CipherParams _decodeCipherParams(final Map jsonMap) { if (io.Platform.isAndroid) { final cipherParamsHandle = jsonMap[TxCipherParams.androidHandle] as int; - return CipherParamsInternal.forAndroid( - androidHandle: cipherParamsHandle, - ); + return CipherParamsInternal.forAndroid(androidHandle: cipherParamsHandle); } else if (io.Platform.isIOS) { final algorithm = jsonMap[TxCipherParams.iosAlgorithm] as String; final key = jsonMap[TxCipherParams.iosKey] as Uint8List; - return CipherParamsInternal.forIOS( - algorithm: algorithm, - key: key, - ); + return CipherParamsInternal.forIOS(algorithm: algorithm, key: key); } else { - throw AblyException( - message: 'Unsupported platform', - ); + throw AblyException(message: 'Unsupported platform'); } } - /// @nodoc - /// Encodes [RestHistoryParams] to a Map - /// returns null if [v] is null - Map _encodeRestHistoryParams(final RestHistoryParams v) { - final jsonMap = {}; - _writeToJson( - jsonMap, TxRestHistoryParams.start, v.start.millisecondsSinceEpoch); - _writeToJson( - jsonMap, TxRestHistoryParams.end, v.end.millisecondsSinceEpoch); - _writeToJson(jsonMap, TxRestHistoryParams.direction, v.direction); - _writeToJson(jsonMap, TxRestHistoryParams.limit, v.limit); - return jsonMap; - } - - /// @nodoc - /// Encodes [RestPresenceParams] to a Map - /// returns null if [v] is null - Map _encodeRestPresenceParams(final RestPresenceParams v) { - final jsonMap = {}; - _writeToJson(jsonMap, TxRestPresenceParams.limit, v.limit); - _writeToJson(jsonMap, TxRestPresenceParams.clientId, v.clientId); - _writeToJson(jsonMap, TxRestPresenceParams.connectionId, v.connectionId); - return jsonMap; - } - Map _encodeRealtimePresenceParams( - final RealtimePresenceParams v) { + final RealtimePresenceParams v, + ) { final jsonMap = {}; _writeToJson(jsonMap, TxRealtimePresenceParams.waitForSync, v.waitForSync); _writeToJson(jsonMap, TxRealtimePresenceParams.clientId, v.clientId); _writeToJson( - jsonMap, TxRealtimePresenceParams.connectionId, v.connectionId); + jsonMap, + TxRealtimePresenceParams.connectionId, + v.connectionId, + ); return jsonMap; } @@ -516,8 +526,8 @@ class Codec extends StandardMessageCodec { final message = (v.message == null) ? null : (codecType == null) - ? v.message - : codecMap[codecType]!.encode(v.message); + ? v.message + : codecMap[codecType]!.encode(v.message); final jsonMap = {}; _writeToJson(jsonMap, TxAblyEventMessage.eventName, v.eventName); _writeToJson(jsonMap, TxAblyEventMessage.type, codecType); @@ -592,30 +602,26 @@ class Codec extends StandardMessageCodec { /// Decodes value [jsonMap] to [ClientOptions] /// returns null if [jsonMap] is null ClientOptions _decodeClientOptions(Map jsonMap) { - final tokenDetails = toJsonMap(_readFromJson>( - jsonMap, - TxClientOptions.tokenDetails, - )); - final tokenParams = toJsonMap(_readFromJson>( - jsonMap, - TxClientOptions.defaultTokenParams, - )); - final clientOptions = ClientOptions( - // AuthOptions (super class of ClientOptions) - authUrl: _readFromJson( - jsonMap, - TxClientOptions.authUrl, - ), - authMethod: _readFromJson( + final tokenDetails = toJsonMap( + _readFromJson>( jsonMap, - TxClientOptions.authMethod, + TxClientOptions.tokenDetails, ), - key: _readFromJson( + ); + final tokenParams = toJsonMap( + _readFromJson>( jsonMap, - TxClientOptions.key, + TxClientOptions.defaultTokenParams, ), - tokenDetails: - (tokenDetails == null) ? null : _decodeTokenDetails(tokenDetails), + ); + final clientOptions = ClientOptions( + // AuthOptions (super class of ClientOptions) + authUrl: _readFromJson(jsonMap, TxClientOptions.authUrl), + authMethod: _readFromJson(jsonMap, TxClientOptions.authMethod), + key: _readFromJson(jsonMap, TxClientOptions.key), + tokenDetails: (tokenDetails == null) + ? null + : _decodeTokenDetails(tokenDetails), authHeaders: _readFromJson>( jsonMap, TxClientOptions.authHeaders, @@ -624,51 +630,21 @@ class Codec extends StandardMessageCodec { jsonMap, TxClientOptions.authParams, ), - queryTime: _readFromJson( - jsonMap, - TxClientOptions.queryTime, - ), - useTokenAuth: _readFromJson( - jsonMap, - TxClientOptions.useTokenAuth, - ), + queryTime: _readFromJson(jsonMap, TxClientOptions.queryTime), + useTokenAuth: _readFromJson(jsonMap, TxClientOptions.useTokenAuth), // ClientOptions - clientId: _readFromJson( - jsonMap, - TxClientOptions.clientId, - ), + clientId: _readFromJson(jsonMap, TxClientOptions.clientId), logLevel: _decodeLogLevel(jsonMap[TxClientOptions.logLevel] as String?), - //TODO handle logHandler tls: jsonMap[TxClientOptions.tls] as bool, - restHost: _readFromJson( - jsonMap, - TxClientOptions.restHost, - ), - realtimeHost: _readFromJson( - jsonMap, - TxClientOptions.realtimeHost, - ), - port: _readFromJson( - jsonMap, - TxClientOptions.port, - ), - tlsPort: _readFromJson( - jsonMap, - TxClientOptions.tlsPort, - ), + endpoint: _readFromJson(jsonMap, TxClientOptions.endpoint), + port: _readFromJson(jsonMap, TxClientOptions.port), + tlsPort: _readFromJson(jsonMap, TxClientOptions.tlsPort), autoConnect: jsonMap[TxClientOptions.autoConnect] as bool, useBinaryProtocol: jsonMap[TxClientOptions.useBinaryProtocol] as bool, queueMessages: jsonMap[TxClientOptions.queueMessages] as bool, echoMessages: jsonMap[TxClientOptions.echoMessages] as bool, - recover: _readFromJson( - jsonMap, - TxClientOptions.recover, - ), - environment: _readFromJson( - jsonMap, - TxClientOptions.environment, - ), + recover: _readFromJson(jsonMap, TxClientOptions.recover), idempotentRestPublishing: _readFromJson( jsonMap, TxClientOptions.idempotentRestPublishing, @@ -682,14 +658,11 @@ class Codec extends StandardMessageCodec { jsonMap, TxClientOptions.fallbackHosts, ), - fallbackHostsUseDefault: _readFromJson( - jsonMap, - TxClientOptions.fallbackHostsUseDefault, - ), fallbackRetryTimeout: jsonMap[TxClientOptions.fallbackRetryTimeout] as int, - defaultTokenParams: - (tokenParams == null) ? null : _decodeTokenParams(tokenParams), + defaultTokenParams: (tokenParams == null) + ? null + : _decodeTokenParams(tokenParams), channelRetryTimeout: jsonMap[TxClientOptions.channelRetryTimeout] as int, transportParams: _readFromJson>( jsonMap, @@ -753,12 +726,16 @@ class Codec extends StandardMessageCodec { final type = _readFromJson(jsonMap, TxAblyMessage.type); var message = jsonMap[TxAblyMessage.message] as Object; if (type != null) { - message = codecMap[type]!.decode( - toJsonMap(_readFromJson>( - jsonMap, - TxAblyMessage.message, - )), - ) as Object; + message = + codecMap[type]!.decode( + toJsonMap( + _readFromJson>( + jsonMap, + TxAblyMessage.message, + ), + ), + ) + as Object; } return AblyMessage( message: message, @@ -769,19 +746,23 @@ class Codec extends StandardMessageCodec { DeviceDetails _decodeDeviceDetails(Map jsonMap) { final formFactor = _decodeFormFactor( - _readFromJson(jsonMap, TxDeviceDetails.formFactor)); + _readFromJson(jsonMap, TxDeviceDetails.formFactor), + ); return DeviceDetails( id: jsonMap[TxDeviceDetails.id] as String?, clientId: jsonMap[TxDeviceDetails.clientId] as String?, - platform: - _decodeDevicePlatform(jsonMap[TxDeviceDetails.platform] as String), + platform: _decodeDevicePlatform( + jsonMap[TxDeviceDetails.platform] as String, + ), formFactor: formFactor, metadata: toTypedJsonMap( - jsonMap[TxDeviceDetails.metadata] as Map?), + jsonMap[TxDeviceDetails.metadata] as Map?, + ), push: _decodeDevicePushDetails( - Map.from(jsonMap[TxDeviceDetails.devicePushDetails] - as Map), + Map.from( + jsonMap[TxDeviceDetails.devicePushDetails] as Map, + ), ), ); } @@ -794,10 +775,12 @@ class Codec extends StandardMessageCodec { jsonMap[TxDevicePushDetails.recipient] as Map?; return DevicePushDetails( - recipient: - (recipient != null) ? Map.from(recipient) : null, - state: - _decodeDevicePushState(jsonMap[TxDevicePushDetails.state] as String?), + recipient: (recipient != null) + ? Map.from(recipient) + : null, + state: _decodeDevicePushState( + jsonMap[TxDevicePushDetails.state] as String?, + ), errorReason: (jsonMapErrorReason != null) ? _decodeErrorInfo(Map.from(jsonMapErrorReason)) : null, @@ -805,11 +788,10 @@ class Codec extends StandardMessageCodec { } LocalDevice _decodeLocalDevice(Map jsonMap) => LocalDevice( - deviceDetails: _decodeDeviceDetails(jsonMap), - deviceSecret: jsonMap[TxLocalDevice.deviceSecret] as String?, - deviceIdentityToken: - jsonMap[TxLocalDevice.deviceIdentityToken] as String?, - ); + deviceDetails: _decodeDeviceDetails(jsonMap), + deviceSecret: jsonMap[TxLocalDevice.deviceSecret] as String?, + deviceIdentityToken: jsonMap[TxLocalDevice.deviceIdentityToken] as String?, + ); FormFactor _decodeFormFactor(String? enumValue) { switch (enumValue) { @@ -865,46 +847,56 @@ class Codec extends StandardMessageCodec { } PushChannelSubscription _decodePushChannelSubscription( - Map jsonMap) => - PushChannelSubscription( - channel: jsonMap[TxPushChannelSubscription.channel] as String, - clientId: jsonMap[TxPushChannelSubscription.clientId] as String?, - deviceId: jsonMap[TxPushChannelSubscription.deviceId] as String?, - ); + Map jsonMap, + ) => PushChannelSubscription( + channel: jsonMap[TxPushChannelSubscription.channel] as String, + clientId: jsonMap[TxPushChannelSubscription.clientId] as String?, + deviceId: jsonMap[TxPushChannelSubscription.deviceId] as String?, + ); UNNotificationSettings _decodeUNNotificationSettings( - Map jsonMap) => - UNNotificationSettings( - authorizationStatus: _decodeUNAuthorizationStatus( - jsonMap[TxUNNotificationSettings.authorizationStatus] as String), - soundSetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.soundSetting] as String), - badgeSetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.badgeSetting] as String), - alertSetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.alertSetting] as String), - notificationCenterSetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.notificationCenterSetting] - as String), - lockScreenSetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.lockScreenSetting] as String), - carPlaySetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.carPlaySetting] as String), - alertStyle: _decodeUNAlertStyle( - jsonMap[TxUNNotificationSettings.alertStyle] as String), - showPreviewsSetting: _decodeUNShowPreviewsSetting( - jsonMap[TxUNNotificationSettings.showPreviewsSetting] as String), - criticalAlertSetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.criticalAlertSetting] as String), - providesAppNotificationSettings: - jsonMap[TxUNNotificationSettings.providesAppNotificationSettings] - as bool, - announcementSetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.announcementSetting] as String), - scheduledDeliverySetting: _decodeUNNotificationSetting( - jsonMap[TxUNNotificationSettings.scheduledDeliverySetting] - as String), - ); + Map jsonMap, + ) => UNNotificationSettings( + authorizationStatus: _decodeUNAuthorizationStatus( + jsonMap[TxUNNotificationSettings.authorizationStatus] as String, + ), + soundSetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.soundSetting] as String, + ), + badgeSetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.badgeSetting] as String, + ), + alertSetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.alertSetting] as String, + ), + notificationCenterSetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.notificationCenterSetting] as String, + ), + lockScreenSetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.lockScreenSetting] as String, + ), + carPlaySetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.carPlaySetting] as String, + ), + alertStyle: _decodeUNAlertStyle( + jsonMap[TxUNNotificationSettings.alertStyle] as String, + ), + showPreviewsSetting: _decodeUNShowPreviewsSetting( + jsonMap[TxUNNotificationSettings.showPreviewsSetting] as String, + ), + criticalAlertSetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.criticalAlertSetting] as String, + ), + providesAppNotificationSettings: + jsonMap[TxUNNotificationSettings.providesAppNotificationSettings] + as bool, + announcementSetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.announcementSetting] as String, + ), + scheduledDeliverySetting: _decodeUNNotificationSetting( + jsonMap[TxUNNotificationSettings.scheduledDeliverySetting] as String, + ), + ); UNShowPreviewsSetting _decodeUNShowPreviewsSetting(String setting) { switch (setting) { @@ -916,7 +908,8 @@ class Codec extends StandardMessageCodec { return UNShowPreviewsSetting.never; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNShowPreviewsSetting is invalid: $setting', ); } @@ -931,7 +924,8 @@ class Codec extends StandardMessageCodec { return UNAlertStyle.none; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNAlertStyle is invalid: $style', ); } @@ -950,7 +944,8 @@ class Codec extends StandardMessageCodec { return UNAuthorizationStatus.ephemeral; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNAuthorizationStatus is invalid: $status', ); } @@ -965,7 +960,8 @@ class Codec extends StandardMessageCodec { return UNNotificationSetting.notSupported; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNNotificationSetting is invalid: $setting', ); } @@ -977,13 +973,13 @@ class Codec extends StandardMessageCodec { /// Decodes value [jsonMap] to [ErrorInfo] /// returns null if [jsonMap] is null ErrorInfo _decodeErrorInfo(Map jsonMap) => ErrorInfo( - code: jsonMap[TxErrorInfo.code] as int?, - message: jsonMap[TxErrorInfo.message] as String?, - statusCode: jsonMap[TxErrorInfo.statusCode] as int?, - href: jsonMap[TxErrorInfo.href] as String?, - requestId: jsonMap[TxErrorInfo.requestId] as String?, - cause: jsonMap[TxErrorInfo.cause] as ErrorInfo?, - ); + code: jsonMap[TxErrorInfo.code] as int?, + message: jsonMap[TxErrorInfo.message] as String?, + statusCode: jsonMap[TxErrorInfo.statusCode] as int?, + href: jsonMap[TxErrorInfo.href] as String?, + requestId: jsonMap[TxErrorInfo.requestId] as String?, + cause: jsonMap[TxErrorInfo.cause] as ErrorInfo?, + ); /// @nodoc /// Decodes [eventName] to [ConnectionEvent] enum if not null @@ -1009,7 +1005,8 @@ class Codec extends StandardMessageCodec { return ConnectionEvent.update; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'Connection event is invalid: $eventName', ); } @@ -1098,23 +1095,34 @@ class Codec extends StandardMessageCodec { EnrichedConnectionStateChange _decodeConnectionStateChange( Map jsonMap, ) { - final connectionId = - _readFromJson(jsonMap, TxConnectionStateChange.connectionId); - final connectionKey = - _readFromJson(jsonMap, TxConnectionStateChange.connectionKey); + final connectionId = _readFromJson( + jsonMap, + TxConnectionStateChange.connectionId, + ); + final connectionKey = _readFromJson( + jsonMap, + TxConnectionStateChange.connectionKey, + ); final current = _decodeConnectionState( - _readFromJson(jsonMap, TxConnectionStateChange.current)); + _readFromJson(jsonMap, TxConnectionStateChange.current), + ); final previous = _decodeConnectionState( - _readFromJson(jsonMap, TxConnectionStateChange.previous)); + _readFromJson(jsonMap, TxConnectionStateChange.previous), + ); final event = _decodeConnectionEvent( - _readFromJson(jsonMap, TxConnectionStateChange.event)); - final retryIn = - _readFromJson(jsonMap, TxConnectionStateChange.retryIn); - final errorInfo = toJsonMap(_readFromJson>( + _readFromJson(jsonMap, TxConnectionStateChange.event), + ); + final retryIn = _readFromJson( jsonMap, - TxConnectionStateChange.reason, - )); + TxConnectionStateChange.retryIn, + ); + final errorInfo = toJsonMap( + _readFromJson>( + jsonMap, + TxConnectionStateChange.reason, + ), + ); final reason = (errorInfo == null) ? null : _decodeErrorInfo(errorInfo); return EnrichedConnectionStateChange( stateChange: ConnectionStateChange( @@ -1134,16 +1142,21 @@ class Codec extends StandardMessageCodec { /// returns null if [jsonMap] is null ChannelStateChange _decodeChannelStateChange(Map jsonMap) { final current = _decodeChannelState( - _readFromJson(jsonMap, TxChannelStateChange.current)); + _readFromJson(jsonMap, TxChannelStateChange.current), + ); final previous = _decodeChannelState( - _readFromJson(jsonMap, TxChannelStateChange.previous)); + _readFromJson(jsonMap, TxChannelStateChange.previous), + ); final event = _decodeChannelEvent( - _readFromJson(jsonMap, TxChannelStateChange.event)); + _readFromJson(jsonMap, TxChannelStateChange.event), + ); final resumed = _readFromJson(jsonMap, TxChannelStateChange.resumed)!; - final errorInfo = toJsonMap(_readFromJson>( - jsonMap, - TxChannelStateChange.reason, - )); + final errorInfo = toJsonMap( + _readFromJson>( + jsonMap, + TxChannelStateChange.reason, + ), + ); final reason = (errorInfo == null) ? null : _decodeErrorInfo(errorInfo); return ChannelStateChange( current: current, @@ -1219,14 +1232,15 @@ class Codec extends StandardMessageCodec { final timestamp = _readFromJson(jsonMap, TxPresenceMessage.timestamp); return PresenceMessage( id: _readFromJson(jsonMap, TxPresenceMessage.id), - action: _decodePresenceAction(_readFromJson( - jsonMap, - TxPresenceMessage.action, - )), + action: _decodePresenceAction( + _readFromJson(jsonMap, TxPresenceMessage.action), + ), clientId: _readFromJson(jsonMap, TxPresenceMessage.clientId), data: _readFromJson(jsonMap, TxPresenceMessage.data), - connectionId: - _readFromJson(jsonMap, TxPresenceMessage.connectionId), + connectionId: _readFromJson( + jsonMap, + TxPresenceMessage.connectionId, + ), encoding: _readFromJson(jsonMap, TxPresenceMessage.encoding), extras: _readFromJson(jsonMap, TxPresenceMessage.extras), timestamp: (timestamp == null) @@ -1240,7 +1254,8 @@ class Codec extends StandardMessageCodec { /// returns null if [jsonMap] is null PaginatedResult _decodePaginatedResult(Map jsonMap) { final type = _readFromJson(jsonMap, TxPaginatedResult.type); - final items = _readFromJson>(jsonMap, TxPaginatedResult.items) + final items = + _readFromJson>(jsonMap, TxPaginatedResult.items) ?.map((e) => codecMap[type]?.decode(toJsonMap(e as Map)) as Object) .toList() ?? []; diff --git a/lib/src/platform/src/info.dart b/lib/src/platform/src/info.dart index 439478293..769081539 100644 --- a/lib/src/platform/src/info.dart +++ b/lib/src/platform/src/info.dart @@ -1,7 +1,7 @@ import 'dart:io' as io show Platform; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Get android/iOS platform version Future platformVersion() async => Platform() diff --git a/lib/src/platform/src/method_call_handler.dart b/lib/src/platform/src/method_call_handler.dart index ac05625d8..94fff180d 100644 --- a/lib/src/platform/src/method_call_handler.dart +++ b/lib/src/platform/src/method_call_handler.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:flutter/services.dart'; /// @nodoc @@ -7,12 +7,10 @@ import 'package:flutter/services.dart'; class AblyMethodCallHandler { /// @nodoc /// creates instance with method channel and forwards calls respective - /// instance methods: [onAuthCallback], [onRealtimeAuthCallback], etc + /// instance methods: [onRealtimeAuthCallback], etc AblyMethodCallHandler(MethodChannel channel) { channel.setMethodCallHandler((call) async { switch (call.method) { - case PlatformMethod.authCallback: - return onAuthCallback(call.arguments as AblyMessage); case PlatformMethod.realtimeAuthCallback: return onRealtimeAuthCallback(call.arguments as AblyMessage?); case PlatformMethod.pushOnActivate: @@ -26,34 +24,22 @@ class AblyMethodCallHandler { case PlatformMethod.pushOnBackgroundMessage: return _onPushBackgroundMessage(call.arguments as RemoteMessage); case PlatformMethod.pushOnShowNotificationInForeground: - return _pushNotificationEvents - .showNotificationInForeground(call.arguments as RemoteMessage); + return _pushNotificationEvents.showNotificationInForeground( + call.arguments as RemoteMessage, + ); case PlatformMethod.pushOnNotificationTap: return _onNotificationTap(call.arguments as RemoteMessage); case PlatformMethod.pushOpenSettingsFor: return _onOpenSettingsFor(); default: throw PlatformException( - code: 'Received invalid method channel call from Platform side', - message: 'No such method ${call.method}'); + code: 'Received invalid method channel call from Platform side', + message: 'No such method ${call.method}', + ); } }); } - /// @nodoc - /// handles auth callback for rest instances - Future onAuthCallback(AblyMessage message) async { - final tokenParams = message.message as TokenParams; - final rest = restInstances[message.handle]; - if (rest == null) { - throw AblyException( - message: "AblyMethodCallHandler#onAuthCallback's " - 'rest handle is ${message.handle}, and rest is $rest', - ); - } - return rest.options.authCallback!(tokenParams); - } - /// @nodoc /// handles auth callback for realtime instances Future onRealtimeAuthCallback(AblyMessage? message) async { @@ -61,7 +47,8 @@ class AblyMethodCallHandler { final realtime = realtimeInstances[message.handle]; if (realtime == null) { throw AblyException( - message: "AblyMethodCallHandler#onRealtimeAuthCallback's " + message: + "AblyMethodCallHandler#onRealtimeAuthCallback's " 'realtime handle is ${message.handle}, and realtime is $realtime', ); } @@ -99,8 +86,9 @@ class AblyMethodCallHandler { } Future _onNotificationTap(RemoteMessage remoteMessage) async { - _pushNotificationEvents.onNotificationTapStreamController - .add(remoteMessage); + _pushNotificationEvents.onNotificationTapStreamController.add( + remoteMessage, + ); return null; } diff --git a/lib/src/platform/src/paginated_result.dart b/lib/src/platform/src/paginated_result.dart index 37b7d49b4..926a8d8b2 100644 --- a/lib/src/platform/src/paginated_result.dart +++ b/lib/src/platform/src/paginated_result.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Contains a page of results for message or presence history, stats, or REST /// presence requests. @@ -32,8 +32,8 @@ class PaginatedResult extends PlatformObject { /// Creates a PaginatedResult instance from items and a boolean indicating /// whether there is a next page PaginatedResult(this._items, {required bool hasNext}) - : _hasNext = hasNext, - super(fetchHandle: false); + : _hasNext = hasNext, + super(fetchHandle: false); /// @nodoc /// Instantiates by extracting result from [AblyMessage] returned from @@ -42,10 +42,10 @@ class PaginatedResult extends PlatformObject { /// Sets appropriate [_pageHandle] for identifying platform side of this /// result object so that [next] and [first] can be executed PaginatedResult.fromAblyMessage(AblyMessage> message) - : _hasNext = message.message.hasNext(), - _items = message.message.items.map((e) => e as T).toList(), - _pageHandle = message.handle, - super(fetchHandle: false); + : _hasNext = message.message.hasNext(), + _items = message.message.items.map((e) => e as T).toList(), + _pageHandle = message.handle, + super(fetchHandle: false); /// @nodoc @override @@ -57,8 +57,9 @@ class PaginatedResult extends PlatformObject { /// /// If there are no further pages, then null is returned. Future> next() async { - final message = - await invokeRequest>(PlatformMethod.nextPage); + final message = await invokeRequest>( + PlatformMethod.nextPage, + ); return PaginatedResult.fromAblyMessage( AblyMessage.castFrom>(message), ); @@ -68,8 +69,9 @@ class PaginatedResult extends PlatformObject { /// and presence history, stats, and REST presence requests, for the first /// page of results. Future> first() async { - final message = - await invokeRequest>(PlatformMethod.firstPage); + final message = await invokeRequest>( + PlatformMethod.firstPage, + ); return PaginatedResult.fromAblyMessage( AblyMessage.castFrom>(message), ); diff --git a/lib/src/platform/src/platform.dart b/lib/src/platform/src/platform.dart index 4f30f8d87..bfee2e266 100644 --- a/lib/src/platform/src/platform.dart +++ b/lib/src/platform/src/platform.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:flutter/services.dart'; /// @nodoc @@ -18,7 +18,8 @@ class Platform { AblyMethodCallHandler(_methodChannel!); BackgroundIsolateAndroidPlatform().setupCallHandler(); _ablyClientsResetFuture = _invokePlatformMethodWithoutResetCheck( - PlatformMethod.resetAblyClients); + PlatformMethod.resetAblyClients, + ); } static Platform? _platform; @@ -47,8 +48,10 @@ class Platform { /// @nodoc /// Call a platform method which may return null/void as a result and /// check that ably clients have successfully reset - Future invokePlatformMethod(String method, - [AblyMessage>? arguments]) async { + Future invokePlatformMethod( + String method, [ + AblyMessage>? arguments, + ]) async { // Check if ably clients have successfully reset // (to ensure platform method calls won't interfere // with each other after Platform singleton is reinitialized) @@ -58,13 +61,17 @@ class Platform { /// @nodoc /// Call a platform method which always provides a non-null result - Future invokePlatformMethodNonNull(String method, - [AblyMessage>? arguments]) async { + Future invokePlatformMethodNonNull( + String method, [ + AblyMessage>? arguments, + ]) async { final result = await invokePlatformMethod(method, arguments); if (result == null) { throw AblyException( - message: 'invokePlatformMethodNonNull("$method") platform ' - 'method unexpectedly returned a null value.'); + message: + 'invokePlatformMethodNonNull("$method") platform ' + 'method unexpectedly returned a null value.', + ); } else { return result; } @@ -72,8 +79,10 @@ class Platform { /// @nodoc /// Call a platform method which may return null/void as a result - Future _invokePlatformMethodWithoutResetCheck(String method, - [AblyMessage>? arguments]) async { + Future _invokePlatformMethodWithoutResetCheck( + String method, [ + AblyMessage>? arguments, + ]) async { try { // If argument is null, pass an empty [AblyMessage], because codec fails // if argument value is null @@ -92,15 +101,14 @@ class Platform { /// @nodoc /// Call a platform method which always provides an observable stream /// of data as a result - Stream receiveBroadcastStream(String methodName, int handle, - [final Object? payload]) => - _streamsChannel!.receiveBroadcastStream( - AblyMessage( - message: AblyEventMessage( - eventName: methodName, - message: payload, - ), - handle: handle, - ), - ); + Stream receiveBroadcastStream( + String methodName, + int handle, [ + final Object? payload, + ]) => _streamsChannel!.receiveBroadcastStream( + AblyMessage( + message: AblyEventMessage(eventName: methodName, message: payload), + handle: handle, + ), + ); } diff --git a/lib/src/platform/src/platform_object.dart b/lib/src/platform/src/platform_object.dart index 881f7be32..852038a51 100644 --- a/lib/src/platform/src/platform_object.dart +++ b/lib/src/platform/src/platform_object.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// @nodoc @@ -42,22 +42,24 @@ abstract class PlatformObject { /// Invoke platform method channel without current handle. /// /// This method should be protected since it's only used to cover edge - /// case for creating rest and realtime instances. + /// case for creating realtime instances. @protected - Future invokeWithoutHandle(final String method, - [final Map? argument]) async { - final message = AblyMessage( - message: argument ?? {}, - ); + Future invokeWithoutHandle( + final String method, [ + final Map? argument, + ]) async { + final message = AblyMessage(message: argument ?? {}); return _platform.invokePlatformMethod(method, message); } /// @nodoc /// Invoke platform method channel with provided handle, or /// current handle if [externalHandle] is not provided. - Future invoke(final String method, - [final Map? arguments, - final int? externalHandle]) async { + Future invoke( + final String method, [ + final Map? arguments, + final int? externalHandle, + ]) async { final message = AblyMessage( message: arguments ?? {}, handle: externalHandle ?? await handle, @@ -69,8 +71,10 @@ abstract class PlatformObject { /// Invoke platform method channel with AblyMessage encapsulation. /// /// This is similar to [invoke], but ensures the response is not null. - Future invokeRequest(final String method, - [final Map? arguments]) async { + Future invokeRequest( + final String method, [ + final Map? arguments, + ]) async { final response = await invoke(method, arguments); if (response == null) { throw AblyException( @@ -89,8 +93,10 @@ abstract class PlatformObject { // ignore: close_sinks, will be closed by listener final controller = StreamController(); handle - .then((handle) => - _platform.receiveBroadcastStream(method, handle, payload)) + .then( + (handle) => + _platform.receiveBroadcastStream(method, handle, payload), + ) .then(controller.addStream); return controller.stream; } diff --git a/lib/src/platform/src/push_activation_events_internal.dart b/lib/src/platform/src/push_activation_events_internal.dart index 1b7af99f0..0ac935ce3 100644 --- a/lib/src/platform/src/push_activation_events_internal.dart +++ b/lib/src/platform/src/push_activation_events_internal.dart @@ -2,7 +2,7 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// @nodoc /// Package-private implementation of [PushActivationEvents] used internally by diff --git a/lib/src/platform/src/push_notification_events_internal.dart b/lib/src/platform/src/push_notification_events_internal.dart index a00e42193..49d5e3246 100644 --- a/lib/src/platform/src/push_notification_events_internal.dart +++ b/lib/src/platform/src/push_notification_events_internal.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:io' as io show Platform; import 'dart:ui'; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// @nodoc /// Package-private implementation of [PushNotificationEvents]. @@ -15,7 +15,7 @@ class PushNotificationEventsInternal implements PushNotificationEvents { /// @nodoc /// Invoked when pushOnShowNotificationInForeground platform method is called. Future Function(RemoteMessage message)? - onShowNotificationInForegroundHandler; + onShowNotificationInForegroundHandler; /// @nodoc /// Exposes stream of received [RemoteMessage] objects. @@ -34,7 +34,8 @@ class PushNotificationEventsInternal implements PushNotificationEvents { @override Future get notificationTapLaunchedAppFromTerminated => Platform().invokePlatformMethod( - PlatformMethod.pushNotificationTapLaunchedAppFromTerminated); + PlatformMethod.pushNotificationTapLaunchedAppFromTerminated, + ); @override Stream get onMessage => onMessageStreamController.stream; @@ -50,7 +51,8 @@ class PushNotificationEventsInternal implements PushNotificationEvents { @override void setOnShowNotificationInForeground( - Future Function(RemoteMessage message) callback) { + Future Function(RemoteMessage message) callback, + ) { onShowNotificationInForegroundHandler = callback; } @@ -91,10 +93,12 @@ class PushNotificationEventsInternal implements PushNotificationEvents { } } else { // ignore:avoid_print - print('Received RemoteMessage but no handler was set. ' - 'RemoteMessage.data: ${remoteMessage.data}. ' - 'RemoteMessage.notification: ${remoteMessage.notification}. ' - 'Set `ably.Push.notificationEvents.setOnBackgroundMessage()`.'); + print( + 'Received RemoteMessage but no handler was set. ' + 'RemoteMessage.data: ${remoteMessage.data}. ' + 'RemoteMessage.notification: ${remoteMessage.notification}. ' + 'Set `ably.Push.notificationEvents.setOnBackgroundMessage()`.', + ); } } diff --git a/lib/src/platform/src/realtime/connection.dart b/lib/src/platform/src/realtime/connection.dart index b1dce0142..151c52d93 100644 --- a/lib/src/platform/src/realtime/connection.dart +++ b/lib/src/platform/src/realtime/connection.dart @@ -1,13 +1,13 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Enables the management of a connection to Ably. class Connection extends PlatformObject { /// @nodoc - /// Realtime client instance - final Realtime realtime; + /// The Pub/Sub client instance + final PubSubClient realtime; ConnectionState _state; @@ -22,9 +22,7 @@ class Connection extends PlatformObject { /// /// Sets default [state] to [ConnectionState.initialized] and starts listening /// for updates to the connection [state]. - Connection(this.realtime) - : _state = ConnectionState.initialized, - super() { + Connection(this.realtime) : _state = ConnectionState.initialized, super() { _onConnectionStateChange().listen((event) { _state = event.stateChange.current; _errorReason = event.stateChange.reason; @@ -48,22 +46,15 @@ class Connection extends PlatformObject { /// A unique private connection key used to recover or resume a connection, /// assigned by Ably. /// - /// When recovering a connection explicitly, the `recoveryKey` is used in the - /// recover client options as it contains both the key and the last message - /// serial. This private connection key can also be used by other REST clients - /// to publish on behalf of this client. See the + /// When recovering a connection explicitly, the recovery key returned by + /// [createRecoveryKey] is used in the recover client options, as it contains + /// both the key and the last message serial. This private connection key can + /// also be used by other REST clients to publish on behalf of this client. + /// See the /// [publishing over REST on behalf of a realtime client docs](https://ably.com/docs/rest/channels#publish-on-behalf) /// for more info. String? get key => _key; - /// The recovery key string can be used by another client to recover this - /// connection's state in the recover client options property. - /// - /// See [connection state recover options](https://ably.com/docs/realtime/connection#connection-state-recover-options) - /// for more information. - @Deprecated('Use createRecoveryKey instead') - String? recoveryKey; - /// The createRecoveryKey returns key string can be used by another client to /// recover this connection's state in the recover client options property. /// @@ -76,9 +67,9 @@ class Connection extends PlatformObject { /// used automatically by the library when recovering or resuming a /// connection. /// - /// When recovering a connection explicitly, the `recoveryKey` is - /// used in the recover client options as it contains both the key and the - /// last message serial. + /// When recovering a connection explicitly, the recovery key returned by + /// [createRecoveryKey] is used in the recover client options, as it contains + /// both the key and the last message serial. int? serial; /// The current [ConnectionState] of the connection. @@ -92,10 +83,13 @@ class Connection extends PlatformObject { /// Stream of connection events with specified [ConnectionEvent] type. Stream on([ConnectionEvent? connectionEvent]) => - _onConnectionStateChange().map((event) => event.stateChange).where( - (connectionStateChange) => - connectionEvent == null || - connectionStateChange.event == connectionEvent); + _onConnectionStateChange() + .map((event) => event.stateChange) + .where( + (connectionStateChange) => + connectionEvent == null || + connectionStateChange.event == connectionEvent, + ); /// Causes the connection to close, entering the [ConnectionState.closing] /// state. diff --git a/lib/src/platform/src/realtime/presence.dart b/lib/src/platform/src/realtime/presence.dart index 25bc10cfa..75eba37e8 100644 --- a/lib/src/platform/src/realtime/presence.dart +++ b/lib/src/platform/src/realtime/presence.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Enables the presence set to be entered and subscribed to, and the historic /// presence set to be retrieved for a channel. @@ -17,16 +17,12 @@ class RealtimePresence extends PlatformObject { /// Retrieves the current members present on the channel and the metadata for /// each member, such as their [PresenceAction] and ID, based on provided /// [params]. Returns an array of [PresenceMessage] objects. - Future> get([ - RealtimePresenceParams? params, - ]) async { - final presenceMessages = await invokeRequest>( - PlatformMethod.realtimePresenceGet, - { - TxTransportKeys.channelName: _channel.name, - if (params != null) TxTransportKeys.params: params - }, - ); + Future> get([RealtimePresenceParams? params]) async { + final presenceMessages = + await invokeRequest>(PlatformMethod.realtimePresenceGet, { + TxTransportKeys.channelName: _channel.name, + if (params != null) TxTransportKeys.params: params, + }); return presenceMessages .map((e) => e as PresenceMessage) .toList(); @@ -46,7 +42,7 @@ class RealtimePresence extends PlatformObject { PlatformMethod.realtimePresenceHistory, { TxTransportKeys.channelName: _channel.name, - if (params != null) TxTransportKeys.params: params + if (params != null) TxTransportKeys.params: params, }, ); return PaginatedResult.fromAblyMessage( @@ -144,7 +140,9 @@ class RealtimePresence extends PlatformObject { if (action != null) actions ??= [action]; return listen(PlatformMethod.onRealtimePresenceMessage, { TxTransportKeys.channelName: _channel.name, - }).where((presenceMessage) => - actions == null || actions.contains(presenceMessage.action)); + }).where( + (presenceMessage) => + actions == null || actions.contains(presenceMessage.action), + ); } } diff --git a/lib/src/platform/src/realtime/realtime.dart b/lib/src/platform/src/realtime/pub_sub_client.dart similarity index 68% rename from lib/src/platform/src/realtime/realtime.dart rename to lib/src/platform/src/realtime/pub_sub_client.dart index d186dbdc7..1a10de8f5 100644 --- a/lib/src/platform/src/realtime/realtime.dart +++ b/lib/src/platform/src/realtime/pub_sub_client.dart @@ -2,39 +2,29 @@ import 'dart:async'; import 'dart:collection'; import 'dart:io' as io show Platform; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; -import 'package:ably_flutter/src/realtime/src/realtime_auth.dart'; - -/// A client that extends functionality of the [Rest] and provides -/// additional realtime-specific features. -class Realtime extends PlatformObject { - /// Constructs a `Realtime` object using an Ably [options] object or - /// the Ably API [key] or token string used to validate the client. - Realtime({ - ClientOptions? options, - final String? key, - }) : assert(options != null || key != null), - options = options ?? ClientOptions(key: key), - super() { +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/src/realtime/src/realtime_auth.dart'; + +/// A client that offers a realtime API to interact with Ably. +/// +/// Use [createClient] to obtain an instance; this class has no public +/// constructor. +class PubSubClient extends PlatformObject { + PubSubClient._({required this.options}) : super() { _connection = Connection(this); _channels = RealtimeChannels(this); push = Push(realtime: this); auth = RealtimeAuth(this); } - /// Constructs a `Realtime` object using an Ably API [key] or token string - /// used to validate the client. - factory Realtime.fromKey(String key) => - Realtime(options: ClientOptions(key: key)); - ///@nodoc @override Future createPlatformInstance() async { - final handle = - await invokeWithoutHandle(PlatformMethod.createRealtime, { - TxTransportKeys.options: options, - }); + final handle = await invokeWithoutHandle( + PlatformMethod.createRealtime, + {TxTransportKeys.options: options}, + ); _realtimeInstances[handle] = this; if (io.Platform.isAndroid && options.autoConnect) { @@ -45,11 +35,7 @@ class Realtime extends PlatformObject { // to false, and call connect immediately once we get the handle. // This is also a specific case where it's required to pass the handle // value from external source - await invoke( - PlatformMethod.connectRealtime, - null, - handle, - ); + await invoke(PlatformMethod.connectRealtime, null, handle); } return handle; } @@ -65,7 +51,7 @@ class Realtime extends PlatformObject { /// @nodoc /// A [ClientOptions] object used to configure the client connection to Ably. - late ClientOptions options; + final ClientOptions options; /// A [Push] object. late Push push; @@ -105,9 +91,16 @@ class Realtime extends PlatformObject { invokeRequest(PlatformMethod.pushDevice); } -Map _realtimeInstances = {}; +/// Creates a [PubSubClient] configured with the given [options]. +/// +/// The API key or token string used to validate the client is provided +/// through [options]. +PubSubClient createClient({required ClientOptions options}) => + PubSubClient._(options: options); + +Map _realtimeInstances = {}; /// @nodoc -/// Returns readonly copy of instances of all [Realtime] clients created. -Map get realtimeInstances => +/// Returns readonly copy of all [PubSubClient] instances created. +Map get realtimeInstances => UnmodifiableMapView(_realtimeInstances); diff --git a/lib/src/platform/src/realtime/realtime_channel.dart b/lib/src/platform/src/realtime/realtime_channel.dart index 309a95c9d..34844d64e 100644 --- a/lib/src/platform/src/realtime/realtime_channel.dart +++ b/lib/src/platform/src/realtime/realtime_channel.dart @@ -1,21 +1,21 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Enables messages to be published and subscribed to. /// /// Also enables historic messages to be retrieved and provides access to the /// [RealtimePresence] object of a channel. class RealtimeChannel extends PlatformObject { - final Realtime _realtime; + final PubSubClient _realtime; final String _channelName; late RealtimePresence _presence; /// A [RealtimePresence] object. - Realtime get realtime => _realtime; + PubSubClient get realtime => _realtime; /// The channel name. String get name => _channelName; @@ -24,15 +24,15 @@ class RealtimeChannel extends PlatformObject { RealtimePresence get presence => _presence; /// @nodoc - /// instantiates with [Rest], [name] and [RealtimeChannelOptions] + /// instantiates with [PubSubClient], [name] and [RealtimeChannelOptions] /// /// sets default [state] to [ChannelState.initialized] and start listening /// for updates to the channel [state]/ - RealtimeChannel(Realtime realtime, String channelName) - : _realtime = realtime, - _channelName = channelName, - state = ChannelState.initialized, - super() { + RealtimeChannel(PubSubClient realtime, String channelName) + : _realtime = realtime, + _channelName = channelName, + state = ChannelState.initialized, + super() { _presence = RealtimePresence(this); push = PushChannel(_channelName, realtime: _realtime); on().listen((event) => state = event.current); @@ -55,10 +55,12 @@ class RealtimeChannel extends PlatformObject { RealtimeHistoryParams? params, ]) async { final message = await invokeRequest>( - PlatformMethod.realtimeHistory, { - TxTransportKeys.channelName: _channelName, - if (params != null) TxTransportKeys.params: params, - }); + PlatformMethod.realtimeHistory, + { + TxTransportKeys.channelName: _channelName, + if (params != null) TxTransportKeys.params: params, + }, + ); return PaginatedResult.fromAblyMessage( AblyMessage.castFrom>(message), ); @@ -76,7 +78,7 @@ class RealtimeChannel extends PlatformObject { Object? data, }) async { messages ??= [ - if (message == null) Message(name: name, data: data) else message + if (message == null) Message(name: name, data: data) else message, ]; await invoke(PlatformMethod.publishRealtimeChannelMessage, { TxTransportKeys.channelName: _channelName, @@ -114,8 +116,8 @@ class RealtimeChannel extends PlatformObject { /// [RealtimePresence.subscribe] are called on the [RealtimePresence] object /// for this channel. Future attach() => invoke(PlatformMethod.attachRealtimeChannel, { - TxTransportKeys.channelName: _channelName, - }); + TxTransportKeys.channelName: _channelName, + }); /// Detach from this channel. Any resulting channel state change is emitted to /// any listeners registered using the [RealtimeChannel.on] stream. @@ -123,8 +125,8 @@ class RealtimeChannel extends PlatformObject { /// Once all clients globally have detached from the channel, the channel will /// be released in the Ably service within two minutes. Future detach() => invoke(PlatformMethod.detachRealtimeChannel, { - TxTransportKeys.channelName: _channelName, - }); + TxTransportKeys.channelName: _channelName, + }); /// Sets the [options] for the channel. Future setOptions(RealtimeChannelOptions options) => @@ -135,10 +137,9 @@ class RealtimeChannel extends PlatformObject { /// Stream of channel events with specified [ChannelEvent] type Stream on([ChannelEvent? channelEvent]) => - listen( - PlatformMethod.onRealtimeChannelStateChanged, - {TxTransportKeys.channelName: _channelName}, - ).where( + listen(PlatformMethod.onRealtimeChannelStateChanged, { + TxTransportKeys.channelName: _channelName, + }).where( (stateChange) => channelEvent == null || stateChange.event == channelEvent, ); @@ -156,8 +157,10 @@ class RealtimeChannel extends PlatformObject { final subscribedNames = {name, ...?names}.where((n) => n != null).toList(); return listen(PlatformMethod.onRealtimeChannelMessage, { TxTransportKeys.channelName: _channelName, - }).where((message) => - subscribedNames.isEmpty || - subscribedNames.any((n) => n == message.name)); + }).where( + (message) => + subscribedNames.isEmpty || + subscribedNames.any((n) => n == message.name), + ); } } diff --git a/lib/src/platform/src/realtime/realtime_channels.dart b/lib/src/platform/src/realtime/realtime_channels.dart index 093fe6686..5addd42c1 100644 --- a/lib/src/platform/src/realtime/realtime_channels.dart +++ b/lib/src/platform/src/realtime/realtime_channels.dart @@ -1,15 +1,15 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:meta/meta.dart'; /// Creates and destroys [RealtimeChannel] objects. class RealtimeChannels extends Channels { /// @nodoc /// instance of ably realtime client - Realtime realtime; + PubSubClient realtime; /// @nodoc - /// instantiates with the ably [Realtime] instance + /// instantiates with the ably [PubSubClient] instance RealtimeChannels(this.realtime); /// @nodoc @@ -20,12 +20,17 @@ class RealtimeChannels extends Channels { /// Releases a [RealtimeChannel] object with a specified [name], deleting it. /// /// It also removes any listeners associated with the channel. A realtime - /// channel should only be released when it is in the `INITIALIZED`, - /// `DETACHED`, or `FAILED` state; releasing a realtime channel in any other - /// state is deprecated and will throw an error in the next major version. + /// channel can only be released when it is in the `INITIALIZED`, + /// `DETACHED`, or `FAILED` state. If the channel is in any other state, the + /// returned future completes with an [AblyException] with code 90011 and + /// the channel is not released. Await [RealtimeChannel.detach] before calling + /// `release()` on an attached channel. Releasing a channel that does not + /// exist completes without error. @override - void release(String name) { - realtime.invoke(PlatformMethod.releaseRealtimeChannel, { + Future release(String name) async { + // The check against the channel state is left to the platform SDK, as + // [RealtimeChannel.state] is updated asynchronously and can lag behind it. + await realtime.invoke(PlatformMethod.releaseRealtimeChannel, { TxTransportKeys.channelName: name, }); super.release(name); diff --git a/lib/src/platform/src/rest/rest.dart b/lib/src/platform/src/rest/rest.dart deleted file mode 100644 index 2947173fb..000000000 --- a/lib/src/platform/src/rest/rest.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; - -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; -import 'package:ably_flutter/src/rest/src/rest_auth.dart'; - -Map _restInstances = {}; - -/// @nodoc -/// Returns readonly copy of instances of all [Rest] clients created. -Map get restInstances => UnmodifiableMapView(_restInstances); - -/// A client that offers a simple stateless API to interact directly with Ably's -/// REST API. -class Rest extends PlatformObject { - /// Construct a `Rest` object using an Ably [options] object. - Rest({required this.options}) : super() { - channels = RestChannels(this); - push = Push(rest: this); - auth = RestAuth(this); - } - - /// Constructs a `Rest` object using an Ably API [key] or token string - /// that's used to validate the cliet. - factory Rest.fromKey(String key) => Rest(options: ClientOptions(key: key)); - - /// @nodoc - @override - Future createPlatformInstance() async { - final handle = await invokeWithoutHandle(PlatformMethod.createRest, { - TxTransportKeys.options: options, - }); - _restInstances[handle] = this; - return handle; - } - - /// An [Auth] object. - late Auth auth; - - /// An object that contains additional client-specific properties - late ClientOptions options; - - /// Retrieves the time from the Ably service as milliseconds since the Unix - /// epoch. - /// - /// Clients that do not have access to a sufficiently well maintained - /// time source and wish to issue Ably [TokenRequest]s with a more accurate - /// timestamp should use the [AuthOptions.queryTime] property on a - /// [ClientOptions] object instead of this method. - Future time() async { - final time = await invokeRequest(PlatformMethod.restTime); - return DateTime.fromMillisecondsSinceEpoch(time); - } - - /// A [Push] object. - late Push push; - - /// A [Channels] object. - late RestChannels channels; - - /// Retrieves a [LocalDevice] object that represents the - /// current state of the device as a target for push notifications. - Future device() async => - invokeRequest(PlatformMethod.pushDevice); -} diff --git a/lib/src/platform/src/rest/rest_channel.dart b/lib/src/platform/src/rest/rest_channel.dart deleted file mode 100644 index 906e9bcdf..000000000 --- a/lib/src/platform/src/rest/rest_channel.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'dart:async'; - -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; - -/// Enables messages to be published and historic messages to be retrieved for a -/// channel. -class RestChannel extends PlatformObject { - /// @nodoc - /// reference to Rest client - Rest rest; - - /// A [PushChannel] object. - PushChannel push; - - /// The channel name. - String name; - - late RestPresence _presence; - - /// A [RestPresence] object. - RestPresence get presence => _presence; - - /// @nodoc - /// instantiates with [Rest], [name] and [RestChannelOptions] - RestChannel(this.rest, this.push, this.name) { - _presence = RestPresence(this); - } - - /// @nodoc - /// createPlatformInstance will return restPlatformObject's handle - /// as that is what will be required in platforms end to find rest instance - /// and send message to channel - @override - Future createPlatformInstance() async => rest.handle; - - /// Retrieves a [PaginatedResult] object, containing an array of historical - /// [Message] objects for the channel using the specified [params]. - /// - /// If the channel is configured to persist messages, then messages can be - /// retrieved from history for up to 72 hours in the past. If not, messages - /// can only be retrieved from history for up to two minutes in the past. - Future> history([ - RestHistoryParams? params, - ]) async { - final message = await invokeRequest>( - PlatformMethod.restHistory, - { - TxTransportKeys.channelName: name, - if (params != null) TxTransportKeys.params: params - }, - ); - return PaginatedResult.fromAblyMessage( - AblyMessage.castFrom>(message), - ); - } - - /// Publishes a [message] or list of [messages] to the channel, or a single - /// message with the given event [name] and [data] payload. - Future publish({ - Message? message, - List? messages, - String? name, - Object? data, - }) async { - messages ??= [ - if (message == null) Message(name: name, data: data) else message - ]; - await invoke(PlatformMethod.publish, { - TxTransportKeys.channelName: this.name, - TxTransportKeys.messages: messages, - }); - } - - /// Sets the [options] for the channel. - Future setOptions(RestChannelOptions options) => - invoke(PlatformMethod.setRestChannelOptions, { - TxTransportKeys.channelName: name, - TxTransportKeys.options: options, - }); -} diff --git a/lib/src/platform/src/rest/rest_channels.dart b/lib/src/platform/src/rest/rest_channels.dart deleted file mode 100644 index da5ff80bf..000000000 --- a/lib/src/platform/src/rest/rest_channels.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; -import 'package:meta/meta.dart'; - -/// Creates and destroys [RestChannel] objects. -class RestChannels extends Channels { - final Rest _rest; - - /// @nodoc - /// Instantiates with the ably [Rest] instance. - RestChannels(this._rest); - - /// @nodoc - /// Creates a channel with provided name and options. - /// - /// This is a private method to be overridden by implementation classes. - @override - @protected - RestChannel createChannel(String name) => - RestChannel(_rest, PushChannel(name, rest: _rest), name); - - /// Releases a [RestChannel] object with a specified [name], deleting it. - /// - /// It also removes any listeners associated with the channel. To release a - /// channel, the [ChannelState] must be `INITIALIZED`, `DETACHED`, or - /// `FAILED`. - @override - void release(String name) { - _rest.invoke(PlatformMethod.releaseRestChannel, { - TxTransportKeys.channelName: name, - }); - super.release(name); - } -} diff --git a/lib/src/platform/src/rest/rest_presence.dart b/lib/src/platform/src/rest/rest_presence.dart deleted file mode 100644 index 84835afbd..000000000 --- a/lib/src/platform/src/rest/rest_presence.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; - -/// Enables the retrieval of the current and historic presence set for a -/// channel. -class RestPresence extends PlatformObject { - final RestChannel _restChannel; - - /// @nodoc - /// instantiates with a channel - RestPresence(this._restChannel); - - /// @nodoc - @override - Future createPlatformInstance() => _restChannel.rest.handle; - - /// Retrieves the current members present on the channel and the metadata for - /// each member, such as their [PresenceAction] and ID, based on provided - /// [params]. - /// - /// Returns a [PaginatedResult] object, containing an - /// array of [PresenceMessage] objects. - Future> get([ - RestPresenceParams? params, - ]) async { - final message = await invokeRequest>( - PlatformMethod.restPresenceGet, - { - TxTransportKeys.channelName: _restChannel.name, - if (params != null) TxTransportKeys.params: params - }, - ); - return PaginatedResult.fromAblyMessage( - AblyMessage.castFrom>(message), - ); - } - - /// Retrieves a [PaginatedResult] object, containing an - /// array of historical [PresenceMessage] objects for - /// the channel, based on provided [params]. - /// - /// If the channel is configured to persist messages, then - /// presence messages can be retrieved from history for up to 72 hours in the - /// past. If not, presence messages can only be retrieved from history for up - /// to two minutes in the past. - Future> history([ - RestHistoryParams? params, - ]) async { - final message = await invokeRequest>( - PlatformMethod.restPresenceHistory, - { - TxTransportKeys.channelName: _restChannel.name, - if (params != null) TxTransportKeys.params: params - }, - ); - return PaginatedResult.fromAblyMessage( - AblyMessage.castFrom>(message), - ); - } -} diff --git a/lib/src/platform/src/streams_channel.dart b/lib/src/platform/src/streams_channel.dart index 45b156436..ae2cccac1 100644 --- a/lib/src/platform/src/streams_channel.dart +++ b/lib/src/platform/src/streams_channel.dart @@ -20,7 +20,7 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; @@ -50,57 +50,70 @@ class StreamsChannel { final handlerName = '$name#$id'; late StreamController controller; - controller = StreamController.broadcast(onListen: () async { - // We need to keep this null-asserted for backwards compatibility with - // Flutter versions before 3.0.0 - // ignore: unnecessary_non_null_assertion - ServicesBinding.instance!.defaultBinaryMessenger - .setMessageHandler(handlerName, (reply) async { - if (reply == null) { - await controller.close(); - } else { - try { - controller.add(codec.decodeEnvelope(reply) as T); - } on PlatformException catch (pe) { - if (pe.details is ErrorInfo) { - throw AblyException.fromPlatformException(pe); + controller = StreamController.broadcast( + onListen: () async { + // We need to keep this null-asserted for backwards compatibility with + // Flutter versions before 3.0.0 + // ignore: unnecessary_non_null_assertion + ServicesBinding.instance!.defaultBinaryMessenger.setMessageHandler( + handlerName, + (reply) async { + if (reply == null) { + await controller.close(); } else { - controller.addError(pe); + try { + controller.add(codec.decodeEnvelope(reply) as T); + } on PlatformException catch (pe) { + if (pe.details is ErrorInfo) { + throw AblyException.fromPlatformException(pe); + } else { + controller.addError(pe); + } + } } - } - } - return reply; - }); - try { - await methodChannel.invokeMethod('listen#$id', arguments); - } on Exception catch (exception, stack) { - FlutterError.reportError(FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'streams_channel', - context: DiagnosticsNode.message( - 'while activating platform stream on channel $name'), - )); - } - }, onCancel: () async { - // We need to keep this null-asserted for backwards compatibility with - // Flutter versions before 3.0.0 - // ignore: unnecessary_non_null_assertion - ServicesBinding.instance!.defaultBinaryMessenger - .setMessageHandler(handlerName, null); - try { - await methodChannel.invokeMethod('cancel#$id', arguments); - } on Exception catch (exception, stack) { - FlutterError.reportError(FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'streams_channel', - context: DiagnosticsNode.message( - 'while de-activating platform stream on channel $name'), - )); - } - }); + return reply; + }, + ); + try { + await methodChannel.invokeMethod('listen#$id', arguments); + } on Exception catch (exception, stack) { + FlutterError.reportError( + FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'streams_channel', + context: DiagnosticsNode.message( + 'while activating platform stream on channel $name', + ), + ), + ); + } + }, + onCancel: () async { + // We need to keep this null-asserted for backwards compatibility with + // Flutter versions before 3.0.0 + // ignore: unnecessary_non_null_assertion + ServicesBinding.instance!.defaultBinaryMessenger.setMessageHandler( + handlerName, + null, + ); + try { + await methodChannel.invokeMethod('cancel#$id', arguments); + } on Exception catch (exception, stack) { + FlutterError.reportError( + FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'streams_channel', + context: DiagnosticsNode.message( + 'while de-activating platform stream on channel $name', + ), + ), + ); + } + }, + ); return controller.stream; } } diff --git a/lib/src/push_notifications/src/admin/push_admin.dart b/lib/src/push_notifications/src/admin/push_admin.dart index 6a3e93b33..57ff2042f 100644 --- a/lib/src/push_notifications/src/admin/push_admin.dart +++ b/lib/src/push_notifications/src/admin/push_admin.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Enables the management of device registrations and push notification /// subscriptions. Also enables the publishing of push notifications to devices. diff --git a/lib/src/push_notifications/src/admin/push_device_registrations.dart b/lib/src/push_notifications/src/admin/push_device_registrations.dart index 3666c7d6a..16dcfe61f 100644 --- a/lib/src/push_notifications/src/admin/push_device_registrations.dart +++ b/lib/src/push_notifications/src/admin/push_device_registrations.dart @@ -1,21 +1,16 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Enables the management of push notification registrations with Ably. abstract class PushDeviceRegistrations { /// Retrieves the [DeviceDetails] of a device registered /// to receive push notifications using the `id` property of a /// [DeviceDetails] object, or by directly using the unique [deviceId]. - Future get({ - DeviceDetails? deviceDetails, - String? deviceId, - }); + Future get({DeviceDetails? deviceDetails, String? deviceId}); /// Retrieves all devices matching the filter [params] provided. /// Returns a [PaginatedResult] object, containing an array of [DeviceDetails] /// objects. - Future> list( - DeviceRegistrationParams params, - ); + Future> list(DeviceRegistrationParams params); /// Registers or updates a [deviceDetails] object with Ably. Returns the new, /// or updated [DeviceDetails] object. @@ -24,10 +19,7 @@ abstract class PushDeviceRegistrations { /// Removes a device registered to receive push notifications from Ably using /// the id property of a [deviceDetails] object, or by directly using the /// unique [deviceId]. - Future remove({ - DeviceDetails? deviceDetails, - String? deviceId, - }); + Future remove({DeviceDetails? deviceDetails, String? deviceId}); /// Removes all devices registered to receive push notifications from Ably /// matching the filter [params] provided. diff --git a/lib/src/push_notifications/src/device_details.dart b/lib/src/push_notifications/src/device_details.dart index a4c61db30..86311432e 100644 --- a/lib/src/push_notifications/src/device_details.dart +++ b/lib/src/push_notifications/src/device_details.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Contains the properties of a device registered for push notifications. diff --git a/lib/src/push_notifications/src/device_push_details.dart b/lib/src/push_notifications/src/device_push_details.dart index 761b8f1fb..4bd64a026 100644 --- a/lib/src/push_notifications/src/device_push_details.dart +++ b/lib/src/push_notifications/src/device_push_details.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Contains the details of the push registration of a device. @@ -17,9 +17,5 @@ class DevicePushDetails { /// @nodoc /// Initializes an instance without any defaults - const DevicePushDetails({ - this.errorReason, - this.recipient, - this.state, - }); + const DevicePushDetails({this.errorReason, this.recipient, this.state}); } diff --git a/lib/src/push_notifications/src/device_push_state.dart b/lib/src/push_notifications/src/device_push_state.dart index 1d210743d..a382716c6 100644 --- a/lib/src/push_notifications/src/device_push_state.dart +++ b/lib/src/push_notifications/src/device_push_state.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// The current state of the push registration in [DevicePushDetails]. enum DevicePushState { diff --git a/lib/src/push_notifications/src/device_registration_params.dart b/lib/src/push_notifications/src/device_registration_params.dart index fb4758525..18ccc1fff 100644 --- a/lib/src/push_notifications/src/device_registration_params.dart +++ b/lib/src/push_notifications/src/device_registration_params.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains properties used to filter [PushDeviceRegistrations] devices. abstract class DeviceRegistrationParams { diff --git a/lib/src/push_notifications/src/ios_notification_settings.dart b/lib/src/push_notifications/src/ios_notification_settings.dart index a054ed0a2..d30b00c6e 100644 --- a/lib/src/push_notifications/src/ios_notification_settings.dart +++ b/lib/src/push_notifications/src/ios_notification_settings.dart @@ -1,6 +1,6 @@ // ignore_for_file: public_member_api_docs -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// @nodoc /// This file contains push notification types specific to iOS. All types begin @@ -26,31 +26,19 @@ enum UNAuthorizationStatus { /// Constants indicating the presentation styles for alerts. /// /// [Apple docs](https://developer.apple.com/documentation/usernotifications/unalertstyle) -enum UNAlertStyle { - none, - banner, - alert, -} +enum UNAlertStyle { none, banner, alert } /// @nodoc /// Constants that indicate the current status of a notification setting. /// /// [Apple docs](https://developer.apple.com/documentation/usernotifications/unnotificationsetting) -enum UNNotificationSetting { - notSupported, - disabled, - enabled, -} +enum UNNotificationSetting { notSupported, disabled, enabled } /// @nodoc /// Constants indicating the style previewing a notification's content. /// /// [Apple docs](https://developer.apple.com/documentation/usernotifications/unshowpreviewssetting) -enum UNShowPreviewsSetting { - always, - whenAuthenticated, - never, -} +enum UNShowPreviewsSetting { always, whenAuthenticated, never } /// @nodoc /// The object for managing notification-related settings and the authorization diff --git a/lib/src/push_notifications/src/local_device.dart b/lib/src/push_notifications/src/local_device.dart index 8853ab13e..6dc076e52 100644 --- a/lib/src/push_notifications/src/local_device.dart +++ b/lib/src/push_notifications/src/local_device.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Contains the device identity token and secret of a device. @@ -19,11 +19,11 @@ class LocalDevice extends DeviceDetails { this.deviceIdentityToken, this.deviceSecret, }) : super( - clientId: deviceDetails.clientId, - formFactor: deviceDetails.formFactor, - id: deviceDetails.id, - metadata: deviceDetails.metadata, - platform: deviceDetails.platform, - push: deviceDetails.push, - ); + clientId: deviceDetails.clientId, + formFactor: deviceDetails.formFactor, + id: deviceDetails.id, + metadata: deviceDetails.metadata, + platform: deviceDetails.platform, + push: deviceDetails.push, + ); } diff --git a/lib/src/push_notifications/src/notification.dart b/lib/src/push_notifications/src/notification.dart index cfe0acfb2..9f62f9cbc 100644 --- a/lib/src/push_notifications/src/notification.dart +++ b/lib/src/push_notifications/src/notification.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:meta/meta.dart'; /// Notification sent by APNS or FCM @@ -17,15 +17,12 @@ class Notification { /// @nodoc /// Initializes an instance without any defaults. - const Notification({ - required this.title, - this.body, - }); + const Notification({required this.title, this.body}); /// @nodoc /// Creates an instance from the map. factory Notification.fromMap(Map map) => Notification( - body: map[TxNotification.body] as String?, - title: map[TxNotification.title] as String? ?? '', - ); + body: map[TxNotification.body] as String?, + title: map[TxNotification.title] as String? ?? '', + ); } diff --git a/lib/src/push_notifications/src/push.dart b/lib/src/push_notifications/src/push.dart index bd1db2d87..f0275abe7 100644 --- a/lib/src/push_notifications/src/push.dart +++ b/lib/src/push_notifications/src/push.dart @@ -1,6 +1,6 @@ import 'dart:io' as io show Platform; -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:meta/meta.dart'; /// Enables a device to be registered and deregistered from receiving push @@ -15,27 +15,13 @@ class Push extends PlatformObject { static PushNotificationEvents notificationEvents = PushNotificationEventsInternal(); - /// @nodoc - /// A rest client used platform side to invoke push notification methods - final Rest? rest; - /// @nodoc /// A realtime client used platform side to invoke push notification methods - final Realtime? realtime; + final PubSubClient realtime; /// @nodoc - /// Pass an Ably realtime or rest client. - Push({ - this.realtime, - this.rest, - }) : super() { - final ablyClientNotPresent = rest == null && realtime == null; - final moreThanOneAblyClientPresent = rest != null && realtime != null; - if (ablyClientNotPresent || moreThanOneAblyClientPresent) { - throw Exception( - 'Specify one Ably client when creating ${(Push).toString()}.'); - } - } + /// Pass an Ably realtime client. + Push({required this.realtime}) : super(); /// Activates the device for push notifications with FCM or APNS, obtaining a /// unique identifier from them. @@ -105,7 +91,8 @@ class Push extends PlatformObject { Future getNotificationSettings() async { if (io.Platform.isIOS) { return invokeRequest( - PlatformMethod.pushGetNotificationSettings); + PlatformMethod.pushGetNotificationSettings, + ); } else { throw UnsupportedError('getNotificationSettings is only valid on iOS.'); } @@ -134,7 +121,5 @@ class Push extends PlatformObject { /// @nodoc @override - Future createPlatformInstance() => (realtime != null) - ? (realtime as Realtime).handle - : (rest as Rest).handle; + Future createPlatformInstance() => realtime.handle; } diff --git a/lib/src/push_notifications/src/push_activation_events.dart b/lib/src/push_notifications/src/push_activation_events.dart index 23555ace7..c1e8ec468 100644 --- a/lib/src/push_notifications/src/push_activation_events.dart +++ b/lib/src/push_notifications/src/push_activation_events.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains streams that will emit a message whenever events related to push /// notifications occur, such as device activation, deactivation, or update diff --git a/lib/src/push_notifications/src/push_channel.dart b/lib/src/push_notifications/src/push_channel.dart index 2d134620d..7d0f304f6 100644 --- a/lib/src/push_notifications/src/push_channel.dart +++ b/lib/src/push_notifications/src/push_channel.dart @@ -1,75 +1,65 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Enables devices to subscribe to push notifications for a channel. class PushChannel extends PlatformObject { final String _name; - /// @nodoc - /// A rest client used platform side to invoke push notification methods - final Rest? rest; - /// @nodoc /// A realtime client used platform side to invoke push notification methods - final Realtime? realtime; + final PubSubClient realtime; /// @nodoc - /// Pass the channel name and an Ably realtime or rest client. - PushChannel(this._name, {this.rest, this.realtime}) { - final ablyClientNotPresent = rest == null && realtime == null; - final moreThanOneAblyClientPresent = rest != null && realtime != null; - if (ablyClientNotPresent || moreThanOneAblyClientPresent) { - throw Exception( - 'Specify one Ably client when creating ${(Push).toString()}.'); - } - } + /// Pass the channel name and an Ably realtime client. + PushChannel(this._name, {required this.realtime}); /// @nodoc @override - Future createPlatformInstance() => (realtime != null) - ? (realtime as Realtime).handle - : (rest as Rest).handle; + Future createPlatformInstance() => realtime.handle; /// Subscribes the device to push notifications for the channel. - Future subscribeDevice() => invoke( - PlatformMethod.pushSubscribeDevice, {TxTransportKeys.channelName: _name}); + Future subscribeDevice() => invoke(PlatformMethod.pushSubscribeDevice, { + TxTransportKeys.channelName: _name, + }); /// Unsubscribes the device from receiving push notifications for the channel. Future unsubscribeDevice() => invoke( - PlatformMethod.pushUnsubscribeDevice, - {TxTransportKeys.channelName: _name}); + PlatformMethod.pushUnsubscribeDevice, + {TxTransportKeys.channelName: _name}, + ); /// Subscribes all devices associated with the current device's `clientId` to /// push notifications for the channel. - Future subscribeClient() => invoke( - PlatformMethod.pushSubscribeClient, {TxTransportKeys.channelName: _name}); + Future subscribeClient() => invoke(PlatformMethod.pushSubscribeClient, { + TxTransportKeys.channelName: _name, + }); /// Unsubscribes all devices associated with the current device's `clientId` /// from receiving push notifications for the channel. Future unsubscribeClient() => invoke( - PlatformMethod.pushUnsubscribeClient, - {TxTransportKeys.channelName: _name}); + PlatformMethod.pushUnsubscribeClient, + {TxTransportKeys.channelName: _name}, + ); /// Retrieves all push subscriptions for the channel. /// - /// Subscriptions can be filtered using a [params] object containing key-value - /// pairs to filter subscriptions by. Can contain `clientId`, `deviceId` or a - /// combination of both if concatFilters is set to true, and a limit on the - /// number of subscriptions returned, up to 1,000. + /// [params] must contain exactly one of `deviceId` or `clientId` to filter + /// subscriptions by, and can contain a `limit` on the number of + /// subscriptions returned, up to 1,000. + /// + /// Throws an [AblyException] if [params] contains neither or both of + /// `deviceId` and `clientId`. /// /// Returns a [PaginatedResult] object containing a list of /// [PushChannelSubscription] objects. Future> listSubscriptions( - Map params) async { - if (!params.containsKey('deviceId') && - !params.containsKey('clientId') && - !params.containsKey('deviceClientId') && - !params.containsKey('channel')) { - // This error only happen on Androids. They are thrown here - // for both platforms (iOS/ Android) to make the API more consistent. + Map params, + ) async { + // ably-pubsub-cocoa only supports filtering by one of these, so it is + // enforced here on both platforms to keep the API consistent. + if (params.containsKey('deviceId') == params.containsKey('clientId')) { throw AblyException( - message: "expected parameter 'deviceId', 'clientId', " - "'deviceClientId', and/or 'channel'", + message: "expected exactly one of 'deviceId' and 'clientId'", ); } @@ -79,6 +69,7 @@ class PushChannel extends PlatformObject { ); return PaginatedResult.fromAblyMessage( - AblyMessage.castFrom>(message)); + AblyMessage.castFrom>(message), + ); } } diff --git a/lib/src/push_notifications/src/push_channel_params.dart b/lib/src/push_notifications/src/push_channel_params.dart index 4c9582896..1aebcc518 100644 --- a/lib/src/push_notifications/src/push_channel_params.dart +++ b/lib/src/push_notifications/src/push_channel_params.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains properties used to filter [PushChannelSubscriptions] channels. abstract class PushChannelsParams { diff --git a/lib/src/push_notifications/src/push_channel_subscription_params.dart b/lib/src/push_notifications/src/push_channel_subscription_params.dart index 28d456dee..4a3d7930a 100644 --- a/lib/src/push_notifications/src/push_channel_subscription_params.dart +++ b/lib/src/push_notifications/src/push_channel_subscription_params.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains properties used to filter [PushChannel] subscriptions. abstract class PushChannelSubscriptionParams { diff --git a/lib/src/push_notifications/src/push_channel_subscriptions.dart b/lib/src/push_notifications/src/push_channel_subscriptions.dart index 0d1b3e8d3..20f72f28b 100644 --- a/lib/src/push_notifications/src/push_channel_subscriptions.dart +++ b/lib/src/push_notifications/src/push_channel_subscriptions.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Enables device push channel subscriptions. abstract class PushChannelSubscriptions { @@ -13,9 +13,7 @@ abstract class PushChannelSubscriptions { /// notifications using a [params] object. /// /// Returns a [PaginatedResult] object, containing an array of channel names. - Future> listChannels( - PushChannelsParams params, - ); + Future> listChannels(PushChannelsParams params); /// Subscribes a device, or a group of devices sharing the same `clientId` to /// push notifications on a channel, using the [subscription] object. diff --git a/lib/src/push_notifications/src/push_notification_events.dart b/lib/src/push_notifications/src/push_notification_events.dart index f36ff76ea..501a98a00 100644 --- a/lib/src/push_notifications/src/push_notification_events.dart +++ b/lib/src/push_notifications/src/push_notification_events.dart @@ -1,6 +1,6 @@ import 'dart:ui'; -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Type definition for a callback invoked when background push message /// is received. @@ -21,7 +21,8 @@ abstract class PushNotificationEvents { /// notifications while the app is in the foreground, manually create one /// when a [onMessage] or [BackgroundMessageHandler] is called. void setOnShowNotificationInForeground( - Future Function(RemoteMessage message) callback); + Future Function(RemoteMessage message) callback, + ); /// A stream that emmits messages while the app is in the foreground. Stream get onMessage; diff --git a/lib/src/push_notifications/src/remote_message.dart b/lib/src/push_notifications/src/remote_message.dart index cb7e95f76..8ecc10069 100644 --- a/lib/src/push_notifications/src/remote_message.dart +++ b/lib/src/push_notifications/src/remote_message.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// Represents FCM Message on Android and APNS message on iOS /// Both [data] and [notification] are related to corresponding fields in @@ -19,24 +19,23 @@ class RemoteMessage { /// @nodoc /// Initializes an instance with [data] set to empty map - RemoteMessage({ - Map? data, - this.notification, - }) : data = data ??= {}; + RemoteMessage({Map? data, this.notification}) + : data = data ??= {}; /// @nodoc /// Create instance from a map. factory RemoteMessage.fromMap(Map map) => RemoteMessage( - data: (map[TxRemoteMessage.data] == null) - ? {} - : Map.from( - map[TxRemoteMessage.data] as Map, - ), - notification: (map[TxRemoteMessage.notification] == null) - ? null - : Notification.fromMap( - Map.from( - map[TxRemoteMessage.notification] as Map), - ), - ); + data: (map[TxRemoteMessage.data] == null) + ? {} + : Map.from( + map[TxRemoteMessage.data] as Map, + ), + notification: (map[TxRemoteMessage.notification] == null) + ? null + : Notification.fromMap( + Map.from( + map[TxRemoteMessage.notification] as Map, + ), + ), + ); } diff --git a/lib/src/realtime/src/channel_event.dart b/lib/src/realtime/src/channel_event.dart index 07eee0831..e9de60ca7 100644 --- a/lib/src/realtime/src/channel_event.dart +++ b/lib/src/realtime/src/channel_event.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; -/// Describes the events emitted by a [RestChannel] or [RealtimeChannel] object. +/// Describes the events emitted by a [RealtimeChannel] object. /// /// An event is either an `UPDATE` or a [ChannelState]. enum ChannelEvent { diff --git a/lib/src/realtime/src/channel_mode.dart b/lib/src/realtime/src/channel_mode.dart index 63af40825..686a86db2 100644 --- a/lib/src/realtime/src/channel_mode.dart +++ b/lib/src/realtime/src/channel_mode.dart @@ -1,8 +1,7 @@ -import 'package:ably_flutter/src/realtime/realtime.dart'; -import 'package:ably_flutter/src/rest/rest.dart'; +import 'package:ably_pubsub_device_flutter/src/realtime/realtime.dart'; /// Describes the possible flags used to configure client capabilities, using -/// [RestChannelOptions] or [RealtimeChannelOptions]. +/// [RealtimeChannelOptions]. enum ChannelMode { /// The client can enter the presence set. presence, diff --git a/lib/src/realtime/src/channel_state.dart b/lib/src/realtime/src/channel_state.dart index b1114f091..bd685a9c0 100644 --- a/lib/src/realtime/src/channel_state.dart +++ b/lib/src/realtime/src/channel_state.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; -/// Describes the possible states of a [RestChannel] or [RealtimeChannel] +/// Describes the possible states of a [RealtimeChannel] /// object. enum ChannelState { /// The channel has been initialized but no attach has yet been attempted. diff --git a/lib/src/realtime/src/channel_state_event.dart b/lib/src/realtime/src/channel_state_event.dart index ac2775f9a..d7e9bccd6 100644 --- a/lib/src/realtime/src/channel_state_event.dart +++ b/lib/src/realtime/src/channel_state_event.dart @@ -1,8 +1,7 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; -/// Contains state change information emitted by [RestChannel] and -/// [RealtimeChannel] objects. +/// Contains state change information emitted by [RealtimeChannel] objects. @immutable class ChannelStateChange { /// The event that triggered this [ChannelState] change. diff --git a/lib/src/realtime/src/connection_event.dart b/lib/src/realtime/src/connection_event.dart index 4463f59d9..aa0a718a3 100644 --- a/lib/src/realtime/src/connection_event.dart +++ b/lib/src/realtime/src/connection_event.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Describes the events emitted by a [Connection] object. An event is /// either an `UPDATE` or an event from [ConnectionState]. diff --git a/lib/src/realtime/src/connection_state.dart b/lib/src/realtime/src/connection_state.dart index 164ad0a57..acb6aec2e 100644 --- a/lib/src/realtime/src/connection_state.dart +++ b/lib/src/realtime/src/connection_state.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Describes the realtime [Connection] object states. enum ConnectionState { diff --git a/lib/src/realtime/src/connection_state_change.dart b/lib/src/realtime/src/connection_state_change.dart index 06efd7b00..dac2bb859 100644 --- a/lib/src/realtime/src/connection_state_change.dart +++ b/lib/src/realtime/src/connection_state_change.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Contains [ConnectionState] change information emitted by the [Connection] @@ -49,6 +49,9 @@ class EnrichedConnectionStateChange { final String? connectionKey; /// @nodoc - const EnrichedConnectionStateChange( - {required this.stateChange, this.connectionId, this.connectionKey}); + const EnrichedConnectionStateChange({ + required this.stateChange, + this.connectionId, + this.connectionKey, + }); } diff --git a/lib/src/realtime/src/realtime_auth.dart b/lib/src/realtime/src/realtime_auth.dart index c6366d27e..c30f6e976 100644 --- a/lib/src/realtime/src/realtime_auth.dart +++ b/lib/src/realtime/src/realtime_auth.dart @@ -1,41 +1,41 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// See [Auth] for more information. class RealtimeAuth extends Auth { - final Realtime _realtime; + final PubSubClient _realtime; /// Constructor for RealtimeAuth RealtimeAuth(this._realtime); @override - Future authorize( - {AuthOptions? authOptions, TokenParams? tokenParams}) => - _realtime.invokeRequest( - PlatformMethod.realtimeAuthAuthorize, { - TxTransportKeys.options: authOptions, - TxTransportKeys.params: tokenParams - }); + Future authorize({ + AuthOptions? authOptions, + TokenParams? tokenParams, + }) => _realtime.invokeRequest( + PlatformMethod.realtimeAuthAuthorize, + {TxTransportKeys.options: authOptions, TxTransportKeys.params: tokenParams}, + ); @override Future get clientId async => _realtime.invokeRequest(PlatformMethod.realtimeAuthGetClientId); @override - Future createTokenRequest( - {AuthOptions? authOptions, TokenParams? tokenParams}) => - _realtime.invokeRequest( - PlatformMethod.realtimeAuthCreateTokenRequest, { - TxTransportKeys.options: authOptions, - TxTransportKeys.params: tokenParams - }); + Future createTokenRequest({ + AuthOptions? authOptions, + TokenParams? tokenParams, + }) => _realtime.invokeRequest( + PlatformMethod.realtimeAuthCreateTokenRequest, + {TxTransportKeys.options: authOptions, TxTransportKeys.params: tokenParams}, + ); @override - Future requestToken( - {AuthOptions? authOptions, TokenParams? tokenParams}) => - _realtime.invokeRequest( - PlatformMethod.realtimeAuthRequestToken, { - TxTransportKeys.options: authOptions, - TxTransportKeys.params: tokenParams - }); + Future requestToken({ + AuthOptions? authOptions, + TokenParams? tokenParams, + }) => _realtime.invokeRequest( + PlatformMethod.realtimeAuthRequestToken, + {TxTransportKeys.options: authOptions, TxTransportKeys.params: tokenParams}, + ); } diff --git a/lib/src/realtime/src/realtime_channel_options.dart b/lib/src/realtime/src/realtime_channel_options.dart index db525c3aa..7e3ed45f4 100644 --- a/lib/src/realtime/src/realtime_channel_options.dart +++ b/lib/src/realtime/src/realtime_channel_options.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Passes additional properties to a [RealtimeChannel] object, such as @@ -28,9 +28,5 @@ class RealtimeChannelOptions { /// If a [cipherParams] is set, messages will be encrypted with the cipher. /// /// https://docs.ably.com/client-lib-development-guide/features/#TB2 - const RealtimeChannelOptions({ - this.cipherParams, - this.modes, - this.params, - }); + const RealtimeChannelOptions({this.cipherParams, this.modes, this.params}); } diff --git a/lib/src/realtime/src/realtime_history_params.dart b/lib/src/realtime/src/realtime_history_params.dart index 7435e20c9..bcbbeb115 100644 --- a/lib/src/realtime/src/realtime_history_params.dart +++ b/lib/src/realtime/src/realtime_history_params.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Contains properties used to filter [RealtimeChannel] history. @@ -41,12 +41,13 @@ class RealtimeHistoryParams { this.limit = 100, DateTime? start, this.untilAttach, - }) : assert(direction == 'backwards' || direction == 'forwards'), - end = end ?? DateTime.now(), - start = start ?? DateTime.fromMillisecondsSinceEpoch(0); + }) : assert(direction == 'backwards' || direction == 'forwards'), + end = end ?? DateTime.now(), + start = start ?? DateTime.fromMillisecondsSinceEpoch(0); @override - String toString() => 'RealtimeHistoryParams:' + String toString() => + 'RealtimeHistoryParams:' ' start=$start' ' end=$end' ' direction=$direction' diff --git a/lib/src/realtime/src/realtime_presence_params.dart b/lib/src/realtime/src/realtime_presence_params.dart index ff35d5859..8735b3858 100644 --- a/lib/src/realtime/src/realtime_presence_params.dart +++ b/lib/src/realtime/src/realtime_presence_params.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:meta/meta.dart'; /// Contains properties used to filter [RealtimePresence] members. diff --git a/lib/src/rest/rest.dart b/lib/src/rest/rest.dart deleted file mode 100644 index 9f5237bbe..000000000 --- a/lib/src/rest/rest.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'src/rest_channel_options.dart'; -export 'src/rest_history_params.dart'; -export 'src/rest_presence_params.dart'; diff --git a/lib/src/rest/src/rest_auth.dart b/lib/src/rest/src/rest_auth.dart deleted file mode 100644 index d9d2897d1..000000000 --- a/lib/src/rest/src/rest_auth.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; - -/// See [Auth] for more information. -class RestAuth extends Auth { - final Rest _rest; - - /// Constructor for RestAuth - RestAuth(this._rest); - - @override - Future authorize( - {AuthOptions? authOptions, TokenParams? tokenParams}) => - _rest.invokeRequest(PlatformMethod.restAuthAuthorize, { - TxTransportKeys.options: authOptions, - TxTransportKeys.params: tokenParams - }); - - @override - Future get clientId async => - _rest.invoke(PlatformMethod.restAuthGetClientId); - - @override - Future createTokenRequest( - {AuthOptions? authOptions, TokenParams? tokenParams}) => - _rest.invokeRequest( - PlatformMethod.restAuthCreateTokenRequest, { - TxTransportKeys.options: authOptions, - TxTransportKeys.params: tokenParams - }); - - @override - Future requestToken( - {AuthOptions? authOptions, TokenParams? tokenParams}) => - _rest.invokeRequest(PlatformMethod.restAuthRequestToken, { - TxTransportKeys.options: authOptions, - TxTransportKeys.params: tokenParams - }); -} diff --git a/lib/src/rest/src/rest_channel_options.dart b/lib/src/rest/src/rest_channel_options.dart deleted file mode 100644 index c0e9f50eb..000000000 --- a/lib/src/rest/src/rest_channel_options.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; - -/// Passes additional properties to a [RestChannel] object, such as encryption, -/// [ChannelMode]and channel parameters. -class RestChannelOptions { - /// [Channel Parameters](https://ably.com/docs/realtime/channels/channel-parameters/overview - /// that configure the behavior of the channel. - final CipherParams? cipherParams; - - /// Constructor `withCipherKey`, that only takes a private [key] used to - /// encrypt and decrypt payloads. - static Future withCipherKey(dynamic key) async { - final cipherParams = await Crypto.getDefaultParams(key: key); - return RestChannelOptions(cipherParams: cipherParams); - } - - /// @nodoc - /// Create channel options with a cipher. - /// If a [cipherParams] is set, messages will be encrypted with the cipher. - /// - /// https://docs.ably.com/client-lib-development-guide/features/#TB2 - RestChannelOptions({this.cipherParams}); -} diff --git a/lib/src/rest/src/rest_history_params.dart b/lib/src/rest/src/rest_history_params.dart deleted file mode 100644 index 51575895f..000000000 --- a/lib/src/rest/src/rest_history_params.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; - -/// Contains properties used to filter [RestChannel] history. -class RestHistoryParams { - /// The [DateTime] from which messages are retrieved. - final DateTime start; - - /// The [DateTime] until messages are retrieved. - final DateTime end; - - /// The order for which messages are returned in. Valid values are `backwards` - /// which orders messages from most recent to oldest, or `forwards` which - /// orders messages from oldest to most recent. - /// - /// The default is `backwards`. - final String direction; - - /// An upper limit on the number of messages returned. - /// - /// The default is 100, and the maximum is 1000. - final int limit; - - /// @nodoc - /// instantiates with [direction] set to "backwards", [limit] to 100 - /// [start] to epoch and end to current time - /// - /// Raises [AssertionError] if [direction] is not "backwards" or "forwards" - RestHistoryParams({ - this.direction = 'backwards', - DateTime? end, - this.limit = 100, - DateTime? start, - }) : assert(direction == 'backwards' || direction == 'forwards'), - end = end ?? DateTime.now(), - start = start ?? DateTime.fromMillisecondsSinceEpoch(0); - - @override - String toString() => 'RestHistoryParams:' - ' start=$start' - ' end=$end' - ' direction=$direction' - ' limit=$limit'; -} diff --git a/lib/src/rest/src/rest_presence_params.dart b/lib/src/rest/src/rest_presence_params.dart deleted file mode 100644 index 3c082ef0e..000000000 --- a/lib/src/rest/src/rest_presence_params.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; - -/// Contains properties used to filter [RestPresence] members. -class RestPresenceParams { - /// An upper limit on the number of messages returned. - /// - /// The default is 100, and the maximum is 1000. - int limit; - - /// Filters the list of returned presence members by a specific client using - /// its ID. - String? clientId; - - /// Filters the list of returned presence members by a specific connection - /// using its ID. - String? connectionId; - - /// @nodoc - /// initializes with default [limit] set to 100 - RestPresenceParams({ - this.clientId, - this.connectionId, - this.limit = 100, - }); -} diff --git a/lib/src/stats/src/stats.dart b/lib/src/stats/src/stats.dart index 4b6cbe823..aa3402169 100644 --- a/lib/src/stats/src/stats.dart +++ b/lib/src/stats/src/stats.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains application statistics for a specified time interval and time /// period. diff --git a/lib/src/stats/src/stats_connection_types.dart b/lib/src/stats/src/stats_connection_types.dart index d056034e6..7c8278856 100644 --- a/lib/src/stats/src/stats_connection_types.dart +++ b/lib/src/stats/src/stats_connection_types.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains a breakdown of summary stats data for different (TLS vs non-TLS) /// connection types. diff --git a/lib/src/stats/src/stats_message_traffic.dart b/lib/src/stats/src/stats_message_traffic.dart index bdf22e3f0..929b3c1e8 100644 --- a/lib/src/stats/src/stats_message_traffic.dart +++ b/lib/src/stats/src/stats_message_traffic.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains a breakdown of summary stats data for traffic over various /// transport types. diff --git a/lib/src/stats/src/stats_message_types.dart b/lib/src/stats/src/stats_message_types.dart index 2e3e7d01c..05bb15bc7 100644 --- a/lib/src/stats/src/stats_message_types.dart +++ b/lib/src/stats/src/stats_message_types.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Contains a breakdown of summary stats data for different /// (channel vs presence) message types. diff --git a/pubspec.lock b/pubspec.lock index e7d20b3e8..0144121b7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,34 +21,34 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: "direct main" description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" fake_async: dependency: "direct dev" description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" flutter: dependency: "direct main" description: flutter @@ -71,26 +71,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -103,34 +103,34 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.20" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: "direct main" description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.19.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -148,18 +148,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: @@ -180,18 +180,18 @@ packages: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.12" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.4.2" vm_service: dependency: transitive description: @@ -201,5 +201,5 @@ packages: source: hosted version: "14.3.0" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8974c9cab..0fb76ebfa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,20 @@ -name: ably_flutter +name: ably_pubsub_device_flutter description: A wrapper around Ably's Cocoa and Java client library SDKs, providing iOS and Android support. version: 1.2.44 repository: https://github.com/ably/ably-pubsub-flutter environment: - sdk: ">=2.14.0 <4.0.0" - flutter: ">=2.5.0" + # Two iOS floors meet at Flutter 3.44: + # + # - The UIScene plugin APIs the iOS side relies on + # (`FlutterSceneLifeCycleDelegate` and + # `FlutterPluginRegistrar.addSceneDelegate:`) first shipped in 3.38. See + # PushNotifications.md. + # - 3.44 is the first release where Swift Package Manager is the default + # dependency manager rather than an opt-in experiment, which is what lets + # this plugin ship a SwiftPM manifest and retire its podspec. + sdk: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" dependencies: collection: ^1.15.0 diff --git a/test/ably_event_listener_test.dart b/test/ably_event_listener_test.dart index 3f1fb0f25..b99898292 100644 --- a/test/ably_event_listener_test.dart +++ b/test/ably_event_listener_test.dart @@ -69,8 +69,9 @@ void main() { //Checking if data received by stream is same as expected expect( - const ListEquality().equals(resultsDefault, emitter.injectables), - true); + const ListEquality().equals(resultsDefault, emitter.injectables), + true, + ); expect(const ListEquality().equals(resultsNestedPre, [1, 2]), true); expect(const ListEquality().equals(resultsNestedPost, [1]), true); }); diff --git a/test/ably_flutter_plugin_test.dart b/test/ably_flutter_plugin_test.dart deleted file mode 100644 index 9fd9cc46d..000000000 --- a/test/ably_flutter_plugin_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - final channel = - MethodChannel('io.ably.flutter.plugin', StandardMethodCodec(Codec())); - - TestWidgetsFlutterBinding.ensureInitialized(); - var counter = 0; - - //test constants - const _platformVersion = '42'; - const _nativeLibraryVersion = '1.1.0'; - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, (methodCall) async { - switch (methodCall.method) { - case PlatformMethod.resetAblyClients: - return true; - - case PlatformMethod.getPlatformVersion: - return _platformVersion; - case PlatformMethod.getVersion: - return _nativeLibraryVersion; - - case PlatformMethod.createRest: - case PlatformMethod.createRealtime: - return ++counter; - - case PlatformMethod.publish: - case PlatformMethod.connectRealtime: - default: - return null; - } - }); - Platform(methodChannel: channel); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, null); - }); - - test(PlatformMethod.getPlatformVersion, () async { - expect(await platformVersion(), _platformVersion); - }); - - test(PlatformMethod.getVersion, () async { - expect(await version(), _nativeLibraryVersion); - }); - - test(PlatformMethod.createRest, () async { - const host = 'http://rest.ably.io/'; - final o = ClientOptions( - restHost: host, - ); - final rest = Rest(options: o); - expect(await rest.handle, counter); - expect(rest.options.restHost, host); - }); - - test('createRestWithToken', () async { - const key = 'TEST-KEY'; - final rest = Rest.fromKey(key); - expect(await rest.handle, counter); - expect(rest.options.tokenDetails!.token, key); - }); - - test('createRestWithKey', () async { - const key = 'TEST:KEY'; - final rest = Rest.fromKey(key); - expect(await rest.handle, counter); - expect(rest.options.key, key); - }); - - test('publishMessage', () async { - final rest = Rest.fromKey('TEST-KEY'); - await rest.channels.get('test').publish(name: 'name', data: 'data'); - expect(1, 1); - }); -} diff --git a/test/ably_pubsub_device_flutter_plugin_test.dart b/test/ably_pubsub_device_flutter_plugin_test.dart new file mode 100644 index 000000000..4bd9ad18e --- /dev/null +++ b/test/ably_pubsub_device_flutter_plugin_test.dart @@ -0,0 +1,83 @@ +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + final channel = MethodChannel( + 'io.ably.flutter.plugin', + StandardMethodCodec(Codec()), + ); + + TestWidgetsFlutterBinding.ensureInitialized(); + var counter = 0; + + //test constants + const _platformVersion = '42'; + const _nativeLibraryVersion = '1.1.0'; + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (methodCall) async { + switch (methodCall.method) { + case PlatformMethod.resetAblyClients: + return true; + + case PlatformMethod.getPlatformVersion: + return _platformVersion; + case PlatformMethod.getVersion: + return _nativeLibraryVersion; + + case PlatformMethod.createRealtime: + return ++counter; + + case PlatformMethod.publishRealtimeChannelMessage: + case PlatformMethod.connectRealtime: + default: + return null; + } + }); + Platform(methodChannel: channel); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test(PlatformMethod.getPlatformVersion, () async { + expect(await platformVersion(), _platformVersion); + }); + + test(PlatformMethod.getVersion, () async { + expect(await version(), _nativeLibraryVersion); + }); + + test(PlatformMethod.createRealtime, () async { + const endpoint = 'nonprod:sandbox'; + final o = ClientOptions(endpoint: endpoint); + final realtime = createClient(options: o); + expect(await realtime.handle, counter); + expect(realtime.options.endpoint, endpoint); + }); + + test('createRealtimeWithToken', () async { + const key = 'TEST-KEY'; + final realtime = createClient(options: ClientOptions(key: key)); + expect(await realtime.handle, counter); + expect(realtime.options.tokenDetails!.token, key); + }); + + test('createRealtimeWithKey', () async { + const key = 'TEST:KEY'; + final realtime = createClient(options: ClientOptions(key: key)); + expect(await realtime.handle, counter); + expect(realtime.options.key, key); + }); + + test('publishMessage', () async { + final realtime = createClient(options: ClientOptions(key: 'TEST-KEY')); + await realtime.channels.get('test').publish(name: 'name', data: 'data'); + expect(1, 1); + }); +} diff --git a/test/ios_plugin_version_test.dart b/test/ios_plugin_version_test.dart new file mode 100644 index 000000000..ff0f968a7 --- /dev/null +++ b/test/ios_plugin_version_test.dart @@ -0,0 +1,26 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; + +/// The iOS plugin reports its version to Ably in the `ably-flutter` agent. +/// Unlike the podspec it replaced, its Swift package cannot read +/// `pubspec.yaml`, so the version is duplicated in a header that has to be +/// bumped alongside it. +void main() { + test('iOS plugin version matches pubspec.yaml', () { + final pubspecVersion = RegExp( + r'^version:\s*(\S+)', + multiLine: true, + ).firstMatch(File('pubspec.yaml').readAsStringSync())!.group(1); + final iosVersion = RegExp('FLUTTER_PACKAGE_PLUGIN_VERSION @"([^"]+)"') + .firstMatch( + File( + 'ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/' + 'codec/AblyFlutterVersion.h', + ).readAsStringSync(), + )! + .group(1); + + expect(iosVersion, pubspecVersion); + }); +} diff --git a/test/mock_method_call_manager.dart b/test/mock_method_call_manager.dart index 57860137b..ead1a658c 100644 --- a/test/mock_method_call_manager.dart +++ b/test/mock_method_call_manager.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -10,10 +10,13 @@ class MockMethodCallManager { bool isAuthenticated = false; final channels = {}; final publishedMessages = >[]; + PlatformException? releaseRealtimeChannelError; MockMethodCallManager() { - final channel = - MethodChannel('io.ably.flutter.plugin', StandardMethodCodec(Codec())); + final channel = MethodChannel( + 'io.ably.flutter.plugin', + StandardMethodCodec(Codec()), + ); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, handler); Platform(methodChannel: channel); @@ -22,9 +25,12 @@ class MockMethodCallManager { void reset() { channels.clear(); publishedMessages.clear(); + releaseRealtimeChannelError = null; handleCounter = 0; - final channel = - MethodChannel('io.ably.flutter.plugin', StandardMethodCodec(Codec())); + final channel = MethodChannel( + 'io.ably.flutter.plugin', + StandardMethodCodec(Codec()), + ); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, handler); Platform(methodChannel: channel); @@ -35,7 +41,6 @@ class MockMethodCallManager { case PlatformMethod.resetAblyClients: return true; - case PlatformMethod.createRest: case PlatformMethod.createRealtime: final handle = ++handleCounter; final ablyMessage = methodCall.arguments as AblyMessage; @@ -45,27 +50,6 @@ class MockMethodCallManager { channels[handle] = channelOptions; return handle; - case PlatformMethod.publish: - final ablyMessage = methodCall.arguments as AblyMessage; - final clientOptions = channels[ablyMessage.handle!] as ClientOptions; - - // `authUrl` is used to indicate the presence of an authCallback, - // because function references (in `authCallback`) get dropped by the - // PlatformChannel. - if (!isAuthenticated && clientOptions.authUrl == 'hasAuthCallback') { - final channel = MethodChannel( - 'io.ably.flutter.plugin', StandardMethodCodec(Codec())); - await AblyMethodCallHandler(channel).onAuthCallback( - AblyMessage( - message: TokenParams(timestamp: DateTime.now()), - handle: ablyMessage.handle, - ), - ); - isAuthenticated = true; - } - publishedMessages.add(ablyMessage); - return null; - case PlatformMethod.publishRealtimeChannelMessage: final ablyMessage = methodCall.arguments as AblyMessage; final clientOptions = channels[ablyMessage.handle!] as ClientOptions; @@ -75,12 +59,12 @@ class MockMethodCallManager { // PlatformChannel. if (!isAuthenticated && clientOptions.authUrl == 'hasAuthCallback') { final channel = MethodChannel( - 'io.ably.flutter.plugin', StandardMethodCodec(Codec())); + 'io.ably.flutter.plugin', + StandardMethodCodec(Codec()), + ); await AblyMethodCallHandler(channel).onRealtimeAuthCallback( AblyMessage( - message: TokenParams( - timestamp: DateTime.now(), - ), + message: TokenParams(timestamp: DateTime.now()), handle: ablyMessage.handle, ), ); @@ -90,13 +74,17 @@ class MockMethodCallManager { publishedMessages.add(ablyMessage); return null; - case PlatformMethod.releaseRestChannel: case PlatformMethod.releaseRealtimeChannel: + if (releaseRealtimeChannelError != null) { + throw releaseRealtimeChannelError!; + } return null; default: - return throw Exception('Unexpected method call: ${methodCall.method}' - ' args: ${methodCall.arguments}'); + return throw Exception( + 'Unexpected method call: ${methodCall.method}' + ' args: ${methodCall.arguments}', + ); } } } diff --git a/test/models/client_options.dart b/test/models/client_options.dart index 49a211220..ba5552c5a 100644 --- a/test/models/client_options.dart +++ b/test/models/client_options.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -11,8 +11,7 @@ void main() { expect(clientOptions.clientId, isNull); expect(clientOptions.logLevel, LogLevel.info); expect(clientOptions.tls, isNull); - expect(clientOptions.restHost, isNull); - expect(clientOptions.realtimeHost, isNull); + expect(clientOptions.endpoint, isNull); expect(clientOptions.port, isNull); expect(clientOptions.tlsPort, isNull); expect(clientOptions.autoConnect, isNull); @@ -20,15 +19,12 @@ void main() { expect(clientOptions.queueMessages, isNull); expect(clientOptions.echoMessages, isNull); expect(clientOptions.recover, isNull); - expect(clientOptions.environment, isNull); expect(clientOptions.idempotentRestPublishing, isNull); expect(clientOptions.httpOpenTimeout, isNull); expect(clientOptions.httpRequestTimeout, isNull); expect(clientOptions.httpMaxRetryCount, isNull); expect(clientOptions.realtimeRequestTimeout, isNull); expect(clientOptions.fallbackHosts, isNull); - // ignore: deprecated_member_use_from_same_package - expect(clientOptions.fallbackHostsUseDefault, isNull); expect(clientOptions.fallbackRetryTimeout, isNull); expect(clientOptions.defaultTokenParams, isNull); expect(clientOptions.channelRetryTimeout, isNull); diff --git a/test/models/message_test.dart b/test/models/message_test.dart index 2e683e911..f3bef5a5f 100644 --- a/test/models/message_test.dart +++ b/test/models/message_test.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -8,12 +8,12 @@ void main() { const connectionId = 'connection-id'; const data = { 'a': 'b', - 'c': [1, 2, 3] + 'c': [1, 2, 3], }; const encoding = 'msgpack'; const extras = { 'a': 'b', - 'c': [1, 2, 3] + 'c': [1, 2, 3], }; final timestamp = DateTime.now(); @@ -77,21 +77,23 @@ void main() { expect(message.hashCode == message2.hashCode, true); }); - test("#== is false and hashes don't match when attributes are not same", - () { - final message2 = Message( - id: messageId, - name: 'other-name', - clientId: clientId, - connectionId: connectionId, - data: data, - encoding: encoding, - extras: const MessageExtras(extras), - timestamp: timestamp, - ); - expect(message == message2, false); - expect(message.hashCode == message2.hashCode, false); - }); + test( + "#== is false and hashes don't match when attributes are not same", + () { + final message2 = Message( + id: messageId, + name: 'other-name', + clientId: clientId, + connectionId: connectionId, + data: data, + encoding: encoding, + extras: const MessageExtras(extras), + timestamp: timestamp, + ); + expect(message == message2, false); + expect(message.hashCode == message2.hashCode, false); + }, + ); test("#== is false and hashes don't match for different classes", () { final object = Object(); @@ -139,7 +141,7 @@ void main() { 'encoding': encoding, 'extras': extras, 'timestamp': timestamp.millisecondsSinceEpoch, - } + }, ]); final message = messages[0]; expect(message.id, messageId); @@ -167,8 +169,9 @@ void main() { expect(message.extras, null); }); test('a map of extras is allowed', () { - final message = - Message(extras: const MessageExtras({'key': 'value'})); + final message = Message( + extras: const MessageExtras({'key': 'value'}), + ); expect(message.extras!.map, const {'key': 'value'}); }); }); diff --git a/test/models/presence_message_test.dart b/test/models/presence_message_test.dart index 9c1fccf76..c62c072ce 100644 --- a/test/models/presence_message_test.dart +++ b/test/models/presence_message_test.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -9,12 +9,12 @@ void main() { const connectionId = 'connection-id'; const data = { 'a': 'b', - 'c': [1, 2, 3] + 'c': [1, 2, 3], }; const encoding = 'msgpack'; const extras = { 'a': 'b', - 'c': [1, 2, 3] + 'c': [1, 2, 3], }; final timestamp = DateTime.now(); group('Behaves like a model', () { @@ -68,21 +68,23 @@ void main() { expect(presenceMessage.hashCode == presenceMessage2.hashCode, true); }); - test("#== is false and hashes don't match when attributes are not same", - () { - final presenceMessage2 = PresenceMessage( - id: '123', - action: action, - clientId: clientId, - connectionId: connectionId, - data: data, - encoding: encoding, - extras: const MessageExtras(extras), - timestamp: timestamp, - ); - expect(presenceMessage == presenceMessage2, false); - expect(presenceMessage.hashCode == presenceMessage2.hashCode, false); - }); + test( + "#== is false and hashes don't match when attributes are not same", + () { + final presenceMessage2 = PresenceMessage( + id: '123', + action: action, + clientId: clientId, + connectionId: connectionId, + data: data, + encoding: encoding, + extras: const MessageExtras(extras), + timestamp: timestamp, + ); + expect(presenceMessage == presenceMessage2, false); + expect(presenceMessage.hashCode == presenceMessage2.hashCode, false); + }, + ); test("#== is false and hashes don't match for different classes", () { final object = Object(); @@ -93,22 +95,32 @@ void main() { group('memberKey attribute', () { test('is connectionId:clientId', () { - final presenceMessage = - PresenceMessage(clientId: clientId, connectionId: connectionId); + final presenceMessage = PresenceMessage( + clientId: clientId, + connectionId: connectionId, + ); expect(presenceMessage.memberKey, '$connectionId:$clientId'); }); test('is unique with the same client id across multiple connections', () { - final presenceMessage = - PresenceMessage(clientId: clientId, connectionId: connectionId); - final presenceMessage2 = - PresenceMessage(clientId: clientId, connectionId: 'different'); + final presenceMessage = PresenceMessage( + clientId: clientId, + connectionId: connectionId, + ); + final presenceMessage2 = PresenceMessage( + clientId: clientId, + connectionId: 'different', + ); expect(presenceMessage.memberKey == presenceMessage2.memberKey, false); }); test('is unique with a single connection and different client_ids', () { - final presenceMessage = - PresenceMessage(clientId: clientId, connectionId: connectionId); - final presenceMessage2 = - PresenceMessage(clientId: 'different', connectionId: connectionId); + final presenceMessage = PresenceMessage( + clientId: clientId, + connectionId: connectionId, + ); + final presenceMessage2 = PresenceMessage( + clientId: 'different', + connectionId: connectionId, + ); expect(presenceMessage.memberKey == presenceMessage2.memberKey, false); }); }); @@ -123,7 +135,7 @@ void main() { 'data': data, 'encoding': encoding, 'extras': extras, - 'timestamp': timestamp.millisecondsSinceEpoch + 'timestamp': timestamp.millisecondsSinceEpoch, }); expect(presenceMessage.id, messageId); expect(presenceMessage.action, action); @@ -151,7 +163,7 @@ void main() { 'encoding': encoding, 'extras': extras, 'timestamp': timestamp.millisecondsSinceEpoch, - } + }, ]); final presenceMessage = presenceMessages[0]; expect(presenceMessage.id, messageId); diff --git a/test/platform/info_test.dart b/test/platform/info_test.dart index 87d17696e..dbced8593 100644 --- a/test/platform/info_test.dart +++ b/test/platform/info_test.dart @@ -1,6 +1,7 @@ import 'dart:io' as io show Platform; -import 'package:ably_flutter/ably_flutter.dart' as ably; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' + as ably; import 'package:flutter_test/flutter_test.dart'; import '../test_constants.dart'; diff --git a/test/push_notifications/push_channel_test.dart b/test/push_notifications/push_channel_test.dart new file mode 100644 index 000000000..ab51a5c48 --- /dev/null +++ b/test/push_notifications/push_channel_test.dart @@ -0,0 +1,37 @@ +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../mock_method_call_manager.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late MockMethodCallManager manager; + late PushChannel pushChannel; + + setUp(() { + manager = MockMethodCallManager(); + final realtime = createClient(options: ClientOptions(key: 'TEST-KEY')); + pushChannel = realtime.channels.get('channel').push; + }); + + tearDown(() { + manager.reset(); + }); + + group('PushChannel#listSubscriptions', () { + for (final params in >[ + {}, + {'channel': 'channel'}, + {'deviceClientId': 'client'}, + {'deviceId': 'device', 'clientId': 'client'}, + ]) { + test('rejects $params', () { + expect( + () => pushChannel.listSubscriptions(params), + throwsA(isA()), + ); + }); + } + }); +} diff --git a/test/realtime/channel_test.dart b/test/realtime/channel_test.dart index 23c1844e8..b78265de5 100644 --- a/test/realtime/channel_test.dart +++ b/test/realtime/channel_test.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; import 'package:flutter_test/flutter_test.dart'; import '../mock_method_call_manager.dart'; @@ -20,13 +20,17 @@ void main() { group('realtime#channels#channel', () { test('publish realtime message without authCallback', () async { // setup - final realtime = Realtime(key: 'TEST-KEY'); + final realtime = createClient(options: ClientOptions(key: 'TEST-KEY')); final channel = realtime.channels.get('test'); // exercise await channel.publish(name: 'name', data: 'data1'); - await channel.publish(message: Message(name: 'name', data: 'data')); - await channel.publish(messages: [Message(name: 'name', data: 'data')]); + await channel.publish( + message: Message(name: 'name', data: 'data'), + ); + await channel.publish( + messages: [Message(name: 'name', data: 'data')], + ); // verification expect(manager.publishedMessages.length, 3); @@ -34,8 +38,9 @@ void main() { final messageData = firstMessage.message as Map; expect(messageData[TxTransportKeys.channelName], 'test'); expect(messageData[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData[TxTransportKeys.messages] as List); + final messages = List.from( + messageData[TxTransportKeys.messages] as List, + ); expect(messages[0].name, 'name'); expect(messages[0].data, 'data1'); }); @@ -48,7 +53,7 @@ void main() { authCallback: authCallback, authUrl: 'hasAuthCallback', ); - final realtime = Realtime(options: options, key: 'TEST-KEY'); + final realtime = createClient(options: options); final channel = realtime.channels.get('test'); @@ -62,8 +67,9 @@ void main() { final messageData = firstMessage.message as Map; expect(messageData[TxTransportKeys.channelName], 'test'); expect(messageData[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData[TxTransportKeys.messages] as List); + final messages = List.from( + messageData[TxTransportKeys.messages] as List, + ); expect(messages[0].name, 'name'); expect(messages[0].data, 'data2'); }); @@ -74,7 +80,7 @@ void main() { authCallback: (tokenParams) async => 'token', authUrl: 'hasAuthCallback', ); - final realtime = Realtime(options: options, key: 'TEST-KEY'); + final realtime = createClient(options: options); final channel = realtime.channels.get('test'); // exercise @@ -97,8 +103,9 @@ void main() { final messageData = firstMessage.message as Map; expect(messageData[TxTransportKeys.channelName], 'test'); expect(messageData[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData[TxTransportKeys.messages] as List); + final messages = List.from( + messageData[TxTransportKeys.messages] as List, + ); expect(messages[0].name, 'name'); expect(messages[0].data, 'data3-1'); }); @@ -111,7 +118,7 @@ void main() { authCallback: authCallback, authUrl: 'hasAuthCallback', ); - final realtime = Realtime(options: options, key: 'TEST-KEY'); + final realtime = createClient(options: options); final channel = realtime.channels.get('test'); // exercise @@ -124,8 +131,9 @@ void main() { final messageData0 = message0.message as Map; expect(messageData0[TxTransportKeys.channelName], 'test'); expect(messageData0[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData0[TxTransportKeys.messages] as List); + final messages = List.from( + messageData0[TxTransportKeys.messages] as List, + ); expect(messages[0].name, 'name'); expect(messages[0].data, 'data4'); @@ -133,8 +141,9 @@ void main() { final messageData1 = message1.message as Map; expect(messageData1[TxTransportKeys.channelName], 'test'); expect(messageData1[TxTransportKeys.messages], isA>()); - final messages1 = - List.from(messageData1[TxTransportKeys.messages] as List); + final messages1 = List.from( + messageData1[TxTransportKeys.messages] as List, + ); expect(messages1[0].name, 'name'); expect(messages1[0].data, 'data5'); }, timeout: Timeout.none); diff --git a/test/rest/channels_test.dart b/test/realtime/channels_test.dart similarity index 56% rename from test/rest/channels_test.dart rename to test/realtime/channels_test.dart index fa3c16d1d..5f1fbcef9 100644 --- a/test/rest/channels_test.dart +++ b/test/realtime/channels_test.dart @@ -1,4 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../mock_method_call_manager.dart'; @@ -7,19 +8,19 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); late MockMethodCallManager manager; - late RestChannels channels; + late RealtimeChannels channels; setUp(() { manager = MockMethodCallManager(); - final rest = Rest.fromKey('TEST-KEY'); - channels = rest.channels; + final realtime = createClient(options: ClientOptions(key: 'TEST-KEY')); + channels = realtime.channels; }); tearDown(() { manager.reset(); }); - group('rest#channels', () { + group('realtime#channels', () { test('creates channel with #get', () { final channel = channels.get('channel-1'); expect(channel.name, 'channel-1'); @@ -64,10 +65,7 @@ void main() { expect( channels.where((e) => e.name.endsWith('3')).map((e) => e.name).toList(), - orderedEquals(const [ - 'channel-3', - 'channel-33', - ]), + orderedEquals(const ['channel-3', 'channel-33']), ); }); @@ -86,31 +84,59 @@ void main() { }); group('#release', () { - test( - '(RSN4a) Takes one argument, the channel name,' - ' and releases the corresponding channel entity', () { + test('(RTS4d) Releasing a channel the platform SDK releases' + ' removes it from the collection', () async { channels ..get('channel-1') ..get('channel-2'); expect(channels.length, 2); - channels.release('channel-1'); + await channels.release('channel-1'); expect(channels.length, 1); expect(channels.exists('channel-1'), false); }); - test( - '(RSN4b) Calling release() with a channel name' + test('(RTS4c) Calling release() with a channel name' ' that does not correspond to an extant channel' - ' entity must return without error', () { + ' must return without error', () async { channels ..get('channel-1') ..get('channel-2'); expect(channels.length, 2); - channels.release('channel-3'); + await channels.release('channel-3'); expect(channels.length, 2); expect(channels.exists('channel-1'), true); expect(channels.exists('channel-2'), true); }); + + test('(RTS4e) If the platform SDK fails to release the channel,' + ' release() completes with its error and the channel is not' + ' removed', () async { + final channel = channels.get('channel-1'); + manager.releaseRealtimeChannelError = PlatformException( + code: '90011', + message: + 'Can only release a channel in a state where there is no' + ' possibility of further updates from the server being' + ' received (initialized, detached, or failed).' + ' The current state is attached', + details: ErrorInfo(code: 90011, statusCode: 400), + ); + + await expectLater( + channels.release('channel-1'), + throwsA( + isA() + .having((e) => e.errorInfo?.code, 'errorInfo.code', 90011) + .having( + (e) => e.errorInfo?.statusCode, + 'errorInfo.statusCode', + 400, + ), + ), + ); + expect(channels.exists('channel-1'), true); + expect(channels.get('channel-1'), same(channel)); + }); }); }); } diff --git a/test/rest/channel_test.dart b/test/rest/channel_test.dart deleted file mode 100644 index 01a00ead1..000000000 --- a/test/rest/channel_test.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter/src/platform/platform_internal.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import '../mock_method_call_manager.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - late MockMethodCallManager manager; - - setUp(() { - manager = MockMethodCallManager(); - }); - - tearDown(() { - manager.reset(); - }); - - group('rest#channels#channel', () { - test('publishes message without authCallback', () async { - // setup - final rest = Rest.fromKey('TEST-KEY'); - final channel = rest.channels.get('test'); - - // exercise - await channel.publish(name: 'name', data: 'data1'); - - // verification - expect(manager.publishedMessages.length, 1); - final firstMessage = manager.publishedMessages.first; - final messageData = firstMessage.message as Map; - expect(messageData[TxTransportKeys.channelName], 'test'); - expect(messageData[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData[TxTransportKeys.messages] as List); - expect(messages[0].name, 'name'); - expect(messages[0].data, 'data1'); - }); - - test('publishes message with authCallback', () async { - // setup - final authCallback = expectAsync1((token) async => 'token', max: 2); - - final options = ClientOptions( - authCallback: authCallback, - authUrl: 'hasAuthCallback', - ); - final rest = Rest(options: options); - - final channel = rest.channels.get('test'); - - // exercise - await channel.publish(name: 'name', data: 'data2'); - - // verification - expect(manager.publishedMessages.length, 1); - final firstMessage = manager.publishedMessages.first; - final messageData = firstMessage.message as Map; - expect(messageData[TxTransportKeys.channelName], 'test'); - expect(messageData[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData[TxTransportKeys.messages] as List); - expect(messages[0].name, 'name'); - expect(messages[0].data, 'data2'); - }); - - test('publishes message with authCallback', () async { - // setup - final options = ClientOptions( - authCallback: (tokenParams) => Future.value('token'), - authUrl: 'hasAuthCallback', - ); - final rest = Rest(options: options); - final channel = rest.channels.get('test'); - - // exercise - final future1 = channel.publish(name: 'name', data: 'data3-1'); - final future2 = channel.publish(name: 'name', data: 'data3-2'); - - await Future.wait([future1, future2]); - - expect(manager.publishedMessages.length, 2); - final future3 = channel.publish(name: 'name', data: 'data3-3'); - await future3; - expect(manager.publishedMessages.length, 3); - - // setup - // exercise - - // verification - await future3; - - final firstMessage = manager.publishedMessages.first; - final messageData = firstMessage.message as Map; - expect(messageData[TxTransportKeys.channelName], 'test'); - expect(messageData[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData[TxTransportKeys.messages] as List); - expect(messages[0].name, 'name'); - expect(messages[0].data, 'data3-1'); - }); - - test('publishes another message with authCallback', () async { - // setup - final authCallback = expectAsync1((token) async => 'token'); - - final options = ClientOptions( - authCallback: authCallback, - authUrl: 'hasAuthCallback', - ); - final rest = Rest(options: options); - final channel = rest.channels.get('test'); - - // exercise - await channel.publish(name: 'name', data: 'data4'); - await channel.publish(name: 'name', data: 'data5'); - - // verification - expect(manager.publishedMessages.length, 2); - final message0 = manager.publishedMessages[0]; - final messageData0 = message0.message as Map; - expect(messageData0[TxTransportKeys.channelName], 'test'); - expect(messageData0[TxTransportKeys.messages], isA>()); - final messages = - List.from(messageData0[TxTransportKeys.messages] as List); - expect(messages[0].name, 'name'); - expect(messages[0].data, 'data4'); - - final message1 = manager.publishedMessages[1]; - final messageData1 = message1.message as Map; - expect(messageData1[TxTransportKeys.channelName], 'test'); - expect(messageData1[TxTransportKeys.messages], isA>()); - final messages2 = - List.from(messageData1[TxTransportKeys.messages] as List); - expect(messages2[0].name, 'name'); - expect(messages2[0].data, 'data5'); - }, timeout: Timeout.none); - }); -} diff --git a/test/stable_id_generator_test.dart b/test/stable_id_generator_test.dart index 94025efe9..6913613ac 100644 --- a/test/stable_id_generator_test.dart +++ b/test/stable_id_generator_test.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/test_integration/android/gradle.properties b/test_integration/android/gradle.properties index 94adc3a3f..475a62803 100644 --- a/test_integration/android/gradle.properties +++ b/test_integration/android/gradle.properties @@ -1,3 +1,7 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/test_integration/android/gradle/wrapper/gradle-wrapper.properties b/test_integration/android/gradle/wrapper/gradle-wrapper.properties index 5c40527d4..aa02b02fc 100644 --- a/test_integration/android/gradle/wrapper/gradle-wrapper.properties +++ b/test_integration/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/test_integration/android/settings.gradle b/test_integration/android/settings.gradle index 6f0fcae6a..f5633c474 100644 --- a/test_integration/android/settings.gradle +++ b/test_integration/android/settings.gradle @@ -18,7 +18,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.7.0" apply false + id "com.android.application" version "8.12.1" apply false id "org.jetbrains.kotlin.android" version "2.2.20" apply false id "com.google.gms.google-services" version "4.3.10" apply false } diff --git a/test_integration/ios/.gitignore b/test_integration/ios/.gitignore index e96ef602b..eb5573aef 100644 --- a/test_integration/ios/.gitignore +++ b/test_integration/ios/.gitignore @@ -20,6 +20,7 @@ Flutter/Flutter.podspec Flutter/Generated.xcconfig Flutter/app.flx Flutter/app.zip +Flutter/ephemeral/ Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json diff --git a/test_integration/ios/Flutter/AppFrameworkInfo.plist b/test_integration/ios/Flutter/AppFrameworkInfo.plist index 8c6e56146..ab8e063fe 100644 --- a/test_integration/ios/Flutter/AppFrameworkInfo.plist +++ b/test_integration/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 12.0 diff --git a/test_integration/ios/Flutter/Debug.xcconfig b/test_integration/ios/Flutter/Debug.xcconfig index e8efba114..592ceee85 100644 --- a/test_integration/ios/Flutter/Debug.xcconfig +++ b/test_integration/ios/Flutter/Debug.xcconfig @@ -1,2 +1 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/test_integration/ios/Flutter/Release.xcconfig b/test_integration/ios/Flutter/Release.xcconfig index 399e9340e..592ceee85 100644 --- a/test_integration/ios/Flutter/Release.xcconfig +++ b/test_integration/ios/Flutter/Release.xcconfig @@ -1,2 +1 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/test_integration/ios/Podfile b/test_integration/ios/Podfile deleted file mode 100644 index 2c068c404..000000000 --- a/test_integration/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/test_integration/ios/Podfile.lock b/test_integration/ios/Podfile.lock deleted file mode 100644 index 9b1ef0c69..000000000 --- a/test_integration/ios/Podfile.lock +++ /dev/null @@ -1,37 +0,0 @@ -PODS: - - Ably (1.2.53): - - AblyDeltaCodec (= 1.3.3) - - msgpack (= 0.4.0) - - ably_flutter (1.2.44): - - Ably (= 1.2.53) - - Flutter - - AblyDeltaCodec (1.3.3) - - Flutter (1.0.0) - - msgpack (0.4.0) - -DEPENDENCIES: - - ably_flutter (from `.symlinks/plugins/ably_flutter/ios`) - - Flutter (from `Flutter`) - -SPEC REPOS: - trunk: - - Ably - - AblyDeltaCodec - - msgpack - -EXTERNAL SOURCES: - ably_flutter: - :path: ".symlinks/plugins/ably_flutter/ios" - Flutter: - :path: Flutter - -SPEC CHECKSUMS: - Ably: 1756d6590a572809c10fc4bcc32e05fe9e7867d7 - ably_flutter: 95785161fc2858c7be524af4334925d30682b3b0 - AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - msgpack: c85f6251873059738472ae136951cec5f30f3251 - -PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 - -COCOAPODS: 1.16.2 diff --git a/test_integration/ios/Runner.xcodeproj/project.pbxproj b/test_integration/ios/Runner.xcodeproj/project.pbxproj index b2609ce9a..4955de398 100644 --- a/test_integration/ios/Runner.xcodeproj/project.pbxproj +++ b/test_integration/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,11 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 4D9D76E1E8A19986F9184800 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E3D3643591DB473C96D602C /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -32,12 +32,10 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2587A31513079E18ED7E76AB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8E3D3643591DB473C96D602C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -45,8 +43,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A461B7ADF69A8962ACC30865 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - F3AD99D2BCA7A7E429A18853 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,7 +51,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4D9D76E1E8A19986F9184800 /* Pods_Runner.framework in Frameworks */, + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -64,24 +61,14 @@ 361F08E1393750955EECE84D /* Pods */ = { isa = PBXGroup; children = ( - A461B7ADF69A8962ACC30865 /* Pods-Runner.debug.xcconfig */, - F3AD99D2BCA7A7E429A18853 /* Pods-Runner.release.xcconfig */, - 2587A31513079E18ED7E76AB /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; - 509D4C1037122997385BC263 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 8E3D3643591DB473C96D602C /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -97,7 +84,6 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 361F08E1393750955EECE84D /* Pods */, - 509D4C1037122997385BC263 /* Frameworks */, ); sourceTree = ""; }; @@ -128,17 +114,18 @@ /* Begin PBXNativeTarget section */ 97C146ED1CF9000F007C117D /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - F09491A080F1EF11E576B27E /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - D58BD7A29F0D002DE8A22B63 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -153,6 +140,9 @@ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastUpgradeCheck = 1510; @@ -228,45 +218,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - D58BD7A29F0D002DE8A22B63 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - F09491A080F1EF11E576B27E /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -342,7 +293,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -428,7 +379,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -477,7 +428,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -573,6 +524,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..a0a678bef --- /dev/null +++ b/test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,32 @@ +{ + "pins" : [ + { + "identity" : "ably-pubsub-cocoa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/ably-pubsub-cocoa", + "state" : { + "revision" : "c897becb1b0c644a9c67db89e7b247a083b6c540", + "version" : "2.0.0" + } + }, + { + "identity" : "delta-codec-cocoa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/delta-codec-cocoa", + "state" : { + "revision" : "d53eec08f9443c6160d941327a6f9d8bbb93cea2", + "version" : "1.3.5" + } + }, + { + "identity" : "msgpack-objective-c", + "kind" : "remoteSourceControl", + "location" : "https://github.com/rvi/msgpack-objective-C", + "state" : { + "revision" : "3e36b48e04ecd756cb927bd5f5b9bf6d45e475f9", + "version" : "0.4.0" + } + } + ], + "version" : 2 +} diff --git a/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 4f746537f..0bd6d4227 100644 --- a/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + @@ -45,6 +64,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/test_integration/ios/Runner.xcworkspace/contents.xcworkspacedata b/test_integration/ios/Runner.xcworkspace/contents.xcworkspacedata index 21a3cc14c..1d526a16e 100644 --- a/test_integration/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/test_integration/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,7 +4,4 @@ - - diff --git a/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..a0a678bef --- /dev/null +++ b/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,32 @@ +{ + "pins" : [ + { + "identity" : "ably-pubsub-cocoa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/ably-pubsub-cocoa", + "state" : { + "revision" : "c897becb1b0c644a9c67db89e7b247a083b6c540", + "version" : "2.0.0" + } + }, + { + "identity" : "delta-codec-cocoa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/delta-codec-cocoa", + "state" : { + "revision" : "d53eec08f9443c6160d941327a6f9d8bbb93cea2", + "version" : "1.3.5" + } + }, + { + "identity" : "msgpack-objective-c", + "kind" : "remoteSourceControl", + "location" : "https://github.com/rvi/msgpack-objective-C", + "state" : { + "revision" : "3e36b48e04ecd756cb927bd5f5b9bf6d45e475f9", + "version" : "0.4.0" + } + } + ], + "version" : 2 +} diff --git a/test_integration/ios/Runner/AppDelegate.swift b/test_integration/ios/Runner/AppDelegate.swift index b63630348..c30b367ec 100644 --- a/test_integration/ios/Runner/AppDelegate.swift +++ b/test_integration/ios/Runner/AppDelegate.swift @@ -1,13 +1,16 @@ -import UIKit import Flutter +import UIKit @main -@objc class AppDelegate: FlutterAppDelegate { +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } } diff --git a/test_integration/ios/Runner/Info.plist b/test_integration/ios/Runner/Info.plist index 7841a3952..24a510da5 100644 --- a/test_integration/ios/Runner/Info.plist +++ b/test_integration/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -22,6 +24,29 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -41,9 +66,5 @@ UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - diff --git a/test_integration/lib/app_provisioning.dart b/test_integration/lib/app_provisioning.dart index 652ccfb12..741cb8c9a 100644 --- a/test_integration/lib/app_provisioning.dart +++ b/test_integration/lib/app_provisioning.dart @@ -2,19 +2,19 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; import 'package:http/http.dart' as http; import 'package:http/retry.dart'; /// Used to provision the app with Ably sandbox environment /// A copy of this class is also present in example app, to avoid putting -/// it in the base ably_flutter package +/// it in the base ably_pubsub_device_flutter package class AppProvisioning { - /// Prefix of REST environment used to provision the app. For example, - /// with prefix `sandbox-` URL `sandbox-rest.ably.io` will be used + /// Host of the Ably environment the app is provisioned in. /// - /// Defaults to [defaultEnvironmentPrefix] - String environmentPrefix; + /// Defaults to [defaultHost], the sandbox environment that clients reach + /// with `endpoint: 'nonprod:sandbox'`. + String host; /// List of capabilities for the provisioned key /// @@ -28,13 +28,13 @@ class AppProvisioning { /// Creates instance of [AppProvisioning] AppProvisioning({ - this.environmentPrefix = defaultEnvironmentPrefix, + this.host = defaultHost, this.keyCapabilities = defaultKeyCapabilities, this.pushEnabled = true, }); - /// Default prefix for REST provisioning url - static const String defaultEnvironmentPrefix = 'sandbox-'; + /// Host of the sandbox environment + static const String defaultHost = 'sandbox.realtime.ably-nonprod.net'; /// Default list of key capabilities /// Enables all capabilities on all channels @@ -57,8 +57,7 @@ class AppProvisioning { ); /// URL used for app provisioning requests - String get _provisioningUrl => - 'https://${environmentPrefix}rest.ably.io/apps'; + String get _provisioningUrl => 'https://$host/apps'; /// URL used for getting token request String get _tokenRequestAuthUrl => @@ -66,30 +65,27 @@ class AppProvisioning { /// A set of base headers for HTTP requests Map get _requestHeaders => { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }; + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; /// App spec used to provision the app /// /// See: https://docs.ably.com/client-lib-development-guide/test-api Map> get _appSpec => { - 'namespaces': [ - { - 'id': 'pushenabled', - 'pushEnabled': pushEnabled, - } - ], - 'keys': [ - { - // The need to use jsonEncode here is a requirement of the - // Sandbox Test API. The capability map has to be JSON encoded - // as a string and then appropriately escaped in order for - // presentation within a string value. - 'capability': jsonEncode(keyCapabilities), - }, - ], - }; + 'namespaces': [ + {'id': 'pushenabled', 'pushEnabled': pushEnabled}, + ], + 'keys': [ + { + // The need to use jsonEncode here is a requirement of the + // Sandbox Test API. The capability map has to be JSON encoded + // as a string and then appropriately escaped in order for + // presentation within a string value. + 'capability': jsonEncode(keyCapabilities), + }, + ], + }; /// Makes request to configured Ably provisioning URL and returns a /// new key for test application instance @@ -102,8 +98,10 @@ class AppProvisioning { if (response.statusCode != HttpStatus.created) { log("Server didn't return success. ${response.body}"); - throw HttpException("Server didn't return success." - ' Status: ${response.statusCode} : ${response.body}'); + throw HttpException( + "Server didn't return success." + ' Status: ${response.statusCode} : ${response.body}', + ); } final responseBody = jsonDecode(response.body) as Map; @@ -121,8 +119,10 @@ class AppProvisioning { if (response.statusCode != HttpStatus.ok) { log("Server didn't return success. ${response.body}"); - throw HttpException("Server didn't return success." - ' Status: ${response.statusCode} : ${response.body}'); + throw HttpException( + "Server didn't return success." + ' Status: ${response.statusCode} : ${response.body}', + ); } return Map.castFrom( diff --git a/test_integration/lib/config/test_factory.dart b/test_integration/lib/config/test_factory.dart index d86dc8bba..acb2fbfe8 100644 --- a/test_integration/lib/config/test_factory.dart +++ b/test_integration/lib/config/test_factory.dart @@ -22,24 +22,12 @@ import 'package:ably_flutter_integration_test/test/realtime/realtime_publish_tes import 'package:ably_flutter_integration_test/test/realtime/realtime_publish_with_auth_callback_test.dart'; import 'package:ably_flutter_integration_test/test/realtime/realtime_subscribe.dart'; import 'package:ably_flutter_integration_test/test/realtime/realtime_time_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_auth_client_id_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_authorize_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_capability_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_create_token_request_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_encrypted_publish_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_history_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_history_with_auth_callback_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_presence_get_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_presence_history_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_publish_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_publish_with_auth_callback_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_request_token_test.dart'; -import 'package:ably_flutter_integration_test/test/rest/rest_time_test.dart'; -typedef TestFactory = Future> Function({ - required Reporter reporter, - Map? payload, -}); +typedef TestFactory = + Future> Function({ + required Reporter reporter, + Map? payload, + }); final testFactory = { // platform and app key tests @@ -71,23 +59,6 @@ final testFactory = { TestName.realtimeAuthClientId: testRealtimeAuthClientId, TestName.realtimeWithAuthUrl: testCreateRealtimeWithAuthUrl, - // rest tests - TestName.restCapabilities: testRestCapabilities, - TestName.restEncryptedPublish: testRestEncryptedPublish, - TestName.restEncryptedPublishSpec: testRestEncryptedPublishSpec, - TestName.restHistory: testRestHistory, - TestName.restHistoryWithAuthCallback: testRestHistoryWithAuthCallback, - TestName.restPresenceGet: testRestPresenceGet, - TestName.restPresenceHistory: testRestPresenceHistory, - TestName.restPublish: testRestPublish, - TestName.restPublishSpec: testRestPublishSpec, - TestName.restPublishWithAuthCallback: testRestPublishWithAuthCallback, - TestName.restTime: testRestTime, - TestName.restAuthAuthorize: testRestAuthorize, - TestName.restRequestToken: testRestRequestToken, - TestName.restCreateTokenRequest: testRestCreateTokenRequest, - TestName.restAuthClientId: testRestAuthClientId, - // helper tests TestName.testHelperUnhandledExceptionTest: testHelperUnhandledException, }; diff --git a/test_integration/lib/config/test_names.dart b/test_integration/lib/config/test_names.dart index b4d11428c..9baf6cbd0 100644 --- a/test_integration/lib/config/test_names.dart +++ b/test_integration/lib/config/test_names.dart @@ -38,26 +38,6 @@ class TestName { static const String realtimeAuthClientId = 'realtimeAuthClientId'; static const String realtimeWithAuthUrl = 'realtimeWithAuthUrl'; - // rest - static const String restCapabilities = 'restCapabilities'; - static const String restEncryptedPublish = 'restEncryptedPublish'; - static const String restEncryptedPublishSpec = 'restEncryptedPublishSpec'; - static const String restHistory = 'restHistory'; - static const String restHistoryWithAuthCallback = - 'restHistoryWithAuthCallback'; - static const String restPresenceGet = 'restPresenceGet'; - static const String restPresenceHistory = 'restPresenceHistory'; - static const String restPublish = 'restPublish'; - static const String restPublishSpec = 'restPublishSpec'; - static const String restPublishWithAuthCallback = - 'restPublishWithAuthCallback'; - static const String restTime = 'restTime'; - - static const String restAuthAuthorize = 'restAuthorize'; - static const String restCreateTokenRequest = 'restCreateTokenRequest'; - static const String restRequestToken = 'restRequestToken'; - static const String restAuthClientId = 'restAuthClientId'; - // helpers static const String testHelperFlutterErrorTest = 'testHelperFlutterErrorTest'; static const String testHelperUnhandledExceptionTest = diff --git a/test_integration/lib/driver_data_handler.dart b/test_integration/lib/driver_data_handler.dart index 6f4d14830..0943c8e6d 100644 --- a/test_integration/lib/driver_data_handler.dart +++ b/test_integration/lib/driver_data_handler.dart @@ -17,10 +17,7 @@ Future requestDataForTest( /// Used to encode and decode messages between driver test and test widget. class TestControlMessage { - const TestControlMessage( - this.testName, { - this.payload, - }); + const TestControlMessage(this.testName, {this.payload}); static const testNameKey = 'testName'; static const payloadKey = 'payload'; @@ -39,10 +36,7 @@ class TestControlMessage { payload: jsonValue[payloadKey] as Map?, ); - Map toJson() => { - testNameKey: testName, - payloadKey: payload, - }; + Map toJson() => {testNameKey: testName, payloadKey: payload}; String toJsonEncoded() => json.encode(toJson()); @@ -79,10 +73,10 @@ class TestControlResponseMessage { ); Map toJson() => { - testNameKey: testName, - payloadKey: payload, - logKey: log, - }; + testNameKey: testName, + payloadKey: payload, + logKey: log, + }; String toJsonEncoded() => json.encode(toJson()); diff --git a/test_integration/lib/factory/error_handler.dart b/test_integration/lib/factory/error_handler.dart index 73494efe1..b39a7dfe8 100644 --- a/test_integration/lib/factory/error_handler.dart +++ b/test_integration/lib/factory/error_handler.dart @@ -4,9 +4,11 @@ import 'package:flutter/foundation.dart'; /// to the test driver. class ErrorHandler { static Map encodeFlutterError(FlutterErrorDetails details) { - print('Caught FlutterError::\n' - 'exception: ${details.exception}\n' - 'stack: ${details.stack}'); + print( + 'Caught FlutterError::\n' + 'exception: ${details.exception}\n' + 'stack: ${details.stack}', + ); return { 'exceptionType': '${details.exception.runtimeType}', @@ -20,9 +22,11 @@ class ErrorHandler { static Map encodeException(Object error, StackTrace stack) { print(error); print(stack); - print('Caught Exception::\n' - 'error: $error\n' - 'stack: $stack'); + print( + 'Caught Exception::\n' + 'error: $error\n' + 'stack: $stack', + ); return { 'exceptionType': '${error.runtimeType}', diff --git a/test_integration/lib/factory/reporter.dart b/test_integration/lib/factory/reporter.dart index 112eb513d..469ff12ef 100644 --- a/test_integration/lib/factory/reporter.dart +++ b/test_integration/lib/factory/reporter.dart @@ -8,10 +8,8 @@ class Reporter { final DispatcherController controller; final Completer response; - Reporter( - this.message, - this.controller, - ) : response = Completer(); + Reporter(this.message, this.controller) + : response = Completer(); String get testName => message.testName; diff --git a/test_integration/lib/test/basic_test.dart b/test_integration/lib/test/basic_test.dart index e34593744..1280bea56 100644 --- a/test_integration/lib/test/basic_test.dart +++ b/test_integration/lib/test/basic_test.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testAppKeyProvision({ required Reporter reporter, @@ -21,8 +21,5 @@ Future> testPlatformAndAblyVersion({ final _platformVersion = await platformVersion(); final _ablyVersion = await version(); - return { - 'platformVersion': _platformVersion, - 'ablyVersion': _ablyVersion, - }; + return {'platformVersion': _platformVersion, 'ablyVersion': _ablyVersion}; } diff --git a/test_integration/lib/test/crypto/crypto_ensure_supported_key_length_test.dart b/test_integration/lib/test/crypto/crypto_ensure_supported_key_length_test.dart index df168a881..834e04e50 100644 --- a/test_integration/lib/test/crypto/crypto_ensure_supported_key_length_test.dart +++ b/test_integration/lib/test/crypto/crypto_ensure_supported_key_length_test.dart @@ -1,8 +1,8 @@ import 'dart:typed_data'; -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testCryptoEnsureSupportedKeyLength({ required Reporter reporter, @@ -36,8 +36,9 @@ Future> testCryptoEnsureSupportedKeyLength({ } try { - final keyWith127BitLength = - Uint8List.fromList(List.generate(127, (index) => index)); + final keyWith127BitLength = Uint8List.fromList( + List.generate(127, (index) => index), + ); Crypto.ensureSupportedKeyLength(keyWith127BitLength); } on AblyException catch (exception) { keyWith127BitLengthException = encodeAblyException(exception); diff --git a/test_integration/lib/test/crypto/crypto_generate_random_key_test.dart b/test_integration/lib/test/crypto/crypto_generate_random_key_test.dart index 530b1548c..d4f14d211 100644 --- a/test_integration/lib/test/crypto/crypto_generate_random_key_test.dart +++ b/test_integration/lib/test/crypto/crypto_generate_random_key_test.dart @@ -1,5 +1,5 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testCryptoGenerateRandomKey({ required Reporter reporter, diff --git a/test_integration/lib/test/crypto/crypto_get_default_params.dart b/test_integration/lib/test/crypto/crypto_get_default_params.dart index 8f116537c..6afa5d94b 100644 --- a/test_integration/lib/test/crypto/crypto_get_default_params.dart +++ b/test_integration/lib/test/crypto/crypto_get_default_params.dart @@ -1,8 +1,8 @@ import 'dart:typed_data'; -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testCryptoGetDefaultParams({ required Reporter reporter, @@ -13,8 +13,9 @@ Future> testCryptoGetDefaultParams({ final keyWithDefaultLength = await Crypto.generateRandomKey(); - final keyWith127BitLength = - Uint8List.fromList(List.generate(127, (index) => index)); + final keyWith127BitLength = Uint8List.fromList( + List.generate(127, (index) => index), + ); try { await Crypto.getDefaultParams(key: keyWith127BitLength); diff --git a/test_integration/lib/test/realtime/realtime_auth_client_id_test.dart b/test_integration/lib/test/realtime/realtime_auth_client_id_test.dart index 9575bc6d4..dbebbc2ae 100644 --- a/test_integration/lib/test/realtime/realtime_auth_client_id_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_client_id_test.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimeAuthClientId({ required Reporter reporter, @@ -12,24 +12,24 @@ Future> testRealtimeAuthClientId({ //init ably for token final clientOptionsForToken = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', logLevel: LogLevel.error, + autoConnect: false, ); - final ablyForToken = Rest( - options: clientOptionsForToken, - ); + final ablyForToken = createClient(options: clientOptionsForToken); /* get first token */ final firstToken = await ablyForToken.auth.requestToken(); final clientOptions = ClientOptions( - key: appKey, - environment: 'sandbox', - useTokenAuth: true, - autoConnect: false, - clientId: 'testClientId', - tokenDetails: firstToken); - final realtime = Realtime(options: clientOptions); + key: appKey, + endpoint: 'nonprod:sandbox', + useTokenAuth: true, + autoConnect: false, + clientId: 'testClientId', + tokenDetails: firstToken, + ); + final realtime = createClient(options: clientOptions); await realtime.connect(); final clientId = await realtime.auth.clientId; diff --git a/test_integration/lib/test/realtime/realtime_auth_url_test.dart b/test_integration/lib/test/realtime/realtime_auth_url_test.dart index cea621364..09f7ac5e1 100644 --- a/test_integration/lib/test/realtime/realtime_auth_url_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_url_test.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:ffi'; -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; //Make sure that creating realtime with authUrk connects without a problem Future> testCreateRealtimeWithAuthUrl({ @@ -15,25 +15,24 @@ Future> testCreateRealtimeWithAuthUrl({ final clientOptionsForToken = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', logLevel: LogLevel.error, - fallbackHosts: ['a.ably-realtime.com', 'b.ably-realtime.com'], + autoConnect: false, ); - final ablyForToken = Rest( - options: clientOptionsForToken, - ); + final ablyForToken = createClient(options: clientOptionsForToken); final tokenDetails = await ablyForToken.auth.requestToken(); final authUrl = 'https://echo.ably.io/?body=${Uri.encodeComponent(tokenDetails.token!)}'; final options = ClientOptions( - authUrl: authUrl, - environment: 'sandbox', - useTokenAuth: true, - autoConnect: false, - logLevel: LogLevel.error); - final realtime = Realtime(options: options); + authUrl: authUrl, + endpoint: 'nonprod:sandbox', + useTokenAuth: true, + autoConnect: false, + logLevel: LogLevel.error, + ); + final realtime = createClient(options: options); final completer = Completer(); realtime.connection.on().listen((stateChange) { if (stateChange.current == ConnectionState.connected) { @@ -45,8 +44,10 @@ Future> testCreateRealtimeWithAuthUrl({ throw Error(); } - await completer.future - .timeout(const Duration(seconds: 30), onTimeout: _onTimeout); + await completer.future.timeout( + const Duration(seconds: 30), + onTimeout: _onTimeout, + ); return {'handle': await realtime.handle}; } diff --git a/test_integration/lib/test/realtime/realtime_authorize_test.dart b/test_integration/lib/test/realtime/realtime_authorize_test.dart index c7497c16c..18f5d01ea 100644 --- a/test_integration/lib/test/realtime/realtime_authorize_test.dart +++ b/test_integration/lib/test/realtime/realtime_authorize_test.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimeAuthroize({ required Reporter reporter, @@ -12,36 +12,38 @@ Future> testRealtimeAuthroize({ //init ably for token final clientOptionsForToken = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', logLevel: LogLevel.error, + autoConnect: false, ); - final ablyForToken = Rest( - options: clientOptionsForToken, - ); + final ablyForToken = createClient(options: clientOptionsForToken); /* get first token */ final firstToken = await ablyForToken.auth.requestToken(); final clientOptions = ClientOptions( - key: appKey, - environment: 'sandbox', - useTokenAuth: true, - autoConnect: false, - tokenDetails: firstToken); - final realtime = Realtime(options: clientOptions); + key: appKey, + endpoint: 'nonprod:sandbox', + useTokenAuth: true, + autoConnect: false, + tokenDetails: firstToken, + ); + final realtime = createClient(options: clientOptions); await realtime.connect(); const tokenParams = TokenParams(ttl: 20000); - Future authCallback(params) async => TokenRequest.fromMap( - await AppProvisioning().getTokenRequest(), - ); + Future authCallback(params) async => + TokenRequest.fromMap(await AppProvisioning().getTokenRequest()); final authOptions = AuthOptions( - key: appKey, - useTokenAuth: true, - authCallback: authCallback, - authMethod: 'GET'); - final refreshedToken = await realtime.auth - .authorize(authOptions: authOptions, tokenParams: tokenParams); + key: appKey, + useTokenAuth: true, + authCallback: authCallback, + authMethod: 'GET', + ); + final refreshedToken = await realtime.auth.authorize( + authOptions: authOptions, + tokenParams: tokenParams, + ); //tokens must be different if (refreshedToken == firstToken) { throw Error(); diff --git a/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart b/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart index dee384131..90f91cd0a 100644 --- a/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart @@ -1,9 +1,9 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimeEncryptedPublish({ required Reporter reporter, @@ -12,15 +12,16 @@ Future> testRealtimeEncryptedPublish({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final cipherParams = - await Crypto.getDefaultParams(key: TestConstants.encryptedChannelKey); + final cipherParams = await Crypto.getDefaultParams( + key: TestConstants.encryptedChannelKey, + ); final channelOptions = RealtimeChannelOptions(cipherParams: cipherParams); - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', ), ); @@ -29,9 +30,7 @@ Future> testRealtimeEncryptedPublish({ await channel.setOptions(channelOptions); await publishMessages(channel); - return { - 'handle': await realtime.handle, - }; + return {'handle': await realtime.handle}; } Future> testRealtimeEncryptedPublishSpec({ @@ -41,16 +40,17 @@ Future> testRealtimeEncryptedPublishSpec({ const clientId = 'clientId'; final appKey = await AppProvisioning().provisionApp(); - final cipherParams = - await Crypto.getDefaultParams(key: TestConstants.encryptedChannelKey); + final cipherParams = await Crypto.getDefaultParams( + key: TestConstants.encryptedChannelKey, + ); final channelOptions = RealtimeChannelOptions(cipherParams: cipherParams); - // Realtime instance where client id is specified in the instance itself - final realtimeWithClientId = Realtime( + // PubSubClient instance where client id is specified in the instance itself + final realtimeWithClientId = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: clientId, logLevel: LogLevel.error, ), @@ -69,18 +69,17 @@ Future> testRealtimeEncryptedPublishSpec({ // Send single message object await encryptedChannel.publish( - message: Message( - name: 'single-message-name', - data: 'single-message-data', - ), + message: Message(name: 'single-message-name', data: 'single-message-data'), ); await Future.delayed(TestConstants.publishToHistoryDelay); // Send multiple message objects at once - await encryptedChannel.publish(messages: [ - Message(name: 'multi-message-name-1', data: 'multi-message-data-1'), - Message(name: 'multi-message-name-2', data: 'multi-message-data-2'), - ]); + await encryptedChannel.publish( + messages: [ + Message(name: 'multi-message-name-1', data: 'multi-message-data-1'), + Message(name: 'multi-message-name-2', data: 'multi-message-data-2'), + ], + ); await Future.delayed(TestConstants.publishToHistoryDelay); // Send message with [clientId] defined @@ -100,8 +99,9 @@ Future> testRealtimeEncryptedPublishSpec({ ); // Create encrypted channel with push capability - final encryptedPushEnabledChannel = - realtimeWithClientId.channels.get('pushenabled:test:extras'); + final encryptedPushEnabledChannel = realtimeWithClientId.channels.get( + 'pushenabled:test:extras', + ); await encryptedPushEnabledChannel.setOptions(channelOptions); // Send message with extras to encrypted push-enabled channel @@ -114,8 +114,9 @@ Future> testRealtimeEncryptedPublishSpec({ ); // Retrieve history of push-enabled channels - final historyOfEncryptedPushEnabledChannel = - await getHistory(encryptedPushEnabledChannel); + final historyOfEncryptedPushEnabledChannel = await getHistory( + encryptedPushEnabledChannel, + ); // Retreive plaintext history of encrypted channel await encryptedChannel.setOptions(const RealtimeChannelOptions()); @@ -124,8 +125,9 @@ Future> testRealtimeEncryptedPublishSpec({ RealtimeHistoryParams(direction: 'forwards'), ); await encryptedPushEnabledChannel.setOptions(const RealtimeChannelOptions()); - final historyOfPlaintextPushEnabledChannel = - await getHistory(encryptedPushEnabledChannel); + final historyOfPlaintextPushEnabledChannel = await getHistory( + encryptedPushEnabledChannel, + ); return { 'handle': await realtimeWithClientId.handle, diff --git a/test_integration/lib/test/realtime/realtime_events_test.dart b/test_integration/lib/test/realtime/realtime_events_test.dart index c98921adb..05282608c 100644 --- a/test_integration/lib/test/realtime/realtime_events_test.dart +++ b/test_integration/lib/test/realtime/realtime_events_test.dart @@ -1,8 +1,8 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimeEvents({ required Reporter reporter, @@ -18,10 +18,10 @@ Future> testRealtimeEvents({ final channelStateChanges = >[]; final filteredChannelStateChanges = >[]; - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', autoConnect: false, ), @@ -31,11 +31,14 @@ Future> testRealtimeEvents({ connectionStates.add(enumValueToString(realtime.connection.state)); recordConnectionState(); //connection: initialized + realtime.connection.on().listen( + (e) => connectionStateChanges.add(encodeConnectionEvent(e)), + ); realtime.connection - .on() - .listen((e) => connectionStateChanges.add(encodeConnectionEvent(e))); - realtime.connection.on(ConnectionEvent.connected).listen( - (e) => filteredConnectionStateChanges.add(encodeConnectionEvent(e))); + .on(ConnectionEvent.connected) + .listen( + (e) => filteredConnectionStateChanges.add(encodeConnectionEvent(e)), + ); reporter.reportLog({'before realtime.connect': ''}); recordConnectionState(); //connection: initialized diff --git a/test_integration/lib/test/realtime/realtime_history_test.dart b/test_integration/lib/test/realtime/realtime_history_test.dart index f514a52b9..bcc222ce2 100644 --- a/test_integration/lib/test/realtime/realtime_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_history_test.dart @@ -1,9 +1,9 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimeHistory({ required Reporter reporter, @@ -12,10 +12,10 @@ Future> testRealtimeHistory({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), diff --git a/test_integration/lib/test/realtime/realtime_history_with_auth_callback_test.dart b/test_integration/lib/test/realtime/realtime_history_with_auth_callback_test.dart index 4bfe35013..a81679073 100644 --- a/test_integration/lib/test/realtime/realtime_history_with_auth_callback_test.dart +++ b/test_integration/lib/test/realtime/realtime_history_with_auth_callback_test.dart @@ -1,8 +1,8 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimeHistoryWithAuthCallback({ required Reporter reporter, @@ -10,15 +10,13 @@ Future> testRealtimeHistoryWithAuthCallback({ }) async { var authCallbackInvoked = false; - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( clientId: 'someClientId', logLevel: LogLevel.error, authCallback: (params) async { authCallbackInvoked = true; - return TokenRequest.fromMap( - await AppProvisioning().getTokenRequest(), - ); + return TokenRequest.fromMap(await AppProvisioning().getTokenRequest()); }, ), ); diff --git a/test_integration/lib/test/realtime/realtime_presence_enter_update_leave.dart b/test_integration/lib/test/realtime/realtime_presence_enter_update_leave.dart index 94b8699cf..7f7a8a03e 100644 --- a/test_integration/lib/test/realtime/realtime_presence_enter_update_leave.dart +++ b/test_integration/lib/test/realtime/realtime_presence_enter_update_leave.dart @@ -1,21 +1,20 @@ // ignore_for_file: avoid_catching_errors -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; final logMessages = >[]; ClientOptions getClientOptions( String appKey, [ String? clientId = 'someClientId', -]) => - ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: clientId, - logLevel: LogLevel.error, - ); +]) => ClientOptions( + key: appKey, + endpoint: 'nonprod:sandbox', + clientId: clientId, + logLevel: LogLevel.error, +); Future> testRealtimePresenceEnterUpdateLeave({ required Reporter reporter, @@ -27,10 +26,9 @@ Future> testRealtimePresenceEnterUpdateLeave({ final clientIds = [null, 'client-1', 'client-2']; final clientIDClashMatrix = >[]; for (final clientId in clientIds) { - final presence = Realtime(options: getClientOptions(appKey, clientId)) - .channels - .get('test') - .presence; + final presence = createClient( + options: getClientOptions(appKey, clientId), + ).channels.get('test').presence; for (final clientId2 in clientIds) { final result = { 'realtimeClientId': clientId, @@ -102,11 +100,9 @@ Future> testRealtimePresenceEnterUpdateLeave({ // Interact with different types of data. final actionMatrix = >[]; - final realtimePresence = - Realtime(options: getClientOptions(appKey, 'test-client')) - .channels - .get('test') - .presence; + final realtimePresence = createClient( + options: getClientOptions(appKey, 'test-client'), + ).channels.get('test').presence; for (final message in messagesToPublish) { final action = {'data': message}; try { diff --git a/test_integration/lib/test/realtime/realtime_presence_get.dart b/test_integration/lib/test/realtime/realtime_presence_get.dart index 95adb8cd7..e3881d446 100644 --- a/test_integration/lib/test/realtime/realtime_presence_get.dart +++ b/test_integration/lib/test/realtime/realtime_presence_get.dart @@ -1,22 +1,21 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; final logMessages = >[]; ClientOptions getClientOptions( String appKey, [ String clientId = 'someClientId', -]) => - ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: clientId, - logLevel: LogLevel.error, - ); +]) => ClientOptions( + key: appKey, + endpoint: 'nonprod:sandbox', + clientId: clientId, + logLevel: LogLevel.error, +); Future> testRealtimePresenceGet({ required Reporter reporter, @@ -25,13 +24,13 @@ Future> testRealtimePresenceGet({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime(options: getClientOptions(appKey)); + final realtime = createClient(options: getClientOptions(appKey)); final channel = realtime.channels.get('test'); final membersInitial = await getPresenceMembers(channel); // enter multiple clients for (var i = 0; i < messagesToPublish.length; i++) { - await Realtime( + await createClient( options: getClientOptions(appKey, 'client-$i'), ).channels.get('test').presence.enter(messagesToPublish[i][1]); } diff --git a/test_integration/lib/test/realtime/realtime_presence_history_test.dart b/test_integration/lib/test/realtime/realtime_presence_history_test.dart index 530a6d9f4..bf99eec88 100644 --- a/test_integration/lib/test/realtime/realtime_presence_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_presence_history_test.dart @@ -1,9 +1,9 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimePresenceHistory({ required Reporter reporter, @@ -15,12 +15,12 @@ Future> testRealtimePresenceHistory({ final options = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ); - final realtime = Realtime(options: options); + final realtime = createClient(options: options); final channel = realtime.channels.get('test'); final historyInitial = await getPresenceHistory(channel); diff --git a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart index c8842bffa..052b498a5 100644 --- a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart @@ -1,11 +1,11 @@ import 'dart:async'; -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; final logMessages = >[]; @@ -18,10 +18,10 @@ Future> testRealtimePresenceSubscribe({ }) async { reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final presence = Realtime( + final presence = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), @@ -36,14 +36,14 @@ Future> testRealtimePresenceSubscribe({ .listen(enterMessages.add); final enterUpdateMessages = []; - final enterUpdateMessagesSubscription = presence.subscribe(actions: [ - PresenceAction.enter, - PresenceAction.update - ]).listen(enterUpdateMessages.add); + final enterUpdateMessagesSubscription = presence + .subscribe(actions: [PresenceAction.enter, PresenceAction.update]) + .listen(enterUpdateMessages.add); final partialMessages = []; - final partialMessagesSubscription = - presence.subscribe().listen(partialMessages.add); + final partialMessagesSubscription = presence.subscribe().listen( + partialMessages.add, + ); // enter-update-leave sequence with different data types to check if // data received by listeners is intact diff --git a/test_integration/lib/test/realtime/realtime_publish_test.dart b/test_integration/lib/test/realtime/realtime_publish_test.dart index 14ed51ea7..c88ce5abd 100644 --- a/test_integration/lib/test/realtime/realtime_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_publish_test.dart @@ -1,10 +1,10 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/config/test_constants.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimePublish({ Reporter? reporter, @@ -13,20 +13,17 @@ Future> testRealtimePublish({ final appKey = await AppProvisioning().provisionApp(); final logMessages = >[]; - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), ); await publishMessages(realtime.channels.get('test')); await realtime.close(); - return { - 'handle': await realtime.handle, - 'log': logMessages, - }; + return {'handle': await realtime.handle, 'log': logMessages}; } Future> testRealtimePublishSpec({ @@ -35,10 +32,10 @@ Future> testRealtimePublishSpec({ }) async { final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), @@ -53,10 +50,12 @@ Future> testRealtimePublishSpec({ message: Message(name: 'message-name1', data: 'message-data1'), ); await Future.delayed(TestConstants.publishToHistoryDelay); - await channel.publish(messages: [ - Message(name: 'messages-name1', data: 'messages-data1'), - Message(name: 'messages-name2', data: 'messages-data2'), - ]); + await channel.publish( + messages: [ + Message(name: 'messages-name1', data: 'messages-data1'), + Message(name: 'messages-name2', data: 'messages-data2'), + ], + ); await Future.delayed(TestConstants.publishToHistoryDelay); await channel.publish( message: Message( @@ -71,7 +70,8 @@ Future> testRealtimePublishSpec({ Map? exception; try { await channel.publish( - message: Message(name: 'name', clientId: 'client-id')); + message: Message(name: 'name', clientId: 'client-id'), + ); } on AblyException catch (e) { exception = encodeAblyException(e); } @@ -81,11 +81,8 @@ Future> testRealtimePublishSpec({ ); // client options - no client id, message has client id - final realtime2 = Realtime( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - ), + final realtime2 = createClient( + options: ClientOptions(key: appKey, endpoint: 'nonprod:sandbox'), ); final channel2 = realtime2.channels.get('test2'); diff --git a/test_integration/lib/test/realtime/realtime_publish_with_auth_callback_test.dart b/test_integration/lib/test/realtime/realtime_publish_with_auth_callback_test.dart index 669994bbe..ab3596833 100644 --- a/test_integration/lib/test/realtime/realtime_publish_with_auth_callback_test.dart +++ b/test_integration/lib/test/realtime/realtime_publish_with_auth_callback_test.dart @@ -1,21 +1,19 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimePublishWithAuthCallback({ required Reporter reporter, Map? payload, }) async { var authCallbackInvoked = false; - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( logLevel: LogLevel.error, authCallback: (params) async { authCallbackInvoked = true; - return TokenRequest.fromMap( - await AppProvisioning().getTokenRequest(), - ); + return TokenRequest.fromMap(await AppProvisioning().getTokenRequest()); }, ), ); diff --git a/test_integration/lib/test/realtime/realtime_subscribe.dart b/test_integration/lib/test/realtime/realtime_subscribe.dart index d81f7d1e4..99b0e6d50 100644 --- a/test_integration/lib/test/realtime/realtime_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_subscribe.dart @@ -1,15 +1,15 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; import 'package:ably_flutter_integration_test/utils/realtime.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; -Future _createRealtime(String apiKey) async { - final realtime = Realtime( +Future _createRealtime(String apiKey) async { + final realtime = createClient( options: ClientOptions( key: apiKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', autoConnect: false, ), @@ -31,8 +31,8 @@ Future>> _getAllMessages( final subscription = channel .subscribe(name: messageName, names: messageNames) .listen((message) { - messages.add(encodeMessage(message)); - }); + messages.add(encodeMessage(message)); + }); await publishMessages(channel); await subscription.cancel(); return messages; diff --git a/test_integration/lib/test/realtime/realtime_time_test.dart b/test_integration/lib/test/realtime/realtime_time_test.dart index 873e68f53..3c8e26d85 100644 --- a/test_integration/lib/test/realtime/realtime_time_test.dart +++ b/test_integration/lib/test/realtime/realtime_time_test.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/app_provisioning.dart'; import 'package:ably_flutter_integration_test/factory/reporter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future> testRealtimeTime({ required Reporter reporter, @@ -9,10 +9,10 @@ Future> testRealtimeTime({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime( + final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), diff --git a/test_integration/lib/test/rest/rest_auth_client_id_test.dart b/test_integration/lib/test/rest/rest_auth_client_id_test.dart deleted file mode 100644 index 3cfcafea8..000000000 --- a/test_integration/lib/test/rest/rest_auth_client_id_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; - -Future> testRestAuthClientId({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - //init ably for token - final clientOptionsForToken = ClientOptions( - key: appKey, - environment: 'sandbox', - logLevel: LogLevel.error, - ); - - final ablyForToken = Rest( - options: clientOptionsForToken, - ); - /* get first token */ - final firstToken = await ablyForToken.auth.requestToken(); - - final clientOptions = ClientOptions( - key: appKey, - environment: 'sandbox', - useTokenAuth: true, - autoConnect: false, - clientId: 'testClientId', - tokenDetails: firstToken); - final rest = Rest(options: clientOptions); - - final clientId = await rest.auth.clientId; - if (clientId != 'testClientId') { - throw Error(); - } - return {'handle': await rest.handle}; -} diff --git a/test_integration/lib/test/rest/rest_authorize_test.dart b/test_integration/lib/test/rest/rest_authorize_test.dart deleted file mode 100644 index 44621ff21..000000000 --- a/test_integration/lib/test/rest/rest_authorize_test.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; - -Future> testRestAuthorize({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - //init ably for token - final clientOptionsForToken = ClientOptions( - key: appKey, - environment: 'sandbox', - logLevel: LogLevel.error, - ); - - final ablyForToken = Rest( - options: clientOptionsForToken, - ); - /* get first token */ - final firstToken = await ablyForToken.auth.requestToken(); - - final clientOptions = ClientOptions( - key: appKey, - environment: 'sandbox', - useTokenAuth: true, - autoConnect: false, - tokenDetails: firstToken); - final rest = Rest(options: clientOptions); - - const tokenParams = TokenParams(ttl: 20000); - Future authCallback(params) async => TokenRequest.fromMap( - await AppProvisioning().getTokenRequest(), - ); - final authOptions = AuthOptions( - key: appKey, - useTokenAuth: true, - authCallback: authCallback, - authMethod: 'GET'); - final refreshedToken = await rest.auth - .authorize(authOptions: authOptions, tokenParams: tokenParams); - //tokens must be different - if (refreshedToken == firstToken) { - throw Error(); - } - return {'handle': await rest.handle}; -} diff --git a/test_integration/lib/test/rest/rest_capability_test.dart b/test_integration/lib/test/rest/rest_capability_test.dart deleted file mode 100644 index cb4a49097..000000000 --- a/test_integration/lib/test/rest/rest_capability_test.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/data.dart'; -import 'package:ably_flutter_integration_test/utils/encoders.dart'; - -Future> testRestCapabilities({ - required Reporter reporter, - Map? payload, -}) async { - final capabilitySpec = >{}; - final combinations = getAllSubsets(['publish', 'history', 'subscribe']) - .where((spec) => spec.isNotEmpty) - .toList(); - - for (var i = 0; i < combinations.length; i++) { - capabilitySpec['channel-$i'] = combinations[i]; - } - final appKey = - await AppProvisioning(keyCapabilities: capabilitySpec).provisionApp(); - - final rest = Rest( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: 'someClientId', - logLevel: LogLevel.error, - ), - ); - - final matrix = >[]; - for (final entry in capabilitySpec.entries) { - Map? publishException; - Map? historyException; - Map? presenceException; - Map? presenceHistoryException; - - final channel = rest.channels.get(entry.key); - try { - await channel.publish(); - } on AblyException catch (exception) { - publishException = encodeAblyException(exception); - } - try { - await channel.history(); - } on AblyException catch (exception) { - historyException = encodeAblyException(exception); - } - try { - await channel.presence.get(); - } on AblyException catch (exception) { - presenceException = encodeAblyException(exception); - } - try { - await channel.presence.history(); - } on AblyException catch (exception) { - presenceHistoryException = encodeAblyException(exception); - } - matrix.add({ - 'channelCapabilities': entry.value, - 'publishException': publishException, - 'historyException': historyException, - 'presenceException': presenceException, - 'presenceHistoryException': presenceHistoryException, - }); - } - - return {'matrix': matrix}; -} diff --git a/test_integration/lib/test/rest/rest_create_token_request_test.dart b/test_integration/lib/test/rest/rest_create_token_request_test.dart deleted file mode 100644 index 38d106b8d..000000000 --- a/test_integration/lib/test/rest/rest_create_token_request_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; - -Future> testRestCreateTokenRequest({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - final clientOptionsForToken = ClientOptions( - key: appKey, - environment: 'sandbox', - logLevel: LogLevel.error, - ); - - final rest = Rest( - options: clientOptionsForToken, - ); - const tokenParams = TokenParams( - ttl: 20000, - capability: '{"testchannel":["publish"]}', - clientId: 'testclient'); - - final authOptions = AuthOptions( - key: appKey, - useTokenAuth: true, - authMethod: 'GET', - authHeaders: {'heardkey': 'headervalue'}, - authUrl: 'bogus', - authParams: {'paramkey': 'paramvalue'}); - final request = await rest.auth - .createTokenRequest(authOptions: authOptions, tokenParams: tokenParams); - if (request.ttl != tokenParams.ttl || - request.clientId != tokenParams.clientId || - request.capability != tokenParams.capability) { - throw Error(); - } - - return { - 'handle': await rest.handle, - }; -} diff --git a/test_integration/lib/test/rest/rest_encrypted_publish_test.dart b/test_integration/lib/test/rest/rest_encrypted_publish_test.dart deleted file mode 100644 index 44500d32e..000000000 --- a/test_integration/lib/test/rest/rest_encrypted_publish_test.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/config/test_constants.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/data.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -Future> testRestEncryptedPublish({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - final cipherParams = - await Crypto.getDefaultParams(key: TestConstants.encryptedChannelKey); - - final channelOptions = RestChannelOptions(cipherParams: cipherParams); - - final rest = Rest( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: 'someClientId', - logLevel: LogLevel.error, - ), - ); - - final channel = rest.channels.get('test'); - await channel.setOptions(channelOptions); - - await publishMessages(channel); - return { - 'handle': await rest.handle, - }; -} - -Future> testRestEncryptedPublishSpec({ - required Reporter reporter, - Map? payload, -}) async { - const clientId = 'clientId'; - final appKey = await AppProvisioning().provisionApp(); - - final cipherParams = - await Crypto.getDefaultParams(key: TestConstants.encryptedChannelKey); - - final channelOptions = RestChannelOptions(cipherParams: cipherParams); - - // Rest instance where client id is specified in the instance itself - final restWithClientId = Rest( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: clientId, - logLevel: LogLevel.error, - ), - ); - - // Create encrypted channel with client ID - final encryptedChannel = restWithClientId.channels.get('test'); - await encryptedChannel.setOptions(channelOptions); - - // Send simple message data - await encryptedChannel.publish(); - await encryptedChannel.publish(name: 'name'); - await encryptedChannel.publish(data: 'data'); - await encryptedChannel.publish(name: 'name', data: 'data'); - await Future.delayed(TestConstants.publishToHistoryDelay); - - // Send single message object - await encryptedChannel.publish( - message: Message( - name: 'single-message-name', - data: 'single-message-data', - ), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - // Send multiple message objects at once - await encryptedChannel.publish(messages: [ - Message(name: 'multi-message-name-1', data: 'multi-message-data-1'), - Message(name: 'multi-message-name-2', data: 'multi-message-data-2'), - ]); - await Future.delayed(TestConstants.publishToHistoryDelay); - - // Send message with [clientId] defined - await encryptedChannel.publish( - message: Message( - name: 'single-message-with-client-id-name', - data: 'single-message-with-client-id-data', - clientId: clientId, - ), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - // Retrieve history of channel where client id was specified - final historyOfEncryptedChannel = await getHistory( - encryptedChannel, - RestHistoryParams(direction: 'forwards'), - ); - - // Create encrypted channel with push capability - final encryptedPushEnabledChannel = - restWithClientId.channels.get('pushenabled:test:extras'); - await encryptedPushEnabledChannel.setOptions(channelOptions); - - // Send message with extras to encrypted push-enabled channel - await encryptedPushEnabledChannel.publish( - message: Message( - name: 'single-message-push-enabled-name', - data: 'single-message-push-enabled-data', - extras: const MessageExtras({...pushPayload}), - ), - ); - - // Retrieve history of push-enabled channels - final historyOfEncryptedPushEnabledChannel = - await getHistory(encryptedPushEnabledChannel); - - // Retreive plaintext history of encrypted channel - await encryptedChannel.setOptions(RestChannelOptions()); - final historyOfPlaintextChannel = await getHistory( - encryptedChannel, - RestHistoryParams(direction: 'forwards'), - ); - await encryptedPushEnabledChannel.setOptions(RestChannelOptions()); - final historyOfPlaintextPushEnabledChannel = - await getHistory(encryptedPushEnabledChannel); - - return { - 'handle': await restWithClientId.handle, - 'historyOfEncryptedChannel': historyOfEncryptedChannel, - 'historyOfPlaintextChannel': historyOfPlaintextChannel, - 'historyOfEncryptedPushEnabledChannel': - historyOfEncryptedPushEnabledChannel, - 'historyOfPlaintextPushEnabledChannel': - historyOfPlaintextPushEnabledChannel, - }; -} diff --git a/test_integration/lib/test/rest/rest_history_test.dart b/test_integration/lib/test/rest/rest_history_test.dart deleted file mode 100644 index b84c0511c..000000000 --- a/test_integration/lib/test/rest/rest_history_test.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/config/test_constants.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/encoders.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -Future> testRestHistory({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - final rest = Rest( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: 'someClientId', - logLevel: LogLevel.error, - ), - ); - final channel = rest.channels.get('test'); - await publishMessages(channel); - - final paginatedResult = await channel.history(); - final historyDefault = await getHistory(channel); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyLimit4 = await getHistory(channel, RestHistoryParams(limit: 4)); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyLimit2 = await getHistory(channel, RestHistoryParams(limit: 2)); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyForwardLimit4 = await getHistory( - channel, RestHistoryParams(direction: 'forwards', limit: 4)); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final time1 = DateTime.now(); - //TODO(tiholic) iOS fails without this delay - // - timestamp on message retrieved from history - // is earlier than expected when ran in CI - await Future.delayed(TestConstants.publishToHistoryDelay); - await channel.publish(name: 'history', data: 'test'); - //TODO(tiholic) understand why tests fail without this delay - await Future.delayed(TestConstants.publishToHistoryDelay); - - final time2 = DateTime.now(); - await Future.delayed(TestConstants.publishToHistoryDelay); - await channel.publish(name: 'history', data: 'test2'); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyWithStart = - await getHistory(channel, RestHistoryParams(start: time1)); - final historyWithStartAndEnd = - await getHistory(channel, RestHistoryParams(start: time1, end: time2)); - final historyAll = await getHistory(channel); - return { - 'handle': await rest.handle, - 'paginatedResult': encodePaginatedResult(paginatedResult, encodeMessage), - 'historyDefault': historyDefault, - 'historyLimit4': historyLimit4, - 'historyLimit2': historyLimit2, - 'historyForwardLimit4': historyForwardLimit4, - 'historyWithStart': historyWithStart, - 'historyWithStartAndEnd': historyWithStartAndEnd, - 'historyAll': historyAll, - 't1': time1.toIso8601String(), - 't2': time2.toIso8601String(), - }; -} diff --git a/test_integration/lib/test/rest/rest_history_with_auth_callback_test.dart b/test_integration/lib/test/rest/rest_history_with_auth_callback_test.dart deleted file mode 100644 index c5bed7103..000000000 --- a/test_integration/lib/test/rest/rest_history_with_auth_callback_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/config/test_constants.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -Future> testRestHistoryWithAuthCallback({ - required Reporter reporter, - Map? payload, -}) async { - var authCallbackInvoked = false; - - final rest = Rest( - options: ClientOptions( - clientId: 'someClientId', - logLevel: LogLevel.error, - authCallback: (params) async { - authCallbackInvoked = true; - return TokenRequest.fromMap( - await AppProvisioning().getTokenRequest(), - ); - }, - ), - ); - - final channel = rest.channels.get('test'); - await publishMessages(channel); - - await channel.history(); - await Future.delayed(TestConstants.publishToHistoryDelay); - - return { - 'handle': await rest.handle, - 'authCallbackInvoked': authCallbackInvoked - }; -} diff --git a/test_integration/lib/test/rest/rest_presence_get_test.dart b/test_integration/lib/test/rest/rest_presence_get_test.dart deleted file mode 100644 index f9ce698fe..000000000 --- a/test_integration/lib/test/rest/rest_presence_get_test.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/config/test_constants.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/data.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -final logMessages = >[]; - -ClientOptions getClientOptions( - String appKey, [ - String clientId = 'someClientId', -]) => - ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: clientId, - logLevel: LogLevel.error, - ); - -Future> testRestPresenceGet({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - final rest = Rest(options: getClientOptions(appKey)); - final channel = rest.channels.get('test'); - final membersInitial = await getPresenceMembers(channel); - - // enter multiple clients - for (var i = 0; i < messagesToPublish.length; i++) { - await Realtime( - options: getClientOptions(appKey, 'client-$i'), - ).channels.get('test').presence.enter(messagesToPublish[i][1]); - } - - await Future.delayed(TestConstants.publishToHistoryDelay); - - final membersDefault = await getPresenceMembers(channel); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final membersLimit4 = await getPresenceMembers( - channel, - RestPresenceParams(limit: 4), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final membersLimit2 = await getPresenceMembers( - channel, - RestPresenceParams(limit: 2), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final membersClientId = await getPresenceMembers( - channel, - RestPresenceParams(clientId: 'client-1'), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - // TODO(tiholic) extract connection ID from realtime instance - // after implementing `id` update on connection object from platform - // Until then, `membersConnectionId` will be empty list - final membersConnectionId = await getPresenceMembers( - channel, - RestPresenceParams(connectionId: 'connection-1'), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - return { - 'handle': await rest.handle, - 'membersInitial': membersInitial, - 'membersDefault': membersDefault, - 'membersLimit4': membersLimit4, - 'membersLimit2': membersLimit2, - 'membersClientId': membersClientId, - 'membersConnectionId': membersConnectionId, - 'log': logMessages, - }; -} diff --git a/test_integration/lib/test/rest/rest_presence_history_test.dart b/test_integration/lib/test/rest/rest_presence_history_test.dart deleted file mode 100644 index 41b8895c3..000000000 --- a/test_integration/lib/test/rest/rest_presence_history_test.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/config/test_constants.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/data.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -Future> testRestPresenceHistory({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - final logMessages = >[]; - - final options = ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: 'someClientId', - logLevel: LogLevel.error, - ); - - final rest = Rest(options: options); - final channel = rest.channels.get('test'); - - final historyInitial = await getPresenceHistory(channel); - - // creating presence history on channel - final realtimePresence = - Realtime(options: options).channels.get('test').presence; - // single client enters channel - await realtimePresence.enter(messagesToPublish.first[1]); - // updates, multiple times with different messages - for (var i = 1; i < messagesToPublish.length - 1; i++) { - await realtimePresence.update(messagesToPublish[i][1]); - } - // leaves channel - await realtimePresence.leave(messagesToPublish.last[1]); - - final historyDefault = await getPresenceHistory(channel); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyLimit4 = await getPresenceHistory( - channel, - RestHistoryParams(limit: 4), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyLimit2 = await getPresenceHistory( - channel, - RestHistoryParams(limit: 2), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyForwards = await getPresenceHistory( - channel, - RestHistoryParams(direction: 'forwards'), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final time1 = DateTime.now(); - //TODO(tiholic) iOS fails without this delay - // - timestamp on message retrieved from history - // is earlier than expected when ran in CI - await Future.delayed(TestConstants.publishToHistoryDelay); - await realtimePresence.enter('enter-start-time'); - // TODO(tiholic) understand why tests fail without this delay - await Future.delayed(TestConstants.publishToHistoryDelay); - - final time2 = DateTime.now(); - await Future.delayed(TestConstants.publishToHistoryDelay); - await realtimePresence.leave('leave-end-time'); - await Future.delayed(TestConstants.publishToHistoryDelay); - - final historyWithStart = await getPresenceHistory( - channel, - RestHistoryParams(start: time1), - ); - final historyWithStartAndEnd = await getPresenceHistory( - channel, - RestHistoryParams(start: time1, end: time2), - ); - final historyAll = await getPresenceHistory(channel); - - return { - 'handle': await rest.handle, - 'historyInitial': historyInitial, - 'historyDefault': historyDefault, - 'historyLimit4': historyLimit4, - 'historyLimit2': historyLimit2, - 'historyForwards': historyForwards, - 'historyWithStart': historyWithStart, - 'historyWithStartAndEnd': historyWithStartAndEnd, - 'historyAll': historyAll, - 'log': logMessages, - }; -} diff --git a/test_integration/lib/test/rest/rest_publish_test.dart b/test_integration/lib/test/rest/rest_publish_test.dart deleted file mode 100644 index ef2cb78c9..000000000 --- a/test_integration/lib/test/rest/rest_publish_test.dart +++ /dev/null @@ -1,143 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/config/test_constants.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/data.dart'; -import 'package:ably_flutter_integration_test/utils/encoders.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -Future> testRestPublish({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - final logMessages = >[]; - - final rest = Rest( - options: ClientOptions( - key: appKey.toString(), - environment: 'sandbox', - clientId: 'someClientId', - logLevel: LogLevel.error, - ), - ); - await publishMessages(rest.channels.get('test')); - return { - 'handle': await rest.handle, - 'log': logMessages, - }; -} - -Future> testRestPublishSpec({ - required Reporter reporter, - Map? payload, -}) async { - final appKey = await AppProvisioning().provisionApp(); - - final rest = Rest( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: 'someClientId', - logLevel: LogLevel.error, - ), - ); - final channel = rest.channels.get('test'); - await channel.publish(); - await channel.publish(name: 'name1'); - await channel.publish(data: 'data1'); - await channel.publish(name: 'name1', data: 'data1'); - await Future.delayed(TestConstants.publishToHistoryDelay); - await channel.publish( - message: Message(name: 'message-name1', data: 'message-data1'), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - await channel.publish(messages: [ - Message(name: 'messages-name1', data: 'messages-data1'), - Message(name: 'messages-name2', data: 'messages-data2'), - ]); - await Future.delayed(TestConstants.publishToHistoryDelay); - await channel.publish( - message: Message( - name: 'message-name1', - data: 'message-data1', - clientId: 'someClientId', - ), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - - // publishing message with a different client id - Map? exception; - try { - await channel.publish( - message: Message(name: 'name', clientId: 'client-id')); - } on AblyException catch (e) { - exception = encodeAblyException(e); - } - final history = await getHistory( - channel, - RestHistoryParams(direction: 'forwards'), - ); - - // client options - no client id, message has client id - final rest2 = Rest( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - ), - ); - - final channel2 = rest2.channels.get('test2'); - await channel2.publish( - message: Message(name: 'name-client-id', clientId: 'client-id'), - ); - await Future.delayed(TestConstants.publishToHistoryDelay); - final history2 = await getHistory( - channel2, - RestHistoryParams(direction: 'forwards'), - ); - - // publish max allowed length - sandbox apps message limit is 16384 - Map? exception2; - try { - await channel2.publish(data: getRandomString(16384)); - } on AblyException catch (e) { - exception2 = encodeAblyException(e); - } - - // publish more than max allowed length - Map? exception3; - try { - await channel2.publish(data: getRandomString(16384 + 1)); - } on AblyException catch (e) { - exception3 = encodeAblyException(e); - } - - final channel3 = rest2.channels.get('©Äblý'); - await channel3.publish(name: 'Ωπ', data: 'ΨΔ'); - - await Future.delayed(TestConstants.publishToHistoryDelay); - final history3 = await getHistory(channel3); - - final channelExtras = rest.channels.get('pushenabled:test:extras'); - await channelExtras.publish( - message: Message( - name: 'name', - data: 'data', - extras: const MessageExtras({...pushPayload}), - ), - ); - final historyExtras = await getHistory(channelExtras); - - return { - 'handle': await rest.handle, - 'publishedMessages': history, - 'publishedMessages2': history2, - 'publishedMessages3': history3, - 'publishedExtras': historyExtras, - 'exception': exception, - 'exception2': exception2, - 'exception3': exception3, - }; -} diff --git a/test_integration/lib/test/rest/rest_publish_with_auth_callback_test.dart b/test_integration/lib/test/rest/rest_publish_with_auth_callback_test.dart deleted file mode 100644 index 5ad52f173..000000000 --- a/test_integration/lib/test/rest/rest_publish_with_auth_callback_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -Future> testRestPublishWithAuthCallback({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - var authCallbackInvoked = false; - final rest = Rest( - options: ClientOptions( - clientId: 'someClientId', - logLevel: LogLevel.error, - authCallback: (params) async { - authCallbackInvoked = true; - return TokenRequest.fromMap( - await AppProvisioning().getTokenRequest(), - ); - }, - ), - ); - await publishMessages(rest.channels.get('test')); - return { - 'handle': await rest.handle, - 'authCallbackInvoked': authCallbackInvoked - }; -} diff --git a/test_integration/lib/test/rest/rest_request_token_test.dart b/test_integration/lib/test/rest/rest_request_token_test.dart deleted file mode 100644 index b6c804efd..000000000 --- a/test_integration/lib/test/rest/rest_request_token_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; -import 'package:ably_flutter_integration_test/utils/rest.dart'; - -Future> testRestRequestToken({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - //init ably for token - final clientOptionsForToken = ClientOptions( - key: appKey, - environment: 'sandbox', - logLevel: LogLevel.error, - ); - - final restForToken = Rest( - options: clientOptionsForToken, - ); - - final token = await restForToken.auth.requestToken(); - - final clientOptions = ClientOptions( - tokenDetails: token, environment: 'sandbox', logLevel: LogLevel.error); - final tokenedRest = Rest(options: clientOptions); - - await publishMessages(tokenedRest.channels.get('test')); - - return {'handle': await tokenedRest.handle}; -} diff --git a/test_integration/lib/test/rest/rest_time_test.dart b/test_integration/lib/test/rest/rest_time_test.dart deleted file mode 100644 index 4d840dddb..000000000 --- a/test_integration/lib/test/rest/rest_time_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/app_provisioning.dart'; -import 'package:ably_flutter_integration_test/factory/reporter.dart'; - -Future> testRestTime({ - required Reporter reporter, - Map? payload, -}) async { - reporter.reportLog('init start'); - final appKey = await AppProvisioning().provisionApp(); - - final rest = Rest( - options: ClientOptions( - key: appKey, - environment: 'sandbox', - clientId: 'someClientId', - logLevel: LogLevel.error, - ), - ); - - final restTime = await rest.time(); - - return { - 'handle': await rest.handle, - 'time': restTime.toIso8601String(), - }; -} diff --git a/test_integration/lib/test_dispatcher.dart b/test_integration/lib/test_dispatcher.dart index c7f6f6c0b..196b560e0 100644 --- a/test_integration/lib/test_dispatcher.dart +++ b/test_integration/lib/test_dispatcher.dart @@ -31,9 +31,9 @@ class _TestDispatcherState extends State { late Map _testResults; /// stores whether a test is success/failed/pending - /// {'restPublish': true} => basic test passed, - /// {'restPublish': false} => failed - /// {} i.e., missing 'restPublish' key => test is still pending + /// {'realtimePublish': true} => basic test passed, + /// {'realtimePublish': false} => failed + /// {} i.e., missing 'realtimePublish' key => test is still pending final _testStatuses = {}; @override @@ -66,14 +66,18 @@ class _TestDispatcherState extends State { }); final testFunction = widget.testFactory[reporter.testName]!; await testFunction( - reporter: reporter, - payload: reporter.message.payload, - ).then(reporter.reportTestCompletion).catchError( + reporter: reporter, + payload: reporter.message.payload, + ) + .then(reporter.reportTestCompletion) + .catchError( (Object error, StackTrace stack) => reporter.reportTestCompletion({ - TestControlMessage.errorKey: - ErrorHandler.encodeException(error, stack), - }), + TestControlMessage.errorKey: ErrorHandler.encodeException( + error, + stack, + ), + }), ); } } else if (reporter.testName == TestName.getFlutterErrors) { @@ -83,7 +87,7 @@ class _TestDispatcherState extends State { // report error otherwise reporter.reportTestCompletion({ TestControlMessage.errorKey: - 'Test ${reporter.testName} is not implemented' + 'Test ${reporter.testName} is not implemented', }); } setState(() {}); @@ -151,67 +155,65 @@ class _TestDispatcherState extends State { } Widget getTestRow(BuildContext context, String testName) => Row( - children: [ - Expanded( - child: Text( - testName, - style: TextStyle(color: _getColor(testName), fontSize: 16), + children: [ + Expanded( + child: Text( + testName, + style: TextStyle(color: _getColor(testName), fontSize: 16), + ), + ), + _getAction(testName), + _getStatus(testName), + IconButton( + icon: const Icon(Icons.remove_red_eye), + color: Colors.white, + onPressed: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + contentPadding: const EdgeInsets.all(4), + insetPadding: const EdgeInsets.symmetric(vertical: 24), + content: SingleChildScrollView( + child: Text(_testResults[testName] ?? 'No result yet'), + ), ), - ), - _getAction(testName), - _getStatus(testName), - IconButton( - icon: const Icon(Icons.remove_red_eye), - color: Colors.white, - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - contentPadding: const EdgeInsets.all(4), - insetPadding: const EdgeInsets.symmetric(vertical: 24), - content: SingleChildScrollView( - child: Text( - _testResults[testName] ?? 'No result yet', - ), - ), - ), - ); - }, - ), - ], - ); + ); + }, + ), + ], + ); @override Widget build(BuildContext context) => MaterialApp( - theme: ThemeData(scaffoldBackgroundColor: Colors.black), - home: Scaffold( - appBar: AppBar( - title: const Text('Test dispatcher'), - ), - body: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Center( - child: Text( - _reporters.isEmpty - ? '-' - : 'running ${_reporters.length}' - ' (${_reporters.keys.toList().toString()}) tests', - style: const TextStyle(color: Colors.white)), + theme: ThemeData(scaffoldBackgroundColor: Colors.black), + home: Scaffold( + appBar: AppBar(title: const Text('Test dispatcher')), + body: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: Text( + _reporters.isEmpty + ? '-' + : 'running ${_reporters.length}' + ' (${_reporters.keys.toList().toString()}) tests', + style: const TextStyle(color: Colors.white), ), - Expanded( - child: ListView.builder( - itemCount: _testResults.keys.length, - itemBuilder: (context, idx) { - final testName = _testResults.keys.toList()[idx]; - return ListTile(subtitle: getTestRow(context, testName)); - }, - ), + ), + Expanded( + child: ListView.builder( + itemCount: _testResults.keys.length, + itemBuilder: (context, idx) { + final testName = _testResults.keys.toList()[idx]; + return ListTile(subtitle: getTestRow(context, testName)); + }, ), - ], - ), - )); + ), + ], + ), + ), + ); void renderResponse(TestControlResponseMessage message) { final testName = message.testName; @@ -220,8 +222,8 @@ class _TestDispatcherState extends State { _reporters.remove(testName); _testStatuses[testName] = message.payload.containsKey(TestControlMessage.errorKey) - ? _TestStatus.error - : _TestStatus.success; + ? _TestStatus.error + : _TestStatus.success; }); } } diff --git a/test_integration/lib/utils/data.dart b/test_integration/lib/utils/data.dart index 62b44b6fa..4f2b1fdd5 100644 --- a/test_integration/lib/utils/data.dart +++ b/test_integration/lib/utils/data.dart @@ -7,27 +7,27 @@ final messagesToPublish = [ ['name1', 'Ably'], //message is a string [ 'name2', - [1, 2, 3] + [1, 2, 3], ], //message is a numeric list [ 'name2', - ['hello', 'ably'] + ['hello', 'ably'], ], //message is a string list [ 'name3', { 'hello': 'ably', - 'items': ['1', 2.2, true] - } + 'items': ['1', 2.2, true], + }, ], //message is a map [ 'name3', [ {'hello': 'ably'}, 'ably', - 'realtime' - ] - ] //message is a complex list + 'realtime', + ], + ], //message is a complex list ]; const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; @@ -35,21 +35,23 @@ Random _rnd = Random(); /// get random alpha numeric string of given length String getRandomString(int length) => String.fromCharCodes( - Iterable.generate( - length, - (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length)), - ), - ); + Iterable.generate( + length, + (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length)), + ), +); /// returns subsets of all entries in a list List> getAllSubsets(List l) => l.fold>>( [[]], (subLists, element) => subLists - .map((subList) => [ - subList, - subList + [element] - ]) + .map( + (subList) => [ + subList, + subList + [element], + ], + ) .expand((element) => element) .toList(), ); @@ -58,8 +60,8 @@ const pushPayload = { 'push': { 'notification': { 'title': 'Hello from Ably!', - 'body': 'Test push notification from Ably' + 'body': 'Test push notification from Ably', }, - 'data': {'foo': 'bar'} - } + 'data': {'foo': 'bar'}, + }, }; diff --git a/test_integration/lib/utils/encoders.dart b/test_integration/lib/utils/encoders.dart index 1cd7baddf..dc99f541f 100644 --- a/test_integration/lib/utils/encoders.dart +++ b/test_integration/lib/utils/encoders.dart @@ -1,84 +1,79 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Map? encodeMessageExtras(MessageExtras? extras) => extras == null - ? null - : { - 'extras': extras.map, - 'delta': (extras.delta == null) - ? null - : { - 'from': extras.delta!.from, - 'format': extras.delta!.format, - }, - }; + ? null + : { + 'extras': extras.map, + 'delta': (extras.delta == null) + ? null + : {'from': extras.delta!.from, 'format': extras.delta!.format}, + }; Map encodeMessage(Message message) => { - 'id': message.id, - 'timestamp': message.timestamp?.toIso8601String(), - 'clientId': message.clientId, - 'connectionId': message.connectionId, - 'encoding': message.encoding, - 'data': message.data, - 'name': message.name, - 'extras': encodeMessageExtras(message.extras), - }; + 'id': message.id, + 'timestamp': message.timestamp?.toIso8601String(), + 'clientId': message.clientId, + 'connectionId': message.connectionId, + 'encoding': message.encoding, + 'data': message.data, + 'name': message.name, + 'extras': encodeMessageExtras(message.extras), +}; Map encodePresenceMessage(PresenceMessage message) => { - 'id': message.id, - 'action': enumValueToString(message.action!), - 'clientId': message.clientId, - 'connectionId': message.connectionId, - 'data': message.data, - 'encoding': message.encoding, - 'extras': encodeMessageExtras(message.extras), - 'timestamp': message.timestamp?.toIso8601String(), - }; + 'id': message.id, + 'action': enumValueToString(message.action!), + 'clientId': message.clientId, + 'connectionId': message.connectionId, + 'data': message.data, + 'encoding': message.encoding, + 'extras': encodeMessageExtras(message.extras), + 'timestamp': message.timestamp?.toIso8601String(), +}; String enumValueToString(Object value) => value.toString().substring(value.toString().indexOf('.') + 1); Map encodeChannelEvent(ChannelStateChange e) => { - 'event': enumValueToString(e.event), - 'current': enumValueToString(e.current), - 'previous': enumValueToString(e.previous), - 'reason': e.reason.toString(), - 'resumed': e.resumed, - }; + 'event': enumValueToString(e.event), + 'current': enumValueToString(e.current), + 'previous': enumValueToString(e.previous), + 'reason': e.reason.toString(), + 'resumed': e.resumed, +}; Map encodeConnectionEvent(ConnectionStateChange e) => { - 'event': enumValueToString(e.event), - 'current': enumValueToString(e.current), - 'previous': enumValueToString(e.previous), - 'reason': e.reason.toString(), - 'retryIn': e.retryIn, - }; + 'event': enumValueToString(e.event), + 'current': enumValueToString(e.current), + 'previous': enumValueToString(e.previous), + 'reason': e.reason.toString(), + 'retryIn': e.retryIn, +}; List> encodeList( List items, Map Function(T) typeEncoder, -) => - List.from(items).map>(typeEncoder).toList(); +) => List.from(items).map>(typeEncoder).toList(); Map encodePaginatedResult( PaginatedResult paginatedResult, Map Function(T) typeEncoder, -) => - { - 'items': encodeList(paginatedResult.items, typeEncoder), - 'hasNext': paginatedResult.hasNext(), - 'isLast': paginatedResult.isLast(), - }; +) => { + 'items': encodeList(paginatedResult.items, typeEncoder), + 'hasNext': paginatedResult.hasNext(), + 'isLast': paginatedResult.isLast(), +}; Map encodeAblyException(AblyException exception) => { - 'code': exception.code, - 'message': exception.message, - 'errorInfo': { - 'code': exception.errorInfo?.code, - 'href': exception.errorInfo?.href, - 'message': exception.errorInfo?.message, - 'cause': exception.errorInfo?.cause, - 'statusCode': exception.errorInfo?.statusCode, - 'requestId': exception.errorInfo?.requestId, - } - }; + 'code': exception.code, + 'message': exception.message, + 'errorInfo': { + 'code': exception.errorInfo?.code, + 'href': exception.errorInfo?.href, + 'message': exception.errorInfo?.message, + 'cause': exception.errorInfo?.cause, + 'statusCode': exception.errorInfo?.statusCode, + 'requestId': exception.errorInfo?.requestId, + }, +}; diff --git a/test_integration/lib/utils/realtime.dart b/test_integration/lib/utils/realtime.dart index 5d591c595..acf56b7da 100644 --- a/test_integration/lib/utils/realtime.dart +++ b/test_integration/lib/utils/realtime.dart @@ -1,6 +1,6 @@ -import 'package:ably_flutter/ably_flutter.dart'; import 'package:ably_flutter_integration_test/utils/data.dart'; import 'package:ably_flutter_integration_test/utils/encoders.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Future publishMessages(RealtimeChannel channel) async { for (final data in messagesToPublish) { @@ -24,19 +24,20 @@ Future>> getHistory( Future>> getPresenceMembers( RealtimeChannel channel, [ RealtimePresenceParams? params, -]) async => - encodeList( - await channel.presence.get(params), - encodePresenceMessage, - ); +]) async => encodeList( + await channel.presence.get(params), + encodePresenceMessage, +); Future>> getPresenceHistory( RealtimeChannel channel, [ RealtimeHistoryParams? params, ]) async { var results = await channel.presence.history(params); - final messages = - encodeList(results.items, encodePresenceMessage); + final messages = encodeList( + results.items, + encodePresenceMessage, + ); while (results.hasNext()) { results = await results.next(); messages.addAll( diff --git a/test_integration/lib/utils/rest.dart b/test_integration/lib/utils/rest.dart deleted file mode 100644 index 0c14335fb..000000000 --- a/test_integration/lib/utils/rest.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:ably_flutter/ably_flutter.dart'; -import 'package:ably_flutter_integration_test/utils/data.dart'; -import 'package:ably_flutter_integration_test/utils/encoders.dart'; - -Future publishMessages(RestChannel channel) async { - for (final data in messagesToPublish) { - await channel.publish(name: data[0] as String?, data: data[1]); - } -} - -Future>> getHistory( - RestChannel channel, [ - RestHistoryParams? params, -]) async { - var results = await channel.history(params); - final messages = encodeList(results.items, encodeMessage); - while (results.hasNext()) { - results = await results.next(); - messages.addAll(encodeList(results.items, encodeMessage)); - } - return messages; -} - -Future>> getPresenceHistory( - RestChannel channel, [ - RestHistoryParams? params, -]) async { - var results = await channel.presence.history(params); - final messages = - encodeList(results.items, encodePresenceMessage); - while (results.hasNext()) { - results = await results.next(); - messages.addAll( - encodeList(results.items, encodePresenceMessage)); - } - return messages; -} - -Future>> getPresenceMembers( - RestChannel channel, [ - RestPresenceParams? params, -]) async { - var results = await channel.presence.get(params); - final messages = - encodeList(results.items, encodePresenceMessage); - while (results.hasNext()) { - results = await results.next(); - messages.addAll( - encodeList(results.items, encodePresenceMessage)); - } - return messages; -} diff --git a/test_integration/pubspec.lock b/test_integration/pubspec.lock index 8f513be99..9b9bec96f 100644 --- a/test_integration/pubspec.lock +++ b/test_integration/pubspec.lock @@ -5,11 +5,11 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" url: "https://pub.dev" source: hosted - version: "67.0.0" - ably_flutter: + version: "93.0.0" + ably_pubsub_device_flutter: dependency: "direct main" description: path: ".." @@ -20,18 +20,18 @@ packages: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "10.0.1" args: dependency: "direct main" description: name: args - sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.7.0" async: dependency: transitive description: @@ -52,10 +52,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" charcode: dependency: transitive description: @@ -64,6 +64,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" clock: dependency: transitive description: @@ -92,10 +100,10 @@ packages: dependency: transitive description: name: coverage - sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" + sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d" url: "https://pub.dev" source: hosted - version: "1.7.2" + version: "1.15.1" crypto: dependency: transitive description: @@ -116,10 +124,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" file: dependency: transitive description: @@ -204,38 +212,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.3" - js: - dependency: transitive - description: - name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" - url: "https://pub.dev" - source: hosted - version: "0.6.5" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -256,26 +256,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.20" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.19.0" mime: dependency: transitive description: @@ -445,26 +445,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + sha256: ca578dc12bb8b2f40b67b7d3bd2fac4f31c01a6ff7130a14e2597b919934507f url: "https://pub.dev" source: hosted - version: "1.25.15" + version: "1.31.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.12" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: d2e98ec12998368dc59ddd47ab709f2cd55acd6b66dc7db764455a44082f4bc5 url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.18" typed_data: dependency: transitive description: @@ -477,10 +477,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.4.2" vm_service: dependency: transitive description: @@ -525,10 +525,10 @@ packages: dependency: transitive description: name: yaml - sha256: "3cee79b1715110341012d27756d9bae38e650588acd38d3f3c610822e1337ace" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.3" sdks: - dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/test_integration/pubspec.yaml b/test_integration/pubspec.yaml index 5d487661e..68e4eb2d1 100644 --- a/test_integration/pubspec.yaml +++ b/test_integration/pubspec.yaml @@ -4,13 +4,14 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=2.14.0 <4.0.0" + sdk: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" dependencies: flutter: sdk: flutter - ably_flutter: + ably_pubsub_device_flutter: path: ".." args: ^2.3.1 diff --git a/test_integration/test_driver/runner.dart b/test_integration/test_driver/runner.dart index 5a113d2bd..050e3ae61 100644 --- a/test_integration/test_driver/runner.dart +++ b/test_integration/test_driver/runner.dart @@ -25,8 +25,9 @@ void main(List args) { return; } - final selectedModules = - _parseModulesStringIntoTestModules(argv['modules'] as List); + final selectedModules = _parseModulesStringIntoTestModules( + argv['modules'] as List, + ); if (selectedModules.isEmpty) { runTests(all: true); @@ -36,7 +37,8 @@ void main(List args) { } Iterable _parseModulesStringIntoTestModules( - List modulesStrings) { + List modulesStrings, +) { final modules = modulesStrings .map((module) => EnumToString.fromString(TestModules.values, module)) .whereType() diff --git a/test_integration/test_driver/test_implementation/basic_platform_tests.dart b/test_integration/test_driver/test_implementation/basic_platform_tests.dart index 3eda6ae9d..51f603a47 100644 --- a/test_integration/test_driver/test_implementation/basic_platform_tests.dart +++ b/test_integration/test_driver/test_implementation/basic_platform_tests.dart @@ -6,7 +6,8 @@ void testPlatformAndAblyVersion(FlutterDriver Function() getDriver) { const message = TestControlMessage(TestName.platformAndAblyVersion); late TestControlResponseMessage response; setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); + () async => response = await requestDataForTest(getDriver(), message), + ); test('platformVersion is a string', () { expect(response.payload['platformVersion'], isA()); @@ -29,7 +30,8 @@ void testDemoDependencies(FlutterDriver Function() getDriver) { const message = TestControlMessage(TestName.appKeyProvisioning); late TestControlResponseMessage response; setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); + () async => response = await requestDataForTest(getDriver(), message), + ); test('appKey is a string', () { expect(response.payload['appKey'], isA()); diff --git a/test_integration/test_driver/test_implementation/crypto_tests.dart b/test_integration/test_driver/test_implementation/crypto_tests.dart index 201c6fd5a..a162febcc 100644 --- a/test_integration/test_driver/test_implementation/crypto_tests.dart +++ b/test_integration/test_driver/test_implementation/crypto_tests.dart @@ -45,14 +45,18 @@ void testCryptoEnsureSupportedKeyLength(FlutterDriver Function() getDriver) { setUpAll(() async { response = await requestDataForTest(getDriver(), message); - keyWithDefaultLengthException = response - .payload['keyWithDefaultLengthException'] as Map?; - keyWith128BitLengthException = response - .payload['keyWith128BitLengthException'] as Map?; - keyWith256BitLengthException = response - .payload['keyWith256BitLengthException'] as Map?; - keyWith127BitLengthException = response - .payload['keyWith127BitLengthException'] as Map?; + keyWithDefaultLengthException = + response.payload['keyWithDefaultLengthException'] + as Map?; + keyWith128BitLengthException = + response.payload['keyWith128BitLengthException'] + as Map?; + keyWith256BitLengthException = + response.payload['keyWith256BitLengthException'] + as Map?; + keyWith127BitLengthException = + response.payload['keyWith127BitLengthException'] + as Map?; }); group('crypto#ensureSupportedKeyLength', () { @@ -79,15 +83,17 @@ void testCryptoGetDefaultParams(FlutterDriver Function() getDriver) { late TestControlResponseMessage response; Map? keyWith127BitLengthException; - // Can't use [CipherParams] as a type here because import from - // ably_flutter package in this file breaks integration test suite + // Can't use [CipherParams] as a type here because import from the + // ably_pubsub_device_flutter package in this file breaks the integration + // test suite // So we can only pass a value to determine if params were fetched late bool? didFetchDefaultParams; setUpAll(() async { response = await requestDataForTest(getDriver(), message); - keyWith127BitLengthException = response - .payload['keyWith127BitLengthException'] as Map?; + keyWith127BitLengthException = + response.payload['keyWith127BitLengthException'] + as Map?; didFetchDefaultParams = response.payload['didFetchDefaultParams'] as bool?; }); diff --git a/test_integration/test_driver/test_implementation/helper_tests.dart b/test_integration/test_driver/test_implementation/helper_tests.dart index 287b71770..948639a78 100644 --- a/test_integration/test_driver/test_implementation/helper_tests.dart +++ b/test_integration/test_driver/test_implementation/helper_tests.dart @@ -6,7 +6,8 @@ void testShouldReportUnhandledException(FlutterDriver Function() getDriver) { const message = TestControlMessage(TestName.testHelperUnhandledExceptionTest); late TestControlResponseMessage response; setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); + () async => response = await requestDataForTest(getDriver(), message), + ); test('returns appropriate error message', () { expect(response.payload['error']['exceptionType'], 'String'); diff --git a/test_integration/test_driver/test_implementation/realtime_tests.dart b/test_integration/test_driver/test_implementation/realtime_tests.dart index 1a9646a15..bcd309df3 100644 --- a/test_integration/test_driver/test_implementation/realtime_tests.dart +++ b/test_integration/test_driver/test_implementation/realtime_tests.dart @@ -53,8 +53,9 @@ void testRealtimePublishSpec(FlutterDriver Function() getDriver) { messages = transformListResponse(response.payload['publishedMessages']); messages2 = transformListResponse(response.payload['publishedMessages2']); messages3 = transformListResponse(response.payload['publishedMessages3']); - messagesWithExtras = - transformListResponse(response.payload['publishedExtras']); + messagesWithExtras = transformListResponse( + response.payload['publishedExtras'], + ); exception = response.payload['exception'] as Map?; }); @@ -96,15 +97,13 @@ void testRealtimePublishSpec(FlutterDriver Function() getDriver) { expect(messages[5]['timestamp'] != messages[4]['timestamp'], true); }); - test( - '(RSL1m1) Publishing a Message with no clientId when the clientId' + test('(RSL1m1) Publishing a Message with no clientId when the clientId' ' is set to some value in the client options should result in a message' ' received with the clientId property set to that value', () { expect(messages[0]['clientId'], 'someClientId'); }); - test( - '(RSL1m2) Publishing a Message with a clientId set to the same' + test('(RSL1m2) Publishing a Message with a clientId set to the same' ' value as the clientId in the client options should result in' ' a message received with the clientId property set to that value', () { expect(messages[7]['clientId'], 'someClientId'); @@ -132,13 +131,10 @@ void testRealtimePublishSpec(FlutterDriver Function() getDriver) { () => expect(messages2[0]['clientId'], 'client-id'), ); - test( - 'publishes non-ascii characters', - () { - expect(messages3[0]['name'], 'Ωπ'); - expect(messages3[0]['data'], 'ΨΔ'); - }, - ); + test('publishes non-ascii characters', () { + expect(messages3[0]['name'], 'Ωπ'); + expect(messages3[0]['data'], 'ΨΔ'); + }); }); test('(RSL6a2) publishes message extras', () { @@ -173,21 +169,27 @@ void testRealtimeEncryptedPublishSpec(FlutterDriver Function() getDriver) { setUpAll(() async { response = await requestDataForTest(getDriver(), message); - historyOfEncryptedChannel = - transformListResponse(response.payload['historyOfEncryptedChannel']); - historyOfPlaintextChannel = - transformListResponse(response.payload['historyOfPlaintextChannel']); + historyOfEncryptedChannel = transformListResponse( + response.payload['historyOfEncryptedChannel'], + ); + historyOfPlaintextChannel = transformListResponse( + response.payload['historyOfPlaintextChannel'], + ); historyOfEncryptedPushEnabledChannel = transformListResponse( - response.payload['historyOfEncryptedPushEnabledChannel']); + response.payload['historyOfEncryptedPushEnabledChannel'], + ); historyOfPlaintextPushEnabledChannel = transformListResponse( - response.payload['historyOfPlaintextPushEnabledChannel']); + response.payload['historyOfPlaintextPushEnabledChannel'], + ); }); group('RSL5', () { test('does not encrypt name', () { for (var i = 0; i < historyOfEncryptedPushEnabledChannel.length; i++) { - expect(historyOfEncryptedChannel[i]['name'], - equals(historyOfPlaintextChannel[i]['name'])); + expect( + historyOfEncryptedChannel[i]['name'], + equals(historyOfPlaintextChannel[i]['name']), + ); } }); @@ -205,8 +207,10 @@ void testRealtimeEncryptedPublishSpec(FlutterDriver Function() getDriver) { test('does not encrypt extras', () { for (var i = 0; i < historyOfEncryptedPushEnabledChannel.length; i++) { - expect(historyOfEncryptedPushEnabledChannel[i]['name'], - equals(historyOfPlaintextPushEnabledChannel[i]['name'])); + expect( + historyOfEncryptedPushEnabledChannel[i]['name'], + equals(historyOfPlaintextPushEnabledChannel[i]['name']), + ); } }); }); @@ -246,70 +250,63 @@ void testRealtimeEvents(FlutterDriver Function() getDriver) { group('realtime#channel#connection', () { test('#state', () { expect( - connectionStates, - orderedEquals(const [ - 'initialized', - 'initialized', - 'connected', - 'connected', - 'closed', - ])); + connectionStates, + orderedEquals(const [ + 'initialized', + 'initialized', + 'connected', + 'connected', + 'closed', + ]), + ); }); test( '#on returns a stream which can be subscribed for connectionStateChanges', () { expect( - connectionStateChanges.map((e) => e['event']), - orderedEquals(const [ - 'connecting', - 'connected', - 'closing', - 'closed', - ])); + connectionStateChanges.map((e) => e['event']), + orderedEquals(const ['connecting', 'connected', 'closing', 'closed']), + ); expect( - connectionStateChanges.map((e) => e['previous']), - orderedEquals(const [ - 'initialized', - 'connecting', - 'connected', - 'closing', - ])); + connectionStateChanges.map((e) => e['previous']), + orderedEquals(const [ + 'initialized', + 'connecting', + 'connected', + 'closing', + ]), + ); }, ); - test( - '#on returns a stream which can be subscribed' - ' for connection state changes with filter', - () { - expect(filteredConnectionStateChanges.map((e) => e['event']), const [ - 'connected', - ]); - expect(filteredConnectionStateChanges.map((e) => e['current']), const [ - 'connected', - ]); - expect(filteredConnectionStateChanges.map((e) => e['previous']), const [ - 'connecting', - ]); - }, - ); + test('#on returns a stream which can be subscribed' + ' for connection state changes with filter', () { + expect(filteredConnectionStateChanges.map((e) => e['event']), const [ + 'connected', + ]); + expect(filteredConnectionStateChanges.map((e) => e['current']), const [ + 'connected', + ]); + expect(filteredConnectionStateChanges.map((e) => e['previous']), const [ + 'connecting', + ]); + }); }); group('realtime#channel#chanenls#channel', () { - test( - '#state', - () { - expect( - channelStates, - orderedEquals(const [ - 'initialized', - 'initialized', - 'attached', - 'attached', - 'detached', - 'detached', - ])); - }, - ); + test('#state', () { + expect( + channelStates, + orderedEquals(const [ + 'initialized', + 'initialized', + 'attached', + 'attached', + 'detached', + 'detached', + ]), + ); + }); test( '#on returns a stream which can be subscribed for channel state changes', () { @@ -359,55 +356,54 @@ void testRealtimeEvents(FlutterDriver Function() getDriver) { ]; } - expect(channelStateChanges.map((e) => e['event']), - orderedEquals(_stateChangeEvents)); + expect( + channelStateChanges.map((e) => e['event']), + orderedEquals(_stateChangeEvents), + ); - expect(channelStateChanges.map((e) => e['current']), - orderedEquals(_stateChangeCurrent)); + expect( + channelStateChanges.map((e) => e['current']), + orderedEquals(_stateChangeCurrent), + ); - expect(channelStateChanges.map((e) => e['previous']), - orderedEquals(_stateChangePrevious)); + expect( + channelStateChanges.map((e) => e['previous']), + orderedEquals(_stateChangePrevious), + ); }, ); - test( - '#on returns a stream which can be subscribed' - ' for channel state changes with filter', - () { - List _stateChangeCurrent; - List _stateChangePrevious; - List _stateChangeEvents; - if (channelStateChanges.length == 4) { - // iOS - _stateChangeCurrent = const [ - 'attaching', - ]; - _stateChangePrevious = const [ - 'initialized', - ]; - } else { - // Android - _stateChangeCurrent = const [ - 'attaching', - 'attaching', - ]; - _stateChangePrevious = const [ - 'initialized', - 'attaching', - ]; - } - _stateChangeEvents = _stateChangeCurrent; - // filteredChannelStateChanges - expect(filteredChannelStateChanges.map((e) => e['event']), - orderedEquals(_stateChangeEvents)); + test('#on returns a stream which can be subscribed' + ' for channel state changes with filter', () { + List _stateChangeCurrent; + List _stateChangePrevious; + List _stateChangeEvents; + if (channelStateChanges.length == 4) { + // iOS + _stateChangeCurrent = const ['attaching']; + _stateChangePrevious = const ['initialized']; + } else { + // Android + _stateChangeCurrent = const ['attaching', 'attaching']; + _stateChangePrevious = const ['initialized', 'attaching']; + } + _stateChangeEvents = _stateChangeCurrent; + // filteredChannelStateChanges + expect( + filteredChannelStateChanges.map((e) => e['event']), + orderedEquals(_stateChangeEvents), + ); - expect(filteredChannelStateChanges.map((e) => e['current']), - orderedEquals(_stateChangeCurrent)); + expect( + filteredChannelStateChanges.map((e) => e['current']), + orderedEquals(_stateChangeCurrent), + ); - expect(filteredChannelStateChanges.map((e) => e['previous']), - orderedEquals(_stateChangePrevious)); - }, - ); + expect( + filteredChannelStateChanges.map((e) => e['previous']), + orderedEquals(_stateChangePrevious), + ); + }); }); } @@ -422,55 +418,47 @@ void testRealtimeSubscribe(FlutterDriver Function() getDriver) { setUpAll(() async { response = await requestDataForTest(getDriver(), message); all = transformListResponse(response.payload['all']); - filteredWithName = - transformListResponse(response.payload['filteredWithName']); + filteredWithName = transformListResponse( + response.payload['filteredWithName'], + ); filteredWithNames = transformListResponse( response.payload['filteredWithNames'], ); extrasMessages = transformListResponse(response.payload['extrasMessages']); }); - test( - 'realtime#channels#channel#subscribe should subscribe to' - ' all message on channel', - () { - testAllPublishedMessages(all); - }, - ); + test('realtime#channels#channel#subscribe should subscribe to' + ' all message on channel', () { + testAllPublishedMessages(all); + }); - test( - 'realtime#channels#channel#subscribe(name: string)' - ' should subscribe to messages with specified name', - () { - expect(filteredWithName.length, equals(2)); + test('realtime#channels#channel#subscribe(name: string)' + ' should subscribe to messages with specified name', () { + expect(filteredWithName.length, equals(2)); - expect(filteredWithName[0]['name'], 'name1'); - expect(filteredWithName[0]['data'], isNull); + expect(filteredWithName[0]['name'], 'name1'); + expect(filteredWithName[0]['data'], isNull); - expect(filteredWithName[1]['name'], 'name1'); - expect(filteredWithName[1]['data'], equals('Ably')); - }, - ); + expect(filteredWithName[1]['name'], 'name1'); + expect(filteredWithName[1]['data'], equals('Ably')); + }); - test( - 'realtime#channels#channel#subscribe(names: List)' - ' should subscribe to messages with specified names', - () { - expect(filteredWithNames.length, equals(4)); + test('realtime#channels#channel#subscribe(names: List)' + ' should subscribe to messages with specified names', () { + expect(filteredWithNames.length, equals(4)); - expect(filteredWithNames[0]['name'], 'name1'); - expect(filteredWithNames[0]['data'], isNull); + expect(filteredWithNames[0]['name'], 'name1'); + expect(filteredWithNames[0]['data'], isNull); - expect(filteredWithNames[1]['name'], 'name1'); - expect(filteredWithNames[1]['data'], equals('Ably')); + expect(filteredWithNames[1]['name'], 'name1'); + expect(filteredWithNames[1]['data'], equals('Ably')); - expect(filteredWithNames[2]['name'], 'name2'); - expect(filteredWithNames[2]['data'], equals([1, 2, 3])); + expect(filteredWithNames[2]['name'], 'name2'); + expect(filteredWithNames[2]['data'], equals([1, 2, 3])); - expect(filteredWithNames[3]['name'], 'name2'); - expect(filteredWithNames[3]['data'], equals(['hello', 'ably'])); - }, - ); + expect(filteredWithNames[3]['name'], 'name2'); + expect(filteredWithNames[3]['data'], equals(['hello', 'ably'])); + }); test('retrieves extras posted in message', () { expect(extrasMessages[0]['name'], 'name'); @@ -499,10 +487,12 @@ void testRealtimeHistory(FlutterDriver Function() getDriver) { historyDefault = transformListResponse(response.payload['historyDefault']); historyLimit4 = transformListResponse(response.payload['historyLimit4']); historyLimit2 = transformListResponse(response.payload['historyLimit2']); - historyForwardLimit4 = - transformListResponse(response.payload['historyForwardLimit4']); - historyWithStart = - transformListResponse(response.payload['historyWithStart']); + historyForwardLimit4 = transformListResponse( + response.payload['historyForwardLimit4'], + ); + historyWithStart = transformListResponse( + response.payload['historyWithStart'], + ); historyWithStartAndEnd = transformListResponse( response.payload['historyWithStartAndEnd'], ); @@ -531,11 +521,13 @@ void testRealtimeHistory(FlutterDriver Function() getDriver) { testAllPublishedMessages(historyLimit4.reversed.toList()); testAllPublishedMessages(historyLimit2.reversed.toList()); }); - test('queries entries in reverse order with direction set to "forward"', - () { - expect(historyForwardLimit4.length, equals(8)); - testAllPublishedMessages(historyForwardLimit4); - }); + test( + 'queries entries in reverse order with direction set to "forward"', + () { + expect(historyForwardLimit4.length, equals(8)); + testAllPublishedMessages(historyForwardLimit4); + }, + ); test('returns entries created after specified time', () { expect(historyWithStart.length, equals(2)); expect(historyWithStart[0]['name'], equals('history')); @@ -555,7 +547,8 @@ void testRealtimeHistoryWithAuthCallback(FlutterDriver Function() getDriver) { const message = TestControlMessage(TestName.realtimeHistoryWithAuthCallback); late TestControlResponseMessage response; setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); + () async => response = await requestDataForTest(getDriver(), message), + ); test('auth callback is invoked', () { expect(response.payload['authCallbackInvoked'], isTrue); @@ -592,10 +585,12 @@ void testRealtimePresenceGet(FlutterDriver Function() getDriver) { response = await requestDataForTest(getDriver(), message); membersInitial = transformListResponse(response.payload['membersInitial']); membersDefault = transformListResponse(response.payload['membersDefault']); - membersClientId = - transformListResponse(response.payload['membersClientId']); - membersConnectionId = - transformListResponse(response.payload['membersConnectionId']); + membersClientId = transformListResponse( + response.payload['membersClientId'], + ); + membersConnectionId = transformListResponse( + response.payload['membersConnectionId'], + ); }); group('realtime#channels#channel#presence#get', () { @@ -639,8 +634,9 @@ void testRealtimePresenceHistory(FlutterDriver Function() getDriver) { historyDefault = transformListResponse(response.payload['historyDefault']); historyLimit4 = transformListResponse(response.payload['historyLimit4']); historyLimit2 = transformListResponse(response.payload['historyLimit2']); - historyForwards = - transformListResponse(response.payload['historyForwards']); + historyForwards = transformListResponse( + response.payload['historyForwards'], + ); historyWithStart = transformListResponse( response.payload['historyWithStart'], ).reversed.toList(); @@ -663,13 +659,10 @@ void testRealtimePresenceHistory(FlutterDriver Function() getDriver) { testAllPresenceMessagesHistory(historyLimit4.reversed.toList()); testAllPresenceMessagesHistory(historyLimit2.reversed.toList()); }); - test( - 'queries entries in reverse order with direction set to "forward"', - () { - expect(historyForwards.length, equals(8)); - testAllPresenceMessagesHistory(historyForwards.toList()); - }, - ); + test('queries entries in reverse order with direction set to "forward"', () { + expect(historyForwards.length, equals(8)); + testAllPresenceMessagesHistory(historyForwards.toList()); + }); test('returns entries created after specified time', () { expect(historyWithStart.length, equals(2)); expect(historyWithStart[0]['clientId'], equals('someClientId')); @@ -693,8 +686,9 @@ void testRealtimeEnterUpdateLeave(FlutterDriver Function() getDriver) { setUpAll(() async { response = await requestDataForTest(getDriver(), message); - clientIDClashMatrix = - transformListResponse(response.payload['clientIDClashMatrix']); + clientIDClashMatrix = transformListResponse( + response.payload['clientIDClashMatrix'], + ); actionMatrix = transformListResponse(response.payload['actionMatrix']); }); @@ -739,8 +733,7 @@ void testRealtimeEnterUpdateLeave(FlutterDriver Function() getDriver) { ); } - test( - 'clientID should be same in both realtime ClientOptions as well as' + test('clientID should be same in both realtime ClientOptions as well as' ' the one passed to presence enter/update/leave APIs.' ' If unequal, throw error.', () { for (final clashEntry in clientIDClashMatrix) { @@ -758,20 +751,10 @@ void testRealtimeEnterUpdateLeave(FlutterDriver Function() getDriver) { ); } else if (presenceClientID == null) { // only realtimeClientID is present - testMatrixEntry( - clashEntry, - enter: true, - update: true, - leave: true, - ); + testMatrixEntry(clashEntry, enter: true, update: true, leave: true); } else { // both clientIDs are present and are unequal - testMatrixEntry( - clashEntry, - enter: true, - update: true, - leave: true, - ); + testMatrixEntry(clashEntry, enter: true, update: true, leave: true); } } else { if (presenceClientID == null) { @@ -820,10 +803,12 @@ void testRealtimePresenceSubscription(FlutterDriver Function() getDriver) { response = await requestDataForTest(getDriver(), message); allMessages = transformListResponse(response.payload['allMessages']); enterMessages = transformListResponse(response.payload['enterMessages']); - enterUpdateMessages = - transformListResponse(response.payload['enterUpdateMessages']); - partialMessages = - transformListResponse(response.payload['partialMessages']); + enterUpdateMessages = transformListResponse( + response.payload['enterUpdateMessages'], + ); + partialMessages = transformListResponse( + response.payload['partialMessages'], + ); }); void _test(List> messages) { @@ -838,31 +823,43 @@ void testRealtimePresenceSubscription(FlutterDriver Function() getDriver) { } } - test('listens to messages', () { - expect(allMessages.length, equals(8)); - _test(allMessages); - }, - skip: - 'One message in allMessages gets `present` action, but should have been `enter`. See https://github.com/ably/ably-flutter/issues/150'); - - test('filters messages with single action', () { - expect(enterMessages.length, equals(1)); - _test(enterMessages); - }, - skip: - 'expected 1 but got 0. See https://github.com/ably/ably-flutter/issues/150'); - - test('filters messages with multiple actions', () { - expect(enterUpdateMessages.length, equals(7)); - _test(enterUpdateMessages); - }, - skip: - 'Got a length of 6 but expected 7. See https://github.com/ably/ably-flutter/issues/150'); - - test('listens to messages only until subscription is active', () { - expect(partialMessages.length, equals(7)); - expect(partialMessages, equals(enterUpdateMessages)); - }, - skip: - 'Expected a set of messages, but got the same set but with 1 unexpected extra message at the start, with `present` action. See https://github.com/ably/ably-flutter/issues/150'); + test( + 'listens to messages', + () { + expect(allMessages.length, equals(8)); + _test(allMessages); + }, + skip: + 'One message in allMessages gets `present` action, but should have been `enter`. See https://github.com/ably/ably-flutter/issues/150', + ); + + test( + 'filters messages with single action', + () { + expect(enterMessages.length, equals(1)); + _test(enterMessages); + }, + skip: + 'expected 1 but got 0. See https://github.com/ably/ably-flutter/issues/150', + ); + + test( + 'filters messages with multiple actions', + () { + expect(enterUpdateMessages.length, equals(7)); + _test(enterUpdateMessages); + }, + skip: + 'Got a length of 6 but expected 7. See https://github.com/ably/ably-flutter/issues/150', + ); + + test( + 'listens to messages only until subscription is active', + () { + expect(partialMessages.length, equals(7)); + expect(partialMessages, equals(enterUpdateMessages)); + }, + skip: + 'Expected a set of messages, but got the same set but with 1 unexpected extra message at the start, with `present` action. See https://github.com/ably/ably-flutter/issues/150', + ); } diff --git a/test_integration/test_driver/test_implementation/rest_tests.dart b/test_integration/test_driver/test_implementation/rest_tests.dart deleted file mode 100644 index 0e740c30e..000000000 --- a/test_integration/test_driver/test_implementation/rest_tests.dart +++ /dev/null @@ -1,517 +0,0 @@ -import 'dart:typed_data'; - -import 'package:ably_flutter_integration_test/driver_data_handler.dart'; -import 'package:flutter_driver/flutter_driver.dart'; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void testRestPublish(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restPublish); - late TestControlResponseMessage response; - setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); - - test('rest instance has a valid handle on publish', () { - expect(response.payload['handle'], isA()); - expect(response.payload['handle'], greaterThan(0)); - }); -} - -void testRestRequestTokenPublish(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restRequestToken); - late TestControlResponseMessage response; - setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); - - test( - 'rest instance with token has a valid handle and successfully' - ' publishes messages', () { - expect(response.payload['handle'], isA()); - expect(response.payload['handle'], greaterThan(0)); - }); -} - -void testRestCreateTokenRequestPublish(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restCreateTokenRequest); - late TestControlResponseMessage response; - setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); - - test( - 'rest instance with token has a valid handle and successfully ' - 'publishes messages', () { - expect(response.payload['handle'], isA()); - expect(response.payload['log'], greaterThan(0)); - }); -} - -void testRestEncryptedPublish(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restEncryptedPublish); - late TestControlResponseMessage response; - setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); - - test('rest instance has a valid handle on encrypted publish', () { - expect(response.payload['handle'], isA()); - expect(response.payload['handle'], greaterThan(0)); - }); -} - -void testRestPublishSpec(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restPublishSpec); - late TestControlResponseMessage response; - late List> messages; - late List> messages2; - late List> messages3; - late List> messagesWithExtras; - Map? exception; - Map? exception2; - Map? exception3; - - setUpAll(() async { - response = await requestDataForTest(getDriver(), message); - - messages = transformListResponse(response.payload['publishedMessages']); - messages2 = transformListResponse(response.payload['publishedMessages2']); - messages3 = transformListResponse(response.payload['publishedMessages3']); - messagesWithExtras = - transformListResponse(response.payload['publishedExtras']); - exception = response.payload['exception'] as Map?; - exception2 = response.payload['exception2'] as Map?; - exception3 = response.payload['exception3'] as Map?; - }); - - group('RSl1', () { - test('publishes without a name and data', () { - expect(messages[0]['name'], null); - expect(messages[0]['data'], null); - }); - test('publishes without data', () { - expect(messages[1]['name'], 'name1'); - expect(messages[1]['data'], null); - }); - test('publishes without a name', () { - expect(messages[2]['name'], null); - expect(messages[2]['data'], 'data1'); - }); - test('publishes with name and data', () { - expect(messages[3]['name'], 'name1'); - expect(messages[3]['data'], 'data1'); - }); - test('publishes single message object', () { - expect(messages[4]['name'], 'message-name1'); - expect(messages[4]['data'], 'message-data1'); - }); - test('publishes multiple message objects', () { - expect(messages[5]['name'], 'messages-name1'); - expect(messages[5]['data'], 'messages-data1'); - expect(messages[6]['name'], 'messages-name2'); - expect(messages[6]['data'], 'messages-data2'); - }); - test('publishes multiple messages at once', () { - expect(messages[5]['timestamp'] == messages[6]['timestamp'], true); - expect(messages[5]['timestamp'] != messages[4]['timestamp'], true); - }); - - test( - '(RSL1m1) Publishing a Message with no clientId when the clientId' - ' is set to some value in the client options should result in a message' - ' received with the clientId property set to that value', () { - expect(messages[0]['clientId'], 'someClientId'); - }, - skip: - 'Failing on Android, the clientId is null`. See https://github.com/ably/ably-flutter/issues/150'); - - test( - '(RSL1m2) Publishing a Message with a clientId set to the same' - ' value as the clientId in the client options should result in' - ' a message received with the clientId property set to that value', () { - expect(messages[7]['clientId'], 'someClientId'); - }); - - test( - '(RSL1d) Raises an error if the message was not successfully published', - () => expect(exception == null, false), - ); - - test( - '(RSL1m4) Publishing a Message with a clientId set to a different value' - ' from the clientId in the client options should result in a message' - ' being rejected by the server.', - () { - expect(response.payload['exception'], isA>()); - // TODO as error details are incompatible from both libraries, - // it makes no sense to include below expect's - // - // final exception = Map.castFrom( - // response.payload['exception'] as Map); - // expect(exception['code'], '14'); //40012 from android and 14 from iOS - // expect( - // exception['message'], - // 'Error publishing rest message;' - // ' err = attempted to publish message with an invalid clientId', - // ); - // expect(exception['errorInfo']['message'], - // 'attempted to publish message with an invalid clientId'); - }, - ); - - test( - '(RSL1m3) Publishing a Message with a clientId set to a value' - ' from an unidentified client (no clientId in the client options' - ' and credentials that can assume any clientId) should result in' - ' a message received with the clientId property set to that value', - () => expect(messages2[0]['clientId'], 'client-id'), - ); - - test( - '(RSL1i) If the total size of the message or (if publishing an array)' - ' messages, calculated per TO3l8, exceeds the maxMessageSize, then the' - ' client library should reject the publish and indicate an error' - ' with code 40009', - () { - // allows publishing messages length <= max allowed limit - expect(exception2 == null, true); - // errors out publishing messages length > max allowed limit - expect(exception3 == null, false); - }, - ); - - test( - 'publishes non-ascii characters', - () { - expect(messages3[0]['name'], 'Ωπ'); - expect(messages3[0]['data'], 'ΨΔ'); - }, - ); - }); - - test('RSL6a2 publishes message extras', () { - expect(messagesWithExtras[0]['name'], 'name'); - expect(messagesWithExtras[0]['data'], 'data'); - checkMessageExtras(messagesWithExtras[0]['extras']['extras'] as Map); - expect(messagesWithExtras[0]['extras']['delta'], null); - }); -} - -void testRestEncryptedPublishSpec(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restEncryptedPublishSpec); - late TestControlResponseMessage response; - late List> historyOfEncryptedChannel; - late List> historyOfPlaintextChannel; - late List> historyOfEncryptedPushEnabledChannel; - late List> historyOfPlaintextPushEnabledChannel; - - setUpAll(() async { - response = await requestDataForTest(getDriver(), message); - - historyOfEncryptedChannel = - transformListResponse(response.payload['historyOfEncryptedChannel']); - historyOfPlaintextChannel = - transformListResponse(response.payload['historyOfPlaintextChannel']); - historyOfEncryptedPushEnabledChannel = transformListResponse( - response.payload['historyOfEncryptedPushEnabledChannel']); - historyOfPlaintextPushEnabledChannel = transformListResponse( - response.payload['historyOfPlaintextPushEnabledChannel']); - }); - - group('RSL5', () { - test('does not encrypt name', () { - for (var i = 0; i < historyOfEncryptedPushEnabledChannel.length; i++) { - expect(historyOfEncryptedChannel[i]['name'], - equals(historyOfPlaintextChannel[i]['name'])); - } - }); - - test('does encrypt data', () { - for (var i = 0; i < historyOfEncryptedPushEnabledChannel.length; i++) { - final encryptedData = historyOfEncryptedChannel[i]['data']; - final plaintextData = historyOfPlaintextChannel[i]['data']; - - if (encryptedData != null) { - expect(encryptedData, isNot(equals(plaintextData))); - expect(plaintextData, isA()); - } - } - }); - - test('does not encrypt extras', () { - for (var i = 0; i < historyOfEncryptedPushEnabledChannel.length; i++) { - expect(historyOfEncryptedPushEnabledChannel[i]['name'], - equals(historyOfPlaintextPushEnabledChannel[i]['name'])); - } - }); - }); -} - -void testRestPublishWithAuthCallback(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restPublishWithAuthCallback); - late TestControlResponseMessage response; - setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); - - test('auth callback is invoked', () { - expect(response.payload['authCallbackInvoked'], isTrue); - }); -} - -void testRestHistory(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restHistory); - late TestControlResponseMessage response; - - late Map paginatedResult; - late List> historyDefault; - late List> historyLimit4; - late List> historyLimit2; - late List> historyForwardLimit4; - late List> historyWithStart; - late List> historyWithStartAndEnd; - - setUpAll(() async { - response = await requestDataForTest(getDriver(), message); - paginatedResult = - response.payload['paginatedResult'] as Map; - historyDefault = transformListResponse(response.payload['historyDefault']); - historyLimit4 = transformListResponse(response.payload['historyLimit4']); - historyLimit2 = transformListResponse(response.payload['historyLimit2']); - historyForwardLimit4 = - transformListResponse(response.payload['historyForwardLimit4']); - historyWithStart = - transformListResponse(response.payload['historyWithStart']); - historyWithStartAndEnd = transformListResponse( - response.payload['historyWithStartAndEnd'], - ); - }); - - group('paginated result', () { - test('#items is a list', () { - expect(paginatedResult['items'], isA>()); - }); - test('#hasNext indicates whether there are more entries', () { - expect(paginatedResult['hasNext'], false); - }); - test('#isLast indicates if current page is last page', () { - expect(paginatedResult['isLast'], true); - }); - }); - - group('rest#channels#channel#history', () { - test('queries all entries by default', () { - expect(historyDefault.length, equals(8)); - testAllPublishedMessages(historyDefault.reversed.toList()); - }); - test('queries all entries by paginating with limit', () { - expect(historyLimit4.length, equals(8)); - expect(historyLimit2.length, equals(8)); - testAllPublishedMessages(historyLimit4.reversed.toList()); - testAllPublishedMessages(historyLimit2.reversed.toList()); - }); - test('queries entries in reverse order with direction set to "forward"', - () { - expect(historyForwardLimit4.length, equals(8)); - testAllPublishedMessages(historyForwardLimit4); - }); - test('returns entries created after specified time', () { - expect(historyWithStart.length, equals(2)); - expect(historyWithStart[0]['name'], equals('history')); - expect(historyWithStart[0]['data'], equals('test2')); - expect(historyWithStart[1]['name'], equals('history')); - expect(historyWithStart[1]['data'], equals('test')); - }); - test('returns entries created between specified start and end', () { - expect(historyWithStartAndEnd.length, equals(1)); - expect(historyWithStartAndEnd[0]['name'], equals('history')); - expect(historyWithStartAndEnd[0]['data'], equals('test')); - }); - }); -} - -void testRestHistoryWithAuthCallback(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restHistoryWithAuthCallback); - late TestControlResponseMessage response; - setUpAll( - () async => response = await requestDataForTest(getDriver(), message)); - - test('auth callback is invoked', () { - expect(response.payload['authCallbackInvoked'], isTrue); - }); -} - -void testRestTime(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restTime); - late TestControlResponseMessage response; - late DateTime restTime; - - setUpAll(() async { - response = await requestDataForTest(getDriver(), message); - restTime = DateTime.parse(response.payload['time'] as String); - }); - - group('rest#time', () { - test('returns non-zero date and time', () { - expect(restTime.millisecondsSinceEpoch, isNot(0)); - }); - }); -} - -void testRestPresenceGet(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restPresenceGet); - late TestControlResponseMessage response; - - late List> membersInitial; - late List> membersDefault; - late List> membersLimit4; - late List> membersLimit2; - late List> membersClientId; - late List> membersConnectionId; - - setUpAll(() async { - response = await requestDataForTest(getDriver(), message); - membersInitial = transformListResponse(response.payload['membersInitial']); - membersDefault = transformListResponse(response.payload['membersDefault']); - membersLimit4 = transformListResponse(response.payload['membersLimit4']); - membersLimit2 = transformListResponse(response.payload['membersLimit2']); - membersClientId = - transformListResponse(response.payload['membersClientId']); - membersConnectionId = - transformListResponse(response.payload['membersConnectionId']); - }); - - group('rest#channels#channel#presence#get', () { - test('has 0 members without any clients joined', () { - expect(membersInitial.length, equals(0)); - }); - test('queries all entries by default', () { - expect(membersDefault.length, equals(8)); - testAllPresenceMembers(membersDefault..sort(timestampSorter)); - }); - test('queries all entries by paginating with limit', () { - expect(membersLimit4.length, equals(8)); - expect(membersLimit2.length, equals(8)); - testAllPresenceMembers(membersLimit4..sort(timestampSorter)); - testAllPresenceMembers(membersLimit2..sort(timestampSorter)); - }); - test('filters entries with clientId when specified', () { - // there is only 1 client with clientId 'client-1 - expect(membersClientId.length, equals(1)); - expect(membersClientId[0]['clientId'], equals('client-1')); - checkMessageData(1, membersClientId[0]['data']); - }); - test('filters entries with clientId when specified', () { - // TODO similarly (like clientId) check for membersConnectionId - // after implementing connection id (sync from platform) - // on realtime connection - expect(membersConnectionId.length, equals(0)); - }); - }); -} - -void testRestPresenceHistory(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restPresenceHistory); - late TestControlResponseMessage response; - - late List> historyInitial; - late List> historyDefault; - late List> historyLimit4; - late List> historyLimit2; - late List> historyForwards; - late List> historyWithStart; - late List> historyWithStartAndEnd; - late List> historyAll; - - setUpAll(() async { - response = await requestDataForTest(getDriver(), message); - historyInitial = transformListResponse(response.payload['historyInitial']); - historyDefault = transformListResponse(response.payload['historyDefault']); - historyLimit4 = transformListResponse(response.payload['historyLimit4']); - historyLimit2 = transformListResponse(response.payload['historyLimit2']); - historyForwards = - transformListResponse(response.payload['historyForwards']); - historyWithStart = transformListResponse( - response.payload['historyWithStart'], - ).reversed.toList(); - historyWithStartAndEnd = transformListResponse( - response.payload['historyWithStartAndEnd'], - ); - historyAll = transformListResponse(response.payload['historyAll']); - }); - - group('rest#channels#channel#presence#history', () { - test('queries all entries by default', () { - expect(historyInitial.length, equals(0)); - expect(historyAll.length, equals(10)); - - expect(historyDefault.length, equals(8)); - testAllPresenceMessagesHistory(historyDefault.reversed.toList()); - }); - test('queries all entries by paginating with limit', () { - expect(historyLimit4.length, equals(8)); - expect(historyLimit2.length, equals(8)); - testAllPresenceMessagesHistory(historyLimit4.reversed.toList()); - testAllPresenceMessagesHistory(historyLimit2.reversed.toList()); - }); - test( - 'queries entries in reverse order with direction set to "forward"', - () { - expect(historyForwards.length, equals(8)); - testAllPresenceMessagesHistory(historyForwards.toList()); - }, - ); - test('returns entries created after specified time', () { - expect(historyWithStart.length, equals(2)); - expect(historyWithStart[0]['clientId'], equals('someClientId')); - expect(historyWithStart[0]['data'], equals('enter-start-time')); - expect(historyWithStart[1]['clientId'], equals('someClientId')); - expect(historyWithStart[1]['data'], equals('leave-end-time')); - }); - test('returns entries created between specified start and end', () { - expect(historyWithStartAndEnd.length, equals(1)); - expect(historyWithStartAndEnd[0]['clientId'], equals('someClientId')); - expect(historyWithStartAndEnd[0]['data'], equals('enter-start-time')); - }); - }); -} - -void testCapabilityMatrix(FlutterDriver Function() getDriver) { - const message = TestControlMessage(TestName.restCapabilities); - late TestControlResponseMessage response; - late List> capabilityMatrix; - - setUpAll(() async { - response = await requestDataForTest(getDriver(), message); - capabilityMatrix = transformListResponse(response.payload['matrix']); - }); - - test('capabilitySpec', () { - for (final entry in capabilityMatrix) { - final capabilities = entry['channelCapabilities'] as List; - final publishEnabled = capabilities.contains('publish'); - final historyEnabled = capabilities.contains('history'); - final subscribeEnabled = capabilities.contains('subscribe'); - - final publishException = entry['publishException']; - final historyException = entry['historyException']; - final presenceException = entry['presenceException']; - final presenceHistoryException = entry['presenceHistoryException']; - - if (publishEnabled) { - expect(publishException == null, true); - } else { - expect(publishException['code'], '40160'); - } - if (historyEnabled) { - expect(historyException == null, true); - expect(presenceHistoryException == null, true); - } else { - expect(historyException['code'], '40160'); - expect(presenceHistoryException['code'], '40160'); - } - if (subscribeEnabled) { - expect(presenceException == null, subscribeEnabled); - } else { - expect(presenceException['code'], '40160'); - } - } - }); -} diff --git a/test_integration/test_driver/test_implementation/utils.dart b/test_integration/test_driver/test_implementation/utils.dart index 26297d222..ed0ee7d8e 100644 --- a/test_integration/test_driver/test_implementation/utils.dart +++ b/test_integration/test_driver/test_implementation/utils.dart @@ -23,24 +23,20 @@ void checkMessageData(int index, Object? data) { case 6: expect( data, - equals( - { - 'hello': 'ably', - 'items': ['1', 2.2, true] - }, - ), + equals({ + 'hello': 'ably', + 'items': ['1', 2.2, true], + }), ); break; case 7: expect( data, - equals( - [ - {'hello': 'ably'}, - 'ably', - 'realtime' - ], - ), + equals([ + {'hello': 'ably'}, + 'ably', + 'realtime', + ]), ); } } @@ -113,6 +109,6 @@ void checkMessageExtras(Map messageExtras) { } List> transformListResponse(dynamic items) => - List.from(items as List) - .map((t) => t as Map) - .toList(); + List.from( + items as List, + ).map((t) => t as Map).toList(); diff --git a/test_integration/test_driver/tests_abstract.dart b/test_integration/test_driver/tests_abstract.dart index e93ceb773..64417bb4c 100644 --- a/test_integration/test_driver/tests_abstract.dart +++ b/test_integration/test_driver/tests_abstract.dart @@ -5,14 +5,8 @@ import 'package:test/test.dart'; import 'tests_config.dart'; -void runTests({ - bool all = false, - Iterable? testModules, -}) { - final tests = getTestsFor( - all: all, - testModules: testModules, - ); +void runTests({bool all = false, Iterable? testModules}) { + final tests = getTestsFor(all: all, testModules: testModules); late FlutterDriver driver; @@ -32,10 +26,7 @@ void runTests({ for (final testModule in tests.keys) { final testModuleName = EnumToString.convertToString(testModule); - tests[testModule]!.forEach(( - testGroupName, - testFunction, - ) { + tests[testModule]!.forEach((testGroupName, testFunction) { group( 'Module: $testModuleName. Group: $testGroupName. Test: ', () => testFunction(getDriver), diff --git a/test_integration/test_driver/tests_config.dart b/test_integration/test_driver/tests_config.dart index ecf9a293a..0762b1a97 100644 --- a/test_integration/test_driver/tests_config.dart +++ b/test_integration/test_driver/tests_config.dart @@ -3,62 +3,47 @@ import 'test_implementation/basic_platform_tests.dart'; import 'test_implementation/crypto_tests.dart'; import 'test_implementation/helper_tests.dart'; import 'test_implementation/realtime_tests.dart'; -import 'test_implementation/rest_tests.dart'; -enum TestModules { basicTests, helperTests, rest, realtime, crypto } +enum TestModules { basicTests, helperTests, realtime, crypto } final _tests = >{ - TestModules.basicTests: { - 'should return Platform and Ably version': testPlatformAndAblyVersion, - 'should provision AppKey': testDemoDependencies, - }, - TestModules.helperTests: { - 'should report unhandled exception': testShouldReportUnhandledException, - }, - TestModules.rest: { - 'should publish': testRestPublish, - 'should publish with tokenRequest': testRestRequestTokenPublish, - 'should publish encrypted': testRestEncryptedPublish, - 'should retrieve history': testRestHistory, - 'should retrieve history with auth callback': - testRestHistoryWithAuthCallback, - 'should retrieve time': testRestTime, - 'conforms to publish spec': testRestPublishSpec, - 'conforms to publish spec when encrypted': testRestEncryptedPublishSpec, - 'should publish with AuthCallback': testRestPublishWithAuthCallback, - 'should get Presence Members': testRestPresenceGet, - 'should get Presence History': testRestPresenceHistory, - 'conforms to capabilitySpec': testCapabilityMatrix, - }, - TestModules.realtime: { - 'should publish': testRealtimePublish, - 'should publish encrypted': testRealtimeEncryptedPublish, - 'conforms to publish spec': testRealtimePublishSpec, - 'conforms to publish spec when encrypted': testRealtimeEncryptedPublishSpec, - 'should subscribe to connection and channel': testRealtimeEvents, - 'should subscribe to messages': testRealtimeSubscribe, - 'should retrieve history': testRealtimeHistory, - 'should retrieve history with auth callback': - testRealtimeHistoryWithAuthCallback, - 'should retrieve time': testRealtimeTime, - 'realtime#channels#channel#presence#get': testRealtimePresenceGet, - 'realtime#channels#channel#presence#history': testRealtimePresenceHistory, - 'should enter, update and leave Presence': testRealtimeEnterUpdateLeave, - 'should subscribe to channel presence': testRealtimePresenceSubscription, - }, - TestModules.crypto: { - 'should generate random crypto key': testCryptoGenerateRandomKey, - 'should validate supported key lengths': testCryptoEnsureSupportedKeyLength, - 'should get default params': testCryptoGetDefaultParams, - } -}; + TestModules.basicTests: { + 'should return Platform and Ably version': testPlatformAndAblyVersion, + 'should provision AppKey': testDemoDependencies, + }, + TestModules.helperTests: { + 'should report unhandled exception': testShouldReportUnhandledException, + }, + TestModules.realtime: { + 'should publish': testRealtimePublish, + 'should publish encrypted': testRealtimeEncryptedPublish, + 'conforms to publish spec': testRealtimePublishSpec, + 'conforms to publish spec when encrypted': + testRealtimeEncryptedPublishSpec, + 'should subscribe to connection and channel': testRealtimeEvents, + 'should subscribe to messages': testRealtimeSubscribe, + 'should retrieve history': testRealtimeHistory, + 'should retrieve history with auth callback': + testRealtimeHistoryWithAuthCallback, + 'should retrieve time': testRealtimeTime, + 'realtime#channels#channel#presence#get': testRealtimePresenceGet, + 'realtime#channels#channel#presence#history': + testRealtimePresenceHistory, + 'should enter, update and leave Presence': testRealtimeEnterUpdateLeave, + 'should subscribe to channel presence': + testRealtimePresenceSubscription, + }, + TestModules.crypto: { + 'should generate random crypto key': testCryptoGenerateRandomKey, + 'should validate supported key lengths': + testCryptoEnsureSupportedKeyLength, + 'should get default params': testCryptoGetDefaultParams, + }, + }; Map> - getTestsFor({ - bool all = false, - Iterable? testModules, -}) { +getTestsFor({bool all = false, Iterable? testModules}) { assert(testModules != null || all != false); late Iterable _testModules; if (all) {