From 2ab8046ebc7e37e33cdf8d708b754535c84048c7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 17 Sep 2026 15:01:00 +0100 Subject: [PATCH 01/14] feat!: remove the REST client The SDK now offers a realtime interface only. `Rest`, `RestChannel`, `RestChannels`, `RestPresence`, `RestAuth` and the `Rest*` option and param types are gone, along with their platform methods, codec types and Android/iOS handlers. `Push` and `PushChannel` now require a `realtime` client, and `Message`/`PresenceMessage.fromEncoded` take `RealtimeChannelOptions`. UPDATING.md documents the migration. Two pieces of pre-existing drift had to be fixed to regenerate the platform constants: - The codegen templates used `_` as a lambda parameter, which is a wildcard in current Dart, so `bin/codegen.dart` no longer ran. - The generated constants had been hand-edited with entries missing from the codegen context (`connectionRecoveryKey`, the `ConnectionStateChange` connection id/key, and three Java-only `ClientOptions` timeout keys); these are now in the context so regeneration reproduces them on all three platforms. Tests: the rest unit and integration tests are deleted; the shared `Channels` test moves to `RealtimeChannels`, the plugin test covers client creation over `Realtime`, and the realtime auth tests bootstrap their tokens from a non-auto-connecting `Realtime` client. Co-Authored-By: Claude Opus 5 (1M context) --- API_REF_INTRO_BLURB.md | 4 +- PushNotifications.md | 4 +- UPDATING.md | 8 + .../flutter/plugin/AblyInstanceStore.java | 57 +- .../ably/flutter/plugin/AblyMessageCodec.java | 59 -- .../flutter/plugin/AblyMethodCallHandler.java | 168 +----- .../flutter/plugin/AuthMethodHandler.java | 25 +- .../plugin/generated/PlatformConstants.java | 65 +-- .../plugin/types/PlatformClientOptions.java | 2 +- bin/codegen_context.dart | 53 +- bin/templates/platformconstants.dart.dart | 12 +- bin/templates/platformconstants.h.dart | 10 +- bin/templates/platformconstants.java.dart | 10 +- bin/templates/platformconstants.m.dart | 8 +- example/lib/main.dart | 3 - .../push_notification_service.dart | 95 +--- example/lib/ui/ably_service.dart | 13 +- example/lib/ui/realtime_sliver.dart | 4 +- example/lib/ui/rest_sliver.dart | 129 ----- ios/Classes/AblyFlutter.m | 218 +------- ios/Classes/AblyInstanceStore.h | 5 - ios/Classes/AblyInstanceStore.m | 15 - ios/Classes/codec/AblyFlutterReader.m | 35 -- ios/Classes/codec/AblyFlutterWriter.m | 4 +- ios/Classes/codec/AblyPlatformConstants.h | 65 +-- ios/Classes/codec/AblyPlatformConstants.m | 34 +- ios/Classes/codec/CryptoCodec.swift | 9 - ios/Classes/handlers/AuthHandlers.swift | 76 --- ios/Classes/handlers/PushHandlers.swift | 23 +- lib/ably_flutter.dart | 1 - .../authentication/src/client_options.dart | 4 +- lib/src/common/src/channels.dart | 6 +- lib/src/crypto/src/cipher_params.dart | 2 +- lib/src/generated/platform_constants.dart | 68 +-- lib/src/message/src/message.dart | 4 +- lib/src/message/src/presence_message.dart | 4 +- lib/src/platform/platform.dart | 4 - lib/src/platform/src/ably_message.dart | 2 +- lib/src/platform/src/codec.dart | 47 -- lib/src/platform/src/method_call_handler.dart | 18 +- lib/src/platform/src/platform_object.dart | 2 +- lib/src/platform/src/realtime/realtime.dart | 3 +- .../src/realtime/realtime_channel.dart | 2 +- lib/src/platform/src/rest/rest.dart | 66 --- lib/src/platform/src/rest/rest_channel.dart | 81 --- lib/src/platform/src/rest/rest_channels.dart | 34 -- lib/src/platform/src/rest/rest_presence.dart | 60 -- lib/src/push_notifications/src/push.dart | 24 +- .../push_notifications/src/push_channel.dart | 21 +- lib/src/realtime/src/channel_event.dart | 2 +- lib/src/realtime/src/channel_mode.dart | 3 +- lib/src/realtime/src/channel_state.dart | 2 +- lib/src/realtime/src/channel_state_event.dart | 3 +- lib/src/rest/rest.dart | 3 - lib/src/rest/src/rest_auth.dart | 39 -- lib/src/rest/src/rest_channel_options.dart | 23 - lib/src/rest/src/rest_history_params.dart | 43 -- lib/src/rest/src/rest_presence_params.dart | 25 - test/ably_flutter_plugin_test.dart | 35 +- test/mock_method_call_manager.dart | 23 - test/{rest => realtime}/channels_test.dart | 8 +- test/rest/channel_test.dart | 140 ----- test_integration/lib/config/test_factory.dart | 30 - test_integration/lib/config/test_names.dart | 20 - .../realtime_auth_client_id_test.dart | 3 +- .../test/realtime/realtime_auth_url_test.dart | 3 +- .../realtime/realtime_authorize_test.dart | 3 +- .../test/rest/rest_auth_client_id_test.dart | 39 -- .../lib/test/rest/rest_authorize_test.dart | 49 -- .../lib/test/rest/rest_capability_test.dart | 69 --- .../rest/rest_create_token_request_test.dart | 44 -- .../rest/rest_encrypted_publish_test.dart | 140 ----- .../lib/test/rest/rest_history_test.dart | 72 --- .../rest_history_with_auth_callback_test.dart | 36 -- .../lib/test/rest/rest_presence_get_test.dart | 81 --- .../test/rest/rest_presence_history_test.dart | 97 ---- .../lib/test/rest/rest_publish_test.dart | 143 ----- .../rest_publish_with_auth_callback_test.dart | 29 - .../test/rest/rest_request_token_test.dart | 33 -- .../lib/test/rest/rest_time_test.dart | 27 - test_integration/lib/test_dispatcher.dart | 6 +- test_integration/lib/utils/rest.dart | 52 -- .../test_implementation/rest_tests.dart | 517 ------------------ .../test_driver/tests_config.dart | 18 +- 84 files changed, 211 insertions(+), 3315 deletions(-) delete mode 100644 example/lib/ui/rest_sliver.dart delete mode 100644 lib/src/platform/src/rest/rest.dart delete mode 100644 lib/src/platform/src/rest/rest_channel.dart delete mode 100644 lib/src/platform/src/rest/rest_channels.dart delete mode 100644 lib/src/platform/src/rest/rest_presence.dart delete mode 100644 lib/src/rest/rest.dart delete mode 100644 lib/src/rest/src/rest_auth.dart delete mode 100644 lib/src/rest/src/rest_channel_options.dart delete mode 100644 lib/src/rest/src/rest_history_params.dart delete mode 100644 lib/src/rest/src/rest_presence_params.dart rename test/{rest => realtime}/channels_test.dart (94%) delete mode 100644 test/rest/channel_test.dart delete mode 100644 test_integration/lib/test/rest/rest_auth_client_id_test.dart delete mode 100644 test_integration/lib/test/rest/rest_authorize_test.dart delete mode 100644 test_integration/lib/test/rest/rest_capability_test.dart delete mode 100644 test_integration/lib/test/rest/rest_create_token_request_test.dart delete mode 100644 test_integration/lib/test/rest/rest_encrypted_publish_test.dart delete mode 100644 test_integration/lib/test/rest/rest_history_test.dart delete mode 100644 test_integration/lib/test/rest/rest_history_with_auth_callback_test.dart delete mode 100644 test_integration/lib/test/rest/rest_presence_get_test.dart delete mode 100644 test_integration/lib/test/rest/rest_presence_history_test.dart delete mode 100644 test_integration/lib/test/rest/rest_publish_test.dart delete mode 100644 test_integration/lib/test/rest/rest_publish_with_auth_callback_test.dart delete mode 100644 test_integration/lib/test/rest/rest_request_token_test.dart delete mode 100644 test_integration/lib/test/rest/rest_time_test.dart delete mode 100644 test_integration/lib/utils/rest.dart delete mode 100644 test_integration/test_driver/test_implementation/rest_tests.dart 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/PushNotifications.md b/PushNotifications.md index 6ecf4acda..f9cce2edf 100644 --- a/PushNotifications.md +++ b/PushNotifications.md @@ -97,7 +97,7 @@ If you invoke any methods from the `ably_flutter` package before calling `runApp ### 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.Realtime(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. @@ -178,7 +178,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. diff --git a/UPDATING.md b/UPDATING.md index e28afd24c..da11106d9 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -2,6 +2,14 @@ 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 REST client has been removed. Use the realtime client instead: + - Replace `ably.Rest(options: clientOptions)` / `ably.Rest.fromKey(key)` with `ably.Realtime(options: clientOptions)` / `ably.Realtime.fromKey(key)`, and use `realtime.channels`, `realtime.auth`, `realtime.push` and `realtime.time()` 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. + ## [Upgrading from v1.2.13] - Updated SDK constraint to `>=2.14.0 <3.0.0` 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..710831a5d 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyInstanceStore.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyInstanceStore.java @@ -8,8 +8,6 @@ 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; @@ -31,7 +29,6 @@ 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> paginatedResults = new LongSparseArray<>(); private final AtomicLong nextHandle = new AtomicLong(1); @@ -43,8 +40,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 { /** @@ -54,25 +51,13 @@ 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. * @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. * @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. */ @@ -92,15 +77,6 @@ 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(); @@ -123,36 +99,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) { 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; } @@ -183,7 +139,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..8762f51ea 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java @@ -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; @@ -454,16 +447,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 +503,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 +530,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..83a32c311 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java @@ -25,7 +25,6 @@ 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; @@ -65,15 +64,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 +80,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); @@ -178,91 +153,6 @@ private void resetAblyClients(@NonNull MethodCall call, @NonNull MethodChannel.R 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]; - }; - } - 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)); - } - - 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); - result.success(null); - } - private Callback> paginatedResponseHandler(@NonNull MethodChannel.Result result, Integer handle) { return new Callback>() { @Override @@ -278,45 +168,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); @@ -554,11 +405,6 @@ 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); @@ -693,12 +539,6 @@ private void pushDevice(@NonNull MethodCall call, @NonNull MethodChannel.Result 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..b16d10eb9 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AuthMethodHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/AuthMethodHandler.java @@ -16,8 +16,6 @@ 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/generated/PlatformConstants.java b/android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java index 06b0e2bf3..08dcdfe40 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"; @@ -195,10 +177,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 +330,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 +338,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/types/PlatformClientOptions.java b/android/src/main/java/io/ably/flutter/plugin/types/PlatformClientOptions.java index d123dbb22..19728db41 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 @@ -7,7 +7,7 @@ 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/bin/codegen_context.dart b/bin/codegen_context.dart index 3b91e3079..e2f479702 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'}, @@ -100,7 +83,6 @@ const List> _platformMethods = [ {'name': 'releaseRealtimeChannel', 'value': 'releaseRealtimeChannel'}, {'name': 'realtimeHistory', 'value': 'realtimeHistory'}, {'name': 'realtimeTime', 'value': 'realtimeTime'}, - {'name': 'restTime', 'value': 'restTime'}, {'name': 'realtimeAuthAuthorize', 'value': 'realtimeAuthAuthorize'}, { 'name': 'realtimeAuthCreateTokenRequest', @@ -108,6 +90,7 @@ const List> _platformMethods = [ }, {'name': 'realtimeAuthRequestToken', 'value': 'realtimeAuthRequestToken'}, {'name': 'realtimeAuthGetClientId', 'value': 'realtimeAuthGetClientId'}, + {'name': 'connectionRecoveryKey', 'value': 'connectionRecoveryKey'}, // Push Notifications {'name': 'pushActivate', 'value': 'pushActivate'}, @@ -244,6 +227,9 @@ const List> _objects = [ 'recover', 'environment', 'idempotentRestPublishing', + 'disconnectedRetryTimeout', + 'suspendedRetryTimeout', + 'httpMaxRetryDuration', 'httpOpenTimeout', 'httpRequestTimeout', 'httpMaxRetryCount', @@ -257,10 +243,6 @@ const List> _objects = [ 'dartVersion', ] }, - { - 'name': 'RestChannelOptions', - 'properties': ['cipherParams'] - }, { 'name': 'RealtimeChannelOptions', 'properties': ['params', 'modes', 'cipherParams'] @@ -372,7 +354,15 @@ const List> _objects = [ }, { 'name': 'ConnectionStateChange', - 'properties': ['current', 'previous', 'event', 'retryIn', 'reason'] + 'properties': [ + 'current', + 'previous', + 'event', + 'retryIn', + 'reason', + 'connectionId', + 'connectionKey' + ] }, { 'name': 'ChannelStateChange', @@ -408,15 +398,6 @@ const List> _objects = [ 'name': 'PaginatedResult', 'properties': ['items', 'type', 'hasNext'] }, - { - 'name': 'RestHistoryParams', - 'properties': [ - 'start', - 'end', - 'direction', - 'limit', - ] - }, { 'name': 'RealtimeHistoryParams', 'properties': [ @@ -427,14 +408,6 @@ const List> _objects = [ 'untilAttach', ] }, - { - 'name': 'RestPresenceParams', - 'properties': [ - 'limit', - 'clientId', - 'connectionId', - ] - }, { 'name': 'RealtimePresenceParams', 'properties': [ diff --git a/bin/templates/platformconstants.dart.dart b/bin/templates/platformconstants.dart.dart index 7864dcde3..4f46c5b33 100644 --- a/bin/templates/platformconstants.dart.dart +++ b/bin/templates/platformconstants.dart.dart @@ -4,16 +4,16 @@ 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..45f75a43f 100644 --- a/bin/templates/platformconstants.h.dart +++ b/bin/templates/platformconstants.h.dart @@ -2,16 +2,16 @@ 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..7746af19f 100644 --- a/bin/templates/platformconstants.java.dart +++ b/bin/templates/platformconstants.java.dart @@ -5,16 +5,16 @@ 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..c2def17d4 100644 --- a/bin/templates/platformconstants.m.dart +++ b/bin/templates/platformconstants.m.dart @@ -3,9 +3,9 @@ String $(Map c) => ''' // 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/lib/main.dart b/example/lib/main.dart index d3057f547..b03292aa4 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'; @@ -44,8 +43,6 @@ class AblyFlutterExampleApp extends StatelessWidget { const Divider(), RealtimeSliver(ablyService), const Divider(), - RestSliver(ablyService.rest), - const Divider(), PushNotificationsSliver(ablyService.pushNotificationService) ]), ), diff --git a/example/lib/push_notifications/push_notification_service.dart b/example/lib/push_notifications/push_notification_service.dart index 4769a7e64..ed88a560f 100644 --- a/example/lib/push_notifications/push_notification_service.dart +++ b/example/lib/push_notifications/push_notification_service.dart @@ -8,19 +8,12 @@ import 'package:ably_flutter_example/push_notifications/push_notification_messag import 'package:rxdart/rxdart.dart'; class PushNotificationService { - final bool useRealtimeClient; final ably.Realtime _realtime; - final ably.Rest _rest; 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) { @@ -75,29 +68,17 @@ class PushNotificationService { 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); - } + 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(); @@ -107,22 +88,11 @@ class PushNotificationService { 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 +131,20 @@ 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 +159,11 @@ 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. diff --git a/example/lib/ui/ably_service.dart b/example/lib/ui/ably_service.dart index d812df3d9..7b2652d98 100644 --- a/example/lib/ui/ably_service.dart +++ b/example/lib/ui/ably_service.dart @@ -5,7 +5,6 @@ import 'package:ably_flutter_example/ui/api_key_service.dart'; class AblyService { late final ably.Realtime realtime; - late final ably.Rest rest; late final PushNotificationService pushNotificationService; late final ApiKeyProvision apiKeyProvision; @@ -21,17 +20,7 @@ class AblyService { 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); } } diff --git a/example/lib/ui/realtime_sliver.dart b/example/lib/ui/realtime_sliver.dart index 1f02e0849..242f0c3b2 100644 --- a/example/lib/ui/realtime_sliver.dart +++ b/example/lib/ui/realtime_sliver.dart @@ -272,9 +272,7 @@ class RealtimeSliver extends HookWidget { '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.'), + 'published on the channel.'), ], ), query: () => channel.history( 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/ios/Classes/AblyFlutter.m b/ios/Classes/AblyFlutter.m index d060fe5a0..6aebe5ee2 100644 --- a/ios/Classes/AblyFlutter.m +++ b/ios/Classes/AblyFlutter.m @@ -41,192 +41,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; @@ -267,7 +81,6 @@ -(void)reset; // 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) { @@ -584,21 +397,6 @@ -(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]; @@ -732,13 +530,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 +547,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,10 +569,6 @@ -(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]; @@ -822,13 +608,13 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( } #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/AblyInstanceStore.h b/ios/Classes/AblyInstanceStore.h index 8af0edbc1..cac168bec 100644 --- a/ios/Classes/AblyInstanceStore.h +++ b/ios/Classes/AblyInstanceStore.h @@ -1,7 +1,6 @@ @import Foundation; @import Flutter; -@class ARTRest; @class ARTRealtime; #import "AblyFlutterClientOptions.h" @@ -13,10 +12,6 @@ NS_ASSUME_NONNULL_BEGIN -(NSNumber *) getNextHandle; --(void)setRest:(ARTRest *const)rest with:(NSNumber *const)handle; - --(nullable ARTRest *)restFrom:(NSNumber *)handle; - -(void)setRealtime:(ARTRealtime *const)realtime with:(NSNumber *const)handle; -(nullable ARTRealtime *)realtimeFrom:(NSNumber *)handle; diff --git a/ios/Classes/AblyInstanceStore.m b/ios/Classes/AblyInstanceStore.m index 7ef35ab4d..3b63f99dc 100644 --- a/ios/Classes/AblyInstanceStore.m +++ b/ios/Classes/AblyInstanceStore.m @@ -5,7 +5,6 @@ @implementation AblyInstanceStore { NSMutableDictionary* _realtimeInstances; - NSMutableDictionary* _restInstances; NSMutableDictionary* _paginatedResults; long long _nextHandle; } @@ -30,21 +29,12 @@ -(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 { _realtimeInstances[handle] = realtime; } @@ -69,10 +59,6 @@ -(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]; @@ -84,7 +70,6 @@ -(void)reset { [r close]; } [_realtimeInstances removeAllObjects]; - [_restInstances removeAllObjects]; } @end diff --git a/ios/Classes/codec/AblyFlutterReader.m b/ios/Classes/codec/AblyFlutterReader.m index 49cdc5a84..1f74cc682 100644 --- a/ios/Classes/codec/AblyFlutterReader.m +++ b/ios/Classes/codec/AblyFlutterReader.m @@ -32,11 +32,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, @@ -295,27 +292,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 +314,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/AblyFlutterWriter.m b/ios/Classes/codec/AblyFlutterWriter.m index b2427091a..4d9797f85 100644 --- a/ios/Classes/codec/AblyFlutterWriter.m +++ b/ios/Classes/codec/AblyFlutterWriter.m @@ -48,10 +48,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/codec/AblyPlatformConstants.h b/ios/Classes/codec/AblyPlatformConstants.h index b733bcefd..94983d097 100644 --- a/ios/Classes/codec/AblyPlatformConstants.h +++ b/ios/Classes/codec/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; @@ -169,6 +151,9 @@ 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; @@ -181,9 +166,6 @@ 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 +303,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 +310,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/codec/AblyPlatformConstants.m b/ios/Classes/codec/AblyPlatformConstants.m index 36061c488..421b0ccb9 100644 --- a/ios/Classes/codec/AblyPlatformConstants.m +++ b/ios/Classes/codec/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"; @@ -138,6 +123,9 @@ 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"; @@ -150,9 +138,6 @@ 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 +275,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 +282,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/codec/CryptoCodec.swift b/ios/Classes/codec/CryptoCodec.swift index 106336d95..70434af82 100644 --- a/ios/Classes/codec/CryptoCodec.swift +++ b/ios/Classes/codec/CryptoCodec.swift @@ -37,15 +37,6 @@ public class CryptoCodec: NSObject { } 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 { diff --git a/ios/Classes/handlers/AuthHandlers.swift b/ios/Classes/handlers/AuthHandlers.swift index fdef4657a..a3f61efb2 100644 --- a/ios/Classes/handlers/AuthHandlers.swift +++ b/ios/Classes/handlers/AuthHandlers.swift @@ -83,80 +83,4 @@ public class AuthHandlers: NSObject { }) } - - - @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/PushHandlers.swift b/ios/Classes/handlers/PushHandlers.swift index 7ffdc13ef..d08d4ddcb 100644 --- a/ios/Classes/handlers/PushHandlers.swift +++ b/ios/Classes/handlers/PushHandlers.swift @@ -85,12 +85,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) } } @@ -155,28 +152,23 @@ public class PushHandlers: NSObject { } } - /// Gets the client.push property from ARTRealtime or ARTRest when the call contains a handle. + /// Gets the client.push property from ARTRealtime when the call contains a handle. private static func getPush(instanceStore: AblyInstanceStore, call: FlutterMethodCall, result: @escaping FlutterResult) -> ARTPush? { 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 ARTRealtime /// 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 ARTRealtime 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? { let ablyMessage = call.arguments as! AblyFlutterMessage @@ -200,13 +192,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/lib/ably_flutter.dart b/lib/ably_flutter.dart index 0e4edbc30..28951feda 100644 --- a/lib/ably_flutter.dart +++ b/lib/ably_flutter.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/client_options.dart b/lib/src/authentication/src/client_options.dart index a0bcf46cc..21bf5eef8 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -1,7 +1,7 @@ import 'package:ably_flutter/ably_flutter.dart'; -/// Passes additional client-specific properties to the REST -/// [Rest.new] or the Realtime [Realtime.new] +/// Passes additional client-specific properties to the Realtime +/// [Realtime.new] class ClientOptions extends AuthOptions { /// A client ID, used for identifying this client when publishing messages or /// for presence purposes. diff --git a/lib/src/common/src/channels.dart b/lib/src/common/src/channels.dart index 980cf13d4..4c6d8b068 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: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,7 +37,7 @@ 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 diff --git a/lib/src/crypto/src/cipher_params.dart b/lib/src/crypto/src/cipher_params.dart index d057225bd..7fc3d6247 100644 --- a/lib/src/crypto/src/cipher_params.dart +++ b/lib/src/crypto/src/cipher_params.dart @@ -2,7 +2,7 @@ import 'package:ably_flutter/ably_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/generated/platform_constants.dart b/lib/src/generated/platform_constants.dart index 3c3e2d67e..d0d89ccbe 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'; @@ -184,6 +166,9 @@ class TxClientOptions { 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'; @@ -197,10 +182,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 +335,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 +343,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/message/src/message.dart b/lib/src/message/src/message.dart index d63cae59a..247afaf1e 100644 --- a/lib/src/message/src/message.dart +++ b/lib/src/message/src/message.dart @@ -83,7 +83,7 @@ class Message with ObjectHash { /// data if you have an encrypted channel. Message.fromEncoded( Map jsonObject, [ - RestChannelOptions? channelOptions, + RealtimeChannelOptions? channelOptions, ]) : clientId = jsonObject['clientId'] as String?, connectionId = jsonObject['connectionId'] as String?, _data = MessageData.fromValue(jsonObject['data']), @@ -107,7 +107,7 @@ class Message with ObjectHash { /// allow the library to decrypt the data if you have an encrypted channel. static List fromEncodedArray( List> jsonArray, [ - RestChannelOptions? channelOptions, + RealtimeChannelOptions? channelOptions, ]) => jsonArray.map((e) => Message.fromEncoded(e, channelOptions)).toList(); diff --git a/lib/src/message/src/presence_message.dart b/lib/src/message/src/presence_message.dart index e7b25c526..d9d647642 100644 --- a/lib/src/message/src/presence_message.dart +++ b/lib/src/message/src/presence_message.dart @@ -92,7 +92,7 @@ class PresenceMessage with ObjectHash { /// having to parse the encoding string. PresenceMessage.fromEncoded( Map jsonObject, [ - RestChannelOptions? channelOptions, + RealtimeChannelOptions? channelOptions, ]) : id = jsonObject['id'] as String?, action = PresenceAction.values.firstWhere((e) => e.toString().split('.')[1] == jsonObject['action'] as String?), @@ -120,7 +120,7 @@ class PresenceMessage with ObjectHash { /// having to parse the encoding string. static List fromEncodedArray( List> jsonArray, [ - RestChannelOptions? channelOptions, + RealtimeChannelOptions? channelOptions, ]) => jsonArray .map((jsonObject) => PresenceMessage.fromEncoded( diff --git a/lib/src/platform/platform.dart b/lib/src/platform/platform.dart index 2ba998cf3..f22a90fd3 100644 --- a/lib/src/platform/platform.dart +++ b/lib/src/platform/platform.dart @@ -11,8 +11,4 @@ export 'src/realtime/presence.dart'; export 'src/realtime/realtime.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/src/ably_message.dart b/lib/src/platform/src/ably_message.dart index 6588d80ba..136024c0e 100644 --- a/lib/src/platform/src/ably_message.dart +++ b/lib/src/platform/src/ably_message.dart @@ -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 diff --git a/lib/src/platform/src/codec.dart b/lib/src/platform/src/codec.dart index 953d7d5b7..0b48b4060 100644 --- a/lib/src/platform/src/codec.dart +++ b/lib/src/platform/src/codec.dart @@ -79,8 +79,6 @@ class Codec extends StandardMessageCodec { _CodecPair(_encodeTokenDetails, _decodeTokenDetails), CodecTypes.tokenRequest: _CodecPair(_encodeTokenRequest, _decodeTokenRequest), - CodecTypes.restChannelOptions: - _CodecPair(_encodeRestChannelOptions, null), CodecTypes.realtimeChannelOptions: _CodecPair( _encodeRealtimeChannelOptions, null, @@ -90,10 +88,6 @@ class Codec extends StandardMessageCodec { _CodecPair>(null, _decodePaginatedResult), CodecTypes.realtimeHistoryParams: _CodecPair(_encodeRealtimeHistoryParams, null), - CodecTypes.restHistoryParams: - _CodecPair(_encodeRestHistoryParams, null), - CodecTypes.restPresenceParams: - _CodecPair(_encodeRestPresenceParams, null), CodecTypes.realtimePresenceParams: _CodecPair( _encodeRealtimePresenceParams, null, @@ -173,18 +167,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) { @@ -343,16 +331,6 @@ class Codec extends StandardMessageCodec { 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) { @@ -435,31 +413,6 @@ class Codec extends StandardMessageCodec { } } - /// @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 jsonMap = {}; diff --git a/lib/src/platform/src/method_call_handler.dart b/lib/src/platform/src/method_call_handler.dart index ac05625d8..f006cf5ef 100644 --- a/lib/src/platform/src/method_call_handler.dart +++ b/lib/src/platform/src/method_call_handler.dart @@ -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: @@ -40,20 +38,6 @@ class AblyMethodCallHandler { }); } - /// @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 { diff --git a/lib/src/platform/src/platform_object.dart b/lib/src/platform/src/platform_object.dart index 881f7be32..60e6c5246 100644 --- a/lib/src/platform/src/platform_object.dart +++ b/lib/src/platform/src/platform_object.dart @@ -42,7 +42,7 @@ 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 { diff --git a/lib/src/platform/src/realtime/realtime.dart b/lib/src/platform/src/realtime/realtime.dart index d186dbdc7..2a6fa5742 100644 --- a/lib/src/platform/src/realtime/realtime.dart +++ b/lib/src/platform/src/realtime/realtime.dart @@ -6,8 +6,7 @@ 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. +/// A client that offers a realtime API to interact with Ably. 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. diff --git a/lib/src/platform/src/realtime/realtime_channel.dart b/lib/src/platform/src/realtime/realtime_channel.dart index 309a95c9d..386d9a535 100644 --- a/lib/src/platform/src/realtime/realtime_channel.dart +++ b/lib/src/platform/src/realtime/realtime_channel.dart @@ -24,7 +24,7 @@ class RealtimeChannel extends PlatformObject { RealtimePresence get presence => _presence; /// @nodoc - /// instantiates with [Rest], [name] and [RealtimeChannelOptions] + /// instantiates with [Realtime], [name] and [RealtimeChannelOptions] /// /// sets default [state] to [ChannelState.initialized] and start listening /// for updates to the channel [state]/ 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/push_notifications/src/push.dart b/lib/src/push_notifications/src/push.dart index bd1db2d87..55401a780 100644 --- a/lib/src/push_notifications/src/push.dart +++ b/lib/src/push_notifications/src/push.dart @@ -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 Realtime 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. @@ -134,7 +120,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_channel.dart b/lib/src/push_notifications/src/push_channel.dart index 2d134620d..9db01ed41 100644 --- a/lib/src/push_notifications/src/push_channel.dart +++ b/lib/src/push_notifications/src/push_channel.dart @@ -5,30 +5,17 @@ import 'package:ably_flutter/src/platform/platform_internal.dart'; 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 Realtime 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( diff --git a/lib/src/realtime/src/channel_event.dart b/lib/src/realtime/src/channel_event.dart index 07eee0831..dd9ecb3f2 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'; -/// 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..ad3ab9ae4 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'; /// 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..75eec5b1f 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'; -/// 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..bc834eff8 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: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/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/test/ably_flutter_plugin_test.dart b/test/ably_flutter_plugin_test.dart index 9fd9cc46d..8226cc651 100644 --- a/test/ably_flutter_plugin_test.dart +++ b/test/ably_flutter_plugin_test.dart @@ -26,11 +26,10 @@ void main() { case PlatformMethod.getVersion: return _nativeLibraryVersion; - case PlatformMethod.createRest: case PlatformMethod.createRealtime: return ++counter; - case PlatformMethod.publish: + case PlatformMethod.publishRealtimeChannelMessage: case PlatformMethod.connectRealtime: default: return null; @@ -52,33 +51,33 @@ void main() { expect(await version(), _nativeLibraryVersion); }); - test(PlatformMethod.createRest, () async { - const host = 'http://rest.ably.io/'; + test(PlatformMethod.createRealtime, () async { + const host = 'http://realtime.ably.io/'; final o = ClientOptions( - restHost: host, + realtimeHost: host, ); - final rest = Rest(options: o); - expect(await rest.handle, counter); - expect(rest.options.restHost, host); + final realtime = Realtime(options: o); + expect(await realtime.handle, counter); + expect(realtime.options.realtimeHost, host); }); - test('createRestWithToken', () async { + test('createRealtimeWithToken', () async { const key = 'TEST-KEY'; - final rest = Rest.fromKey(key); - expect(await rest.handle, counter); - expect(rest.options.tokenDetails!.token, key); + final realtime = Realtime.fromKey(key); + expect(await realtime.handle, counter); + expect(realtime.options.tokenDetails!.token, key); }); - test('createRestWithKey', () async { + test('createRealtimeWithKey', () async { const key = 'TEST:KEY'; - final rest = Rest.fromKey(key); - expect(await rest.handle, counter); - expect(rest.options.key, key); + final realtime = Realtime.fromKey(key); + expect(await realtime.handle, counter); + expect(realtime.options.key, key); }); test('publishMessage', () async { - final rest = Rest.fromKey('TEST-KEY'); - await rest.channels.get('test').publish(name: 'name', data: 'data'); + final realtime = Realtime.fromKey('TEST-KEY'); + await realtime.channels.get('test').publish(name: 'name', data: 'data'); expect(1, 1); }); } diff --git a/test/mock_method_call_manager.dart b/test/mock_method_call_manager.dart index 57860137b..9a629d45c 100644 --- a/test/mock_method_call_manager.dart +++ b/test/mock_method_call_manager.dart @@ -35,7 +35,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 +44,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; @@ -90,7 +68,6 @@ class MockMethodCallManager { publishedMessages.add(ablyMessage); return null; - case PlatformMethod.releaseRestChannel: case PlatformMethod.releaseRealtimeChannel: return null; diff --git a/test/rest/channels_test.dart b/test/realtime/channels_test.dart similarity index 94% rename from test/rest/channels_test.dart rename to test/realtime/channels_test.dart index fa3c16d1d..fb6b3a5a9 100644 --- a/test/rest/channels_test.dart +++ b/test/realtime/channels_test.dart @@ -7,19 +7,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 = Realtime.fromKey('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'); 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_integration/lib/config/test_factory.dart b/test_integration/lib/config/test_factory.dart index d86dc8bba..77e278a6a 100644 --- a/test_integration/lib/config/test_factory.dart +++ b/test_integration/lib/config/test_factory.dart @@ -22,19 +22,6 @@ 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, @@ -71,23 +58,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/test/realtime/realtime_auth_client_id_test.dart b/test_integration/lib/test/realtime/realtime_auth_client_id_test.dart index 9575bc6d4..a076a31a0 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 @@ -14,9 +14,10 @@ Future> testRealtimeAuthClientId({ key: appKey, environment: 'sandbox', logLevel: LogLevel.error, + autoConnect: false, ); - final ablyForToken = Rest( + final ablyForToken = Realtime( options: clientOptionsForToken, ); /* get first token */ 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..4c9da4b3a 100644 --- a/test_integration/lib/test/realtime/realtime_auth_url_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_url_test.dart @@ -17,10 +17,11 @@ Future> testCreateRealtimeWithAuthUrl({ key: appKey, environment: 'sandbox', logLevel: LogLevel.error, + autoConnect: false, fallbackHosts: ['a.ably-realtime.com', 'b.ably-realtime.com'], ); - final ablyForToken = Rest( + final ablyForToken = Realtime( options: clientOptionsForToken, ); final tokenDetails = await ablyForToken.auth.requestToken(); diff --git a/test_integration/lib/test/realtime/realtime_authorize_test.dart b/test_integration/lib/test/realtime/realtime_authorize_test.dart index c7497c16c..3eef8dcb5 100644 --- a/test_integration/lib/test/realtime/realtime_authorize_test.dart +++ b/test_integration/lib/test/realtime/realtime_authorize_test.dart @@ -14,9 +14,10 @@ Future> testRealtimeAuthroize({ key: appKey, environment: 'sandbox', logLevel: LogLevel.error, + autoConnect: false, ); - final ablyForToken = Rest( + final ablyForToken = Realtime( options: clientOptionsForToken, ); /* get first token */ 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..2d934ac0f 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 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/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/tests_config.dart b/test_integration/test_driver/tests_config.dart index ecf9a293a..18517ad35 100644 --- a/test_integration/test_driver/tests_config.dart +++ b/test_integration/test_driver/tests_config.dart @@ -3,9 +3,8 @@ 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 = >{ @@ -16,21 +15,6 @@ final _tests = 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, From eebfe22c954da0f9a4e795e7c3dbc4b21945a0e4 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 18 Sep 2026 19:52:31 +0100 Subject: [PATCH 02/14] refactor!: rename package to ably_pubsub_device_flutter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the rename of this SDK to Ably Pub/Sub, rename the published Dart package from `ably_flutter` to `ably_pubsub_device_flutter`, so the package name matches the product. The rename reaches further than `pubspec.yaml` because several names are derived from the package name: - The library entry point moves to `lib/ably_pubsub_device_flutter.dart`, and every `package:ably_flutter/…` import follows. Import blocks are re-sorted where the new name changes their alphabetical order. - Flutter resolves a plugin's podspec by package name, so the podspec is renamed too. That in turn renames the Swift module, so the ObjC sources now import ``, and the iOS CI job builds the `ably_pubsub_device_flutter-Unit-Tests` scheme CocoaPods generates from it. - `android/settings.gradle` names the Gradle project, and the example's Xcode project names the framework it embeds. Both `Podfile.lock`s are regenerated with `pod install` so their spec checksums are real, and the three `pubspec.lock`s with `flutter pub get`. Method channel names and the `io.ably.ably_flutter.PUSH_*` broadcast actions keep their current values, as wire identifiers shared with the platform side. The example, integration test and test harness packages keep their own names. `UPDATING.md` gains a migration note covering the dependency, the import path and the iOS module. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/skills/release/SKILL.md | 2 +- .github/workflows/flutter_integration.yaml | 4 ++-- .github/workflows/ios_unit_tests.yml | 6 +++--- PushNotifications.md | 6 +++--- README.md | 8 ++++---- UPDATING.md | 4 ++++ analysis_options.yaml | 1 - android/settings.gradle | 2 +- example/ios/Podfile.lock | 10 +++++----- example/ios/Runner.xcodeproj/project.pbxproj | 4 ++-- example/lib/app_provisioning.dart | 4 ++-- example/lib/nested_realtime_events.dart | 3 ++- .../push_notifications/push_notification_handlers.dart | 3 ++- .../push_notification_message_examples.dart | 3 ++- .../push_notifications/push_notification_service.dart | 3 ++- example/lib/ui/ably_service.dart | 3 ++- example/lib/ui/paginated_result_viewer.dart | 3 ++- .../push_notifications_activation_sliver.dart | 3 ++- .../push_notifications_device_information.dart | 3 ++- .../push_notifications_ios_permissions_sliver.dart | 3 ++- .../push_notifications_received_sliver.dart | 3 ++- .../push_notifications_subscriptions_sliver.dart | 3 ++- example/lib/ui/realtime_presence_sliver.dart | 3 ++- example/lib/ui/realtime_sliver.dart | 3 ++- example/lib/ui/system_details_sliver.dart | 3 ++- example/lib/ui/utilities.dart | 3 ++- example/pubspec.lock | 2 +- example/pubspec.yaml | 2 +- ios/Classes/AblyFlutter.m | 2 +- ios/Classes/AblyInstanceStore.m | 2 +- ios/Classes/codec/AblyFlutterReader.m | 2 +- ios/Classes/codec/AblyFlutterWriter.m | 2 +- ios/Classes/handlers/AuthHandlers.swift | 2 +- ...tter.podspec => ably_pubsub_device_flutter.podspec} | 6 +++--- ...ly_flutter.dart => ably_pubsub_device_flutter.dart} | 2 +- lib/src/authentication/src/auth.dart | 2 +- lib/src/authentication/src/auth_options.dart | 2 +- lib/src/authentication/src/client_options.dart | 2 +- lib/src/authentication/src/token_params.dart | 2 +- lib/src/authentication/src/token_request.dart | 2 +- lib/src/common/src/channels.dart | 2 +- lib/src/common/src/http_paginated_response.dart | 2 +- lib/src/crypto/src/cipher_params.dart | 2 +- lib/src/crypto/src/crypto.dart | 4 ++-- lib/src/error/src/ably_exception.dart | 2 +- lib/src/logging/src/log_handler.dart | 2 +- lib/src/logging/src/log_level.dart | 2 +- lib/src/message/src/delta_extras.dart | 4 ++-- lib/src/message/src/message.dart | 4 ++-- lib/src/message/src/message_extras.dart | 6 +++--- lib/src/message/src/presence_message.dart | 4 ++-- lib/src/platform/platform_internal.dart | 5 +++-- lib/src/platform/src/ably_message.dart | 2 +- .../src/background_android_isolate_platform.dart | 4 ++-- lib/src/platform/src/cipher_params_internal.dart | 2 +- lib/src/platform/src/codec.dart | 4 ++-- lib/src/platform/src/info.dart | 4 ++-- lib/src/platform/src/method_call_handler.dart | 4 ++-- lib/src/platform/src/paginated_result.dart | 4 ++-- lib/src/platform/src/platform.dart | 4 ++-- lib/src/platform/src/platform_object.dart | 2 +- .../platform/src/push_activation_events_internal.dart | 2 +- .../src/push_notification_events_internal.dart | 4 ++-- lib/src/platform/src/realtime/connection.dart | 4 ++-- lib/src/platform/src/realtime/presence.dart | 4 ++-- lib/src/platform/src/realtime/realtime.dart | 6 +++--- lib/src/platform/src/realtime/realtime_channel.dart | 4 ++-- lib/src/platform/src/realtime/realtime_channels.dart | 4 ++-- lib/src/platform/src/streams_channel.dart | 2 +- lib/src/push_notifications/src/admin/push_admin.dart | 2 +- .../src/admin/push_device_registrations.dart | 2 +- lib/src/push_notifications/src/device_details.dart | 2 +- .../push_notifications/src/device_push_details.dart | 2 +- lib/src/push_notifications/src/device_push_state.dart | 2 +- .../src/device_registration_params.dart | 2 +- .../src/ios_notification_settings.dart | 2 +- lib/src/push_notifications/src/local_device.dart | 2 +- lib/src/push_notifications/src/notification.dart | 2 +- lib/src/push_notifications/src/push.dart | 4 ++-- .../push_notifications/src/push_activation_events.dart | 2 +- lib/src/push_notifications/src/push_channel.dart | 4 ++-- .../push_notifications/src/push_channel_params.dart | 2 +- .../src/push_channel_subscription_params.dart | 2 +- .../src/push_channel_subscriptions.dart | 2 +- .../src/push_notification_events.dart | 2 +- lib/src/push_notifications/src/remote_message.dart | 4 ++-- lib/src/realtime/src/channel_event.dart | 2 +- lib/src/realtime/src/channel_mode.dart | 2 +- lib/src/realtime/src/channel_state.dart | 2 +- lib/src/realtime/src/channel_state_event.dart | 2 +- lib/src/realtime/src/connection_event.dart | 2 +- lib/src/realtime/src/connection_state.dart | 2 +- lib/src/realtime/src/connection_state_change.dart | 2 +- lib/src/realtime/src/realtime_auth.dart | 4 ++-- lib/src/realtime/src/realtime_channel_options.dart | 2 +- lib/src/realtime/src/realtime_history_params.dart | 2 +- lib/src/realtime/src/realtime_presence_params.dart | 2 +- lib/src/stats/src/stats.dart | 2 +- lib/src/stats/src/stats_connection_types.dart | 2 +- lib/src/stats/src/stats_message_traffic.dart | 2 +- lib/src/stats/src/stats_message_types.dart | 2 +- pubspec.yaml | 2 +- ...art => ably_pubsub_device_flutter_plugin_test.dart} | 4 ++-- test/mock_method_call_manager.dart | 4 ++-- test/models/client_options.dart | 2 +- test/models/message_test.dart | 2 +- test/models/presence_message_test.dart | 2 +- test/platform/info_test.dart | 3 ++- test/realtime/channel_test.dart | 4 ++-- test/realtime/channels_test.dart | 2 +- test/stable_id_generator_test.dart | 2 +- test_integration/ios/Podfile.lock | 10 +++++----- test_integration/lib/app_provisioning.dart | 4 ++-- test_integration/lib/test/basic_test.dart | 2 +- .../crypto_ensure_supported_key_length_test.dart | 2 +- .../test/crypto/crypto_generate_random_key_test.dart | 2 +- .../lib/test/crypto/crypto_get_default_params.dart | 2 +- .../test/realtime/realtime_auth_client_id_test.dart | 2 +- .../lib/test/realtime/realtime_auth_url_test.dart | 2 +- .../lib/test/realtime/realtime_authorize_test.dart | 2 +- .../test/realtime/realtime_encrypted_publish_test.dart | 2 +- .../lib/test/realtime/realtime_events_test.dart | 2 +- .../lib/test/realtime/realtime_history_test.dart | 2 +- .../realtime_history_with_auth_callback_test.dart | 2 +- .../realtime/realtime_presence_enter_update_leave.dart | 2 +- .../lib/test/realtime/realtime_presence_get.dart | 2 +- .../test/realtime/realtime_presence_history_test.dart | 2 +- .../lib/test/realtime/realtime_presence_subscribe.dart | 2 +- .../lib/test/realtime/realtime_publish_test.dart | 2 +- .../realtime_publish_with_auth_callback_test.dart | 2 +- .../lib/test/realtime/realtime_subscribe.dart | 2 +- .../lib/test/realtime/realtime_time_test.dart | 2 +- test_integration/lib/utils/encoders.dart | 2 +- test_integration/lib/utils/realtime.dart | 2 +- test_integration/pubspec.lock | 2 +- test_integration/pubspec.yaml | 2 +- .../test_driver/test_implementation/crypto_tests.dart | 5 +++-- 137 files changed, 206 insertions(+), 185 deletions(-) rename ios/{ably_flutter.podspec => ably_pubsub_device_flutter.podspec} (83%) rename lib/{ably_flutter.dart => ably_pubsub_device_flutter.dart} (91%) rename test/{ably_flutter_plugin_test.dart => ably_pubsub_device_flutter_plugin_test.dart} (93%) diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index cbc53b12e..aba319242 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -18,7 +18,7 @@ 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`) 3. Regenerate lock files: diff --git a/.github/workflows/flutter_integration.yaml b/.github/workflows/flutter_integration.yaml index b6bb41ebe..33c2ea700 100644 --- a/.github/workflows/flutter_integration.yaml +++ b/.github/workflows/flutter_integration.yaml @@ -21,10 +21,10 @@ jobs: flutter-version: '3.29' 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 diff --git a/.github/workflows/ios_unit_tests.yml b/.github/workflows/ios_unit_tests.yml index 71510f3d2..867f82a0c 100644 --- a/.github/workflows/ios_unit_tests.yml +++ b/.github/workflows/ios_unit_tests.yml @@ -23,10 +23,10 @@ jobs: - 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 + # the ably_pubsub_device_flutter-Unit-Tests scheme) without booting a simulator run: | cd ios - pod lib lint ably_flutter.podspec \ + pod lib lint ably_pubsub_device_flutter.podspec \ --allow-warnings \ --skip-tests \ --validation-dir=/tmp/ably_test_build \ @@ -45,6 +45,6 @@ jobs: run: | xcodebuild test \ -workspace /tmp/ably_test_build/App.xcworkspace \ - -scheme ably_flutter-Unit-Tests \ + -scheme ably_pubsub_device_flutter-Unit-Tests \ -destination "id=$SIMULATOR_ID,arch=arm64" \ CODE_SIGNING_ALLOWED=NO diff --git a/PushNotifications.md b/PushNotifications.md index f9cce2edf..7b2d241ec 100644 --- a/PushNotifications.md +++ b/PushNotifications.md @@ -93,7 +93,7 @@ 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 @@ -162,7 +162,7 @@ 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 @objc class AppDelegate: FlutterAppDelegate { @@ -601,6 +601,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..186eb2900 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) --- @@ -54,17 +54,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). --- diff --git a/UPDATING.md b/UPDATING.md index da11106d9..c517ce379 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -4,6 +4,10 @@ This guide lists the changes needed to upgrade from one version of Ably to a new ## [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`. - The REST client has been removed. Use the realtime client instead: - Replace `ably.Rest(options: clientOptions)` / `ably.Rest.fromKey(key)` with `ably.Realtime(options: clientOptions)` / `ably.Realtime.fromKey(key)`, and use `realtime.channels`, `realtime.auth`, `realtime.push` and `realtime.time()` in place of their `Rest` equivalents. - `RestChannel`, `RestChannels`, `RestPresence`, `RestChannelOptions`, `RestHistoryParams` and `RestPresenceParams` have been removed. Use `RealtimeChannel`, `RealtimeChannels`, `RealtimePresence`, `RealtimeChannelOptions`, `RealtimeHistoryParams` and `RealtimePresenceParams`. diff --git a/analysis_options.yaml b/analysis_options.yaml index f815b7078..ae1b09a80 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -73,7 +73,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/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/example/ios/Podfile.lock b/example/ios/Podfile.lock index ee9865844..ccf7e3196 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2,7 +2,7 @@ PODS: - Ably (1.2.53): - AblyDeltaCodec (= 1.3.3) - msgpack (= 0.4.0) - - ably_flutter (1.2.44): + - ably_pubsub_device_flutter (1.2.44): - Ably (= 1.2.53) - Flutter - AblyDeltaCodec (1.3.3) @@ -16,7 +16,7 @@ PODS: - msgpack (0.4.0) DEPENDENCIES: - - ably_flutter (from `.symlinks/plugins/ably_flutter/ios`) + - ably_pubsub_device_flutter (from `.symlinks/plugins/ably_pubsub_device_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`) @@ -29,8 +29,8 @@ SPEC REPOS: - msgpack EXTERNAL SOURCES: - ably_flutter: - :path: ".symlinks/plugins/ably_flutter/ios" + ably_pubsub_device_flutter: + :path: ".symlinks/plugins/ably_pubsub_device_flutter/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: @@ -42,7 +42,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Ably: 1756d6590a572809c10fc4bcc32e05fe9e7867d7 - ably_flutter: 95785161fc2858c7be524af4334925d30682b3b0 + ably_pubsub_device_flutter: 90395fd33ab6c8423c44369dc5fabe54af8e6ddf AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 96babbb61..519b37005 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -232,7 +232,7 @@ "${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}/ably_pubsub_device_flutter/ably_pubsub_device_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", @@ -251,7 +251,7 @@ "${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}/ably_pubsub_device_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", diff --git a/example/lib/app_provisioning.dart b/example/lib/app_provisioning.dart index 87f035128..7aa1b1c42 100644 --- a/example/lib/app_provisioning.dart +++ b/example/lib/app_provisioning.dart @@ -2,13 +2,13 @@ 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 diff --git a/example/lib/nested_realtime_events.dart b/example/lib/nested_realtime_events.dart index 574c14758..ec1c65155 100644 --- a/example/lib/nested_realtime_events.dart +++ b/example/lib/nested_realtime_events.dart @@ -3,7 +3,8 @@ 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 diff --git a/example/lib/push_notifications/push_notification_handlers.dart b/example/lib/push_notifications/push_notification_handlers.dart index 7dbbcc9c7..73dfe776e 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'; diff --git a/example/lib/push_notifications/push_notification_message_examples.dart b/example/lib/push_notifications/push_notification_message_examples.dart index abc51b8c6..bbb0be931 100644 --- a/example/lib/push_notifications/push_notification_message_examples.dart +++ b/example/lib/push_notifications/push_notification_message_examples.dart @@ -1,4 +1,5 @@ -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( diff --git a/example/lib/push_notifications/push_notification_service.dart b/example/lib/push_notifications/push_notification_service.dart index ed88a560f..ee4d3a779 100644 --- a/example/lib/push_notifications/push_notification_service.dart +++ b/example/lib/push_notifications/push_notification_service.dart @@ -1,10 +1,11 @@ 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 { diff --git a/example/lib/ui/ably_service.dart b/example/lib/ui/ably_service.dart index 7b2652d98..c4aedee1d 100644 --- a/example/lib/ui/ably_service.dart +++ b/example/lib/ui/ably_service.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/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; diff --git a/example/lib/ui/paginated_result_viewer.dart b/example/lib/ui/paginated_result_viewer.dart index 8be6538ca..03b0c5951 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'; 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..97ee29a86 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'; 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..abcef9286 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 { 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..73a77f0da 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'; 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..066750cb4 100644 --- a/example/lib/ui/push_notifications/push_notifications_received_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_received_sliver.dart @@ -1,6 +1,7 @@ -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 { 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..380f0bb22 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 { diff --git a/example/lib/ui/realtime_presence_sliver.dart b/example/lib/ui/realtime_presence_sliver.dart index de5531e75..96877ed43 100644 --- a/example/lib/ui/realtime_presence_sliver.dart +++ b/example/lib/ui/realtime_presence_sliver.dart @@ -1,9 +1,10 @@ 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'; diff --git a/example/lib/ui/realtime_sliver.dart b/example/lib/ui/realtime_sliver.dart index 242f0c3b2..dbc1b0e0b 100644 --- a/example/lib/ui/realtime_sliver.dart +++ b/example/lib/ui/realtime_sliver.dart @@ -1,12 +1,13 @@ 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'; diff --git a/example/lib/ui/system_details_sliver.dart b/example/lib/ui/system_details_sliver.dart index eabe0ad88..ae106c7bf 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'; diff --git a/example/lib/ui/utilities.dart b/example/lib/ui/utilities.dart index c3926cfe6..2d044998a 100644 --- a/example/lib/ui/utilities.dart +++ b/example/lib/ui/utilities.dart @@ -1,7 +1,8 @@ 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'; diff --git a/example/pubspec.lock b/example/pubspec.lock index 8e79682cc..5c7517383 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: ".." diff --git a/example/pubspec.yaml b/example/pubspec.yaml index bedd473e4..e51dbd61a 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -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/Classes/AblyFlutter.m b/ios/Classes/AblyFlutter.m index 6aebe5ee2..c7a36b4dc 100644 --- a/ios/Classes/AblyFlutter.m +++ b/ios/Classes/AblyFlutter.m @@ -1,7 +1,7 @@ @import Ably; #import "AblyFlutter.h" -#import +#import #import "codec/AblyFlutterReaderWriter.h" #import "AblyFlutterMessage.h" diff --git a/ios/Classes/AblyInstanceStore.m b/ios/Classes/AblyInstanceStore.m index 3b63f99dc..1482d9173 100644 --- a/ios/Classes/AblyInstanceStore.m +++ b/ios/Classes/AblyInstanceStore.m @@ -1,7 +1,7 @@ @import Ably; #import "AblyInstanceStore.h" -#import +#import @implementation AblyInstanceStore { NSMutableDictionary* _realtimeInstances; diff --git a/ios/Classes/codec/AblyFlutterReader.m b/ios/Classes/codec/AblyFlutterReader.m index 1f74cc682..c660d89ea 100644 --- a/ios/Classes/codec/AblyFlutterReader.m +++ b/ios/Classes/codec/AblyFlutterReader.m @@ -3,7 +3,7 @@ #import "AblyFlutterClientOptions.h" #import "AblyFlutterMessage.h" #import "AblyPlatformConstants.h" -#import +#import static ARTLogLevel _logLevel(NSString *const logLevelString) { if ([logLevelString isEqualToString:TxLogLevelEnum_none]) return ARTLogLevelNone; diff --git a/ios/Classes/codec/AblyFlutterWriter.m b/ios/Classes/codec/AblyFlutterWriter.m index 4d9797f85..fec6bb98c 100644 --- a/ios/Classes/codec/AblyFlutterWriter.m +++ b/ios/Classes/codec/AblyFlutterWriter.m @@ -1,6 +1,6 @@ @import Ably; @import UserNotifications; -#import +#import #import "AblyFlutterWriter.h" #import "AblyFlutterMessage.h" #import "AblyFlutterReader.h" diff --git a/ios/Classes/handlers/AuthHandlers.swift b/ios/Classes/handlers/AuthHandlers.swift index a3f61efb2..2f88b4749 100644 --- a/ios/Classes/handlers/AuthHandlers.swift +++ b/ios/Classes/handlers/AuthHandlers.swift @@ -1,6 +1,6 @@ // // AuthHandlers.swift -// ably_flutter +// ably_pubsub_device_flutter // // Created by Ikbal Kaya on 17/03/2023. // diff --git a/ios/ably_flutter.podspec b/ios/ably_pubsub_device_flutter.podspec similarity index 83% rename from ios/ably_flutter.podspec rename to ios/ably_pubsub_device_flutter.podspec index 37f36835d..230ef3b95 100644 --- a/ios/ably_flutter.podspec +++ b/ios/ably_pubsub_device_flutter.podspec @@ -1,6 +1,6 @@ # # 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. +# Run `pod lib lint ably_pubsub_device_flutter.podspec` to validate before publishing. # require 'yaml' @@ -8,7 +8,7 @@ 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.name = 'ably_pubsub_device_flutter' s.version = flutter_package_plugin_version s.summary = 'Ably Cocoa platform support for our Flutter plugin.' s.homepage = 'https://www.ably.com/' @@ -27,7 +27,7 @@ Pod::Spec.new do |s| 'DEFINES_MODULE' => 'YES', 'GCC_PREPROCESSOR_DEFINITIONS' => "FLUTTER_PACKAGE_PLUGIN_VERSION=\\@\\\"#{flutter_package_plugin_version}\\\"" } - s.resource_bundles = {'ably_flutter' => ['Resources/PrivacyInfo.xcprivacy']} + s.resource_bundles = {'ably_pubsub_device_flutter' => ['Resources/PrivacyInfo.xcprivacy']} s.swift_version = '5.0' s.test_spec 'Tests' do |ts| diff --git a/lib/ably_flutter.dart b/lib/ably_pubsub_device_flutter.dart similarity index 91% rename from lib/ably_flutter.dart rename to lib/ably_pubsub_device_flutter.dart index 28951feda..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'; 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..5b2ee2091 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. diff --git a/lib/src/authentication/src/client_options.dart b/lib/src/authentication/src/client_options.dart index 21bf5eef8..048b5570f 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/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'; /// Passes additional client-specific properties to the Realtime /// [Realtime.new] 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..e455a70c8 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 diff --git a/lib/src/common/src/channels.dart b/lib/src/common/src/channels.dart index 4c6d8b068..26a799466 100644 --- a/lib/src/common/src/channels.dart +++ b/lib/src/common/src/channels.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'; /// Creates and destroys [RealtimeChannel] objects. 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 7fc3d6247..d8ffc7698 100644 --- a/lib/src/crypto/src/cipher_params.dart +++ b/lib/src/crypto/src/cipher_params.dart @@ -1,4 +1,4 @@ -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. /// diff --git a/lib/src/crypto/src/crypto.dart b/lib/src/crypto/src/crypto.dart index e0b619b80..b08aa36ed 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. diff --git a/lib/src/error/src/ably_exception.dart b/lib/src/error/src/ably_exception.dart index 99cb21a45..9cc9ac8c2 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 diff --git a/lib/src/logging/src/log_handler.dart b/lib/src/logging/src/log_handler.dart index 2b4d003e9..cda64ce65 100644 --- a/lib/src/logging/src/log_handler.dart +++ b/lib/src/logging/src/log_handler.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; /// Custom handler to handle SDK log messages /// 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..a7b9edacc 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 diff --git a/lib/src/message/src/message.dart b/lib/src/message/src/message.dart index 247afaf1e..fae192dc1 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. diff --git a/lib/src/message/src/message_extras.dart b/lib/src/message/src/message_extras.dart index 59c139047..30005f2e5 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'; diff --git a/lib/src/message/src/presence_message.dart b/lib/src/message/src/presence_message.dart index d9d647642..7288487f0 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. 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_message.dart b/lib/src/platform/src/ably_message.dart index 136024c0e..aa81aa7a9 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 diff --git a/lib/src/platform/src/background_android_isolate_platform.dart b/lib/src/platform/src/background_android_isolate_platform.dart index cf743b3b5..3f01fe936 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 diff --git a/lib/src/platform/src/cipher_params_internal.dart b/lib/src/platform/src/cipher_params_internal.dart index 6543a864e..330ccdb8c 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. diff --git a/lib/src/platform/src/codec.dart b/lib/src/platform/src/codec.dart index 0b48b4060..be59f3e1a 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 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 f006cf5ef..15c7faad4 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 diff --git a/lib/src/platform/src/paginated_result.dart b/lib/src/platform/src/paginated_result.dart index 37b7d49b4..4331ce441 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. diff --git a/lib/src/platform/src/platform.dart b/lib/src/platform/src/platform.dart index 4f30f8d87..2e00089dd 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 diff --git a/lib/src/platform/src/platform_object.dart b/lib/src/platform/src/platform_object.dart index 60e6c5246..00bdc8919 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 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..916618a03 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]. diff --git a/lib/src/platform/src/realtime/connection.dart b/lib/src/platform/src/realtime/connection.dart index b1dce0142..9cc1a2445 100644 --- a/lib/src/platform/src/realtime/connection.dart +++ b/lib/src/platform/src/realtime/connection.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'; /// Enables the management of a connection to Ably. class Connection extends PlatformObject { diff --git a/lib/src/platform/src/realtime/presence.dart b/lib/src/platform/src/realtime/presence.dart index 25bc10cfa..013cafa83 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. diff --git a/lib/src/platform/src/realtime/realtime.dart b/lib/src/platform/src/realtime/realtime.dart index 2a6fa5742..1f1addbac 100644 --- a/lib/src/platform/src/realtime/realtime.dart +++ b/lib/src/platform/src/realtime/realtime.dart @@ -2,9 +2,9 @@ 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'; +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. class Realtime extends PlatformObject { diff --git a/lib/src/platform/src/realtime/realtime_channel.dart b/lib/src/platform/src/realtime/realtime_channel.dart index 386d9a535..779ee52d0 100644 --- a/lib/src/platform/src/realtime/realtime_channel.dart +++ b/lib/src/platform/src/realtime/realtime_channel.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'; /// Enables messages to be published and subscribed to. /// diff --git a/lib/src/platform/src/realtime/realtime_channels.dart b/lib/src/platform/src/realtime/realtime_channels.dart index 093fe6686..4dfce0122 100644 --- a/lib/src/platform/src/realtime/realtime_channels.dart +++ b/lib/src/platform/src/realtime/realtime_channels.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:meta/meta.dart'; /// Creates and destroys [RealtimeChannel] objects. diff --git a/lib/src/platform/src/streams_channel.dart b/lib/src/platform/src/streams_channel.dart index 45b156436..ceba85bd2 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'; 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..41566f547 100644 --- a/lib/src/push_notifications/src/admin/push_device_registrations.dart +++ b/lib/src/push_notifications/src/admin/push_device_registrations.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 push notification registrations with Ably. abstract class PushDeviceRegistrations { 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..64c8d98d1 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. 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..659a376a0 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 diff --git a/lib/src/push_notifications/src/local_device.dart b/lib/src/push_notifications/src/local_device.dart index 8853ab13e..271b25fcf 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. diff --git a/lib/src/push_notifications/src/notification.dart b/lib/src/push_notifications/src/notification.dart index cfe0acfb2..af6064477 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 diff --git a/lib/src/push_notifications/src/push.dart b/lib/src/push_notifications/src/push.dart index 55401a780..89cb1f7e2 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 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 9db01ed41..88b187f4e 100644 --- a/lib/src/push_notifications/src/push_channel.dart +++ b/lib/src/push_notifications/src/push_channel.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 devices to subscribe to push notifications for a channel. class PushChannel extends PlatformObject { 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..0c65f33b6 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 { diff --git a/lib/src/push_notifications/src/push_notification_events.dart b/lib/src/push_notifications/src/push_notification_events.dart index f36ff76ea..8dd88da65 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. diff --git a/lib/src/push_notifications/src/remote_message.dart b/lib/src/push_notifications/src/remote_message.dart index cb7e95f76..64d176c7e 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 diff --git a/lib/src/realtime/src/channel_event.dart b/lib/src/realtime/src/channel_event.dart index dd9ecb3f2..e9de60ca7 100644 --- a/lib/src/realtime/src/channel_event.dart +++ b/lib/src/realtime/src/channel_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 [RealtimeChannel] object. /// diff --git a/lib/src/realtime/src/channel_mode.dart b/lib/src/realtime/src/channel_mode.dart index ad3ab9ae4..686a86db2 100644 --- a/lib/src/realtime/src/channel_mode.dart +++ b/lib/src/realtime/src/channel_mode.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/src/realtime/realtime.dart'; +import 'package:ably_pubsub_device_flutter/src/realtime/realtime.dart'; /// Describes the possible flags used to configure client capabilities, using /// [RealtimeChannelOptions]. diff --git a/lib/src/realtime/src/channel_state.dart b/lib/src/realtime/src/channel_state.dart index 75eec5b1f..bd685a9c0 100644 --- a/lib/src/realtime/src/channel_state.dart +++ b/lib/src/realtime/src/channel_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 possible states of a [RealtimeChannel] /// object. diff --git a/lib/src/realtime/src/channel_state_event.dart b/lib/src/realtime/src/channel_state_event.dart index bc834eff8..d7e9bccd6 100644 --- a/lib/src/realtime/src/channel_state_event.dart +++ b/lib/src/realtime/src/channel_state_event.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 state change information emitted by [RealtimeChannel] objects. 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..4437060ec 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] diff --git a/lib/src/realtime/src/realtime_auth.dart b/lib/src/realtime/src/realtime_auth.dart index c6366d27e..012fbe1ac 100644 --- a/lib/src/realtime/src/realtime_auth.dart +++ b/lib/src/realtime/src/realtime_auth.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'; /// See [Auth] for more information. class RealtimeAuth extends Auth { diff --git a/lib/src/realtime/src/realtime_channel_options.dart b/lib/src/realtime/src/realtime_channel_options.dart index db525c3aa..4be33eb67 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 diff --git a/lib/src/realtime/src/realtime_history_params.dart b/lib/src/realtime/src/realtime_history_params.dart index 7435e20c9..61f139ad3 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. 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/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.yaml b/pubspec.yaml index 8974c9cab..fc46f7e33 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,4 +1,4 @@ -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 diff --git a/test/ably_flutter_plugin_test.dart b/test/ably_pubsub_device_flutter_plugin_test.dart similarity index 93% rename from test/ably_flutter_plugin_test.dart rename to test/ably_pubsub_device_flutter_plugin_test.dart index 8226cc651..e9ce8ef83 100644 --- a/test/ably_flutter_plugin_test.dart +++ b/test/ably_pubsub_device_flutter_plugin_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/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/test/mock_method_call_manager.dart b/test/mock_method_call_manager.dart index 9a629d45c..affa3cfef 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'; diff --git a/test/models/client_options.dart b/test/models/client_options.dart index 49a211220..97461b8ad 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() { diff --git a/test/models/message_test.dart b/test/models/message_test.dart index 2e683e911..9ac0ec7da 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() { diff --git a/test/models/presence_message_test.dart b/test/models/presence_message_test.dart index 9c1fccf76..b32593113 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() { 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/realtime/channel_test.dart b/test/realtime/channel_test.dart index 23c1844e8..c2b956a68 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'; diff --git a/test/realtime/channels_test.dart b/test/realtime/channels_test.dart index fb6b3a5a9..afad0dbd2 100644 --- a/test/realtime/channels_test.dart +++ b/test/realtime/channels_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'; import '../mock_method_call_manager.dart'; 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/ios/Podfile.lock b/test_integration/ios/Podfile.lock index 9b1ef0c69..f91c3e575 100644 --- a/test_integration/ios/Podfile.lock +++ b/test_integration/ios/Podfile.lock @@ -2,7 +2,7 @@ PODS: - Ably (1.2.53): - AblyDeltaCodec (= 1.3.3) - msgpack (= 0.4.0) - - ably_flutter (1.2.44): + - ably_pubsub_device_flutter (1.2.44): - Ably (= 1.2.53) - Flutter - AblyDeltaCodec (1.3.3) @@ -10,7 +10,7 @@ PODS: - msgpack (0.4.0) DEPENDENCIES: - - ably_flutter (from `.symlinks/plugins/ably_flutter/ios`) + - ably_pubsub_device_flutter (from `.symlinks/plugins/ably_pubsub_device_flutter/ios`) - Flutter (from `Flutter`) SPEC REPOS: @@ -20,14 +20,14 @@ SPEC REPOS: - msgpack EXTERNAL SOURCES: - ably_flutter: - :path: ".symlinks/plugins/ably_flutter/ios" + ably_pubsub_device_flutter: + :path: ".symlinks/plugins/ably_pubsub_device_flutter/ios" Flutter: :path: Flutter SPEC CHECKSUMS: Ably: 1756d6590a572809c10fc4bcc32e05fe9e7867d7 - ably_flutter: 95785161fc2858c7be524af4334925d30682b3b0 + ably_pubsub_device_flutter: 90395fd33ab6c8423c44369dc5fabe54af8e6ddf AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 msgpack: c85f6251873059738472ae136951cec5f30f3251 diff --git a/test_integration/lib/app_provisioning.dart b/test_integration/lib/app_provisioning.dart index 652ccfb12..0277c1666 100644 --- a/test_integration/lib/app_provisioning.dart +++ b/test_integration/lib/app_provisioning.dart @@ -2,13 +2,13 @@ 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 diff --git a/test_integration/lib/test/basic_test.dart b/test_integration/lib/test/basic_test.dart index e34593744..a4a58a140 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, 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..ded4fa532 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, 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..e94d381b0 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, 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 a076a31a0..be31775f0 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, 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 4c9da4b3a..e7fd8e663 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({ diff --git a/test_integration/lib/test/realtime/realtime_authorize_test.dart b/test_integration/lib/test/realtime/realtime_authorize_test.dart index 3eef8dcb5..48e92e626 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, 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..fa245a876 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, diff --git a/test_integration/lib/test/realtime/realtime_events_test.dart b/test_integration/lib/test/realtime/realtime_events_test.dart index c98921adb..d6baa6b25 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, diff --git a/test_integration/lib/test/realtime/realtime_history_test.dart b/test_integration/lib/test/realtime/realtime_history_test.dart index f514a52b9..cd0ea95c3 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, 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..f60e2b01c 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, 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..1b0a7ebae 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,8 +1,8 @@ // 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 = >[]; diff --git a/test_integration/lib/test/realtime/realtime_presence_get.dart b/test_integration/lib/test/realtime/realtime_presence_get.dart index 95adb8cd7..28009e04d 100644 --- a/test_integration/lib/test/realtime/realtime_presence_get.dart +++ b/test_integration/lib/test/realtime/realtime_presence_get.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'; final logMessages = >[]; 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..0f3a3e1d5 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, diff --git a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart index c8842bffa..9e9bd8296 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 = >[]; diff --git a/test_integration/lib/test/realtime/realtime_publish_test.dart b/test_integration/lib/test/realtime/realtime_publish_test.dart index 14ed51ea7..839251f6e 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, 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..942532737 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,7 +1,7 @@ -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, diff --git a/test_integration/lib/test/realtime/realtime_subscribe.dart b/test_integration/lib/test/realtime/realtime_subscribe.dart index d81f7d1e4..3004d2875 100644 --- a/test_integration/lib/test/realtime/realtime_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_subscribe.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/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( diff --git a/test_integration/lib/test/realtime/realtime_time_test.dart b/test_integration/lib/test/realtime/realtime_time_test.dart index 873e68f53..619a66e9a 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, diff --git a/test_integration/lib/utils/encoders.dart b/test_integration/lib/utils/encoders.dart index 1cd7baddf..9954da4be 100644 --- a/test_integration/lib/utils/encoders.dart +++ b/test_integration/lib/utils/encoders.dart @@ -1,4 +1,4 @@ -import 'package:ably_flutter/ably_flutter.dart'; +import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; Map? encodeMessageExtras(MessageExtras? extras) => extras == null diff --git a/test_integration/lib/utils/realtime.dart b/test_integration/lib/utils/realtime.dart index 5d591c595..b0823a607 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) { diff --git a/test_integration/pubspec.lock b/test_integration/pubspec.lock index 8f513be99..e0d15731b 100644 --- a/test_integration/pubspec.lock +++ b/test_integration/pubspec.lock @@ -9,7 +9,7 @@ packages: url: "https://pub.dev" source: hosted version: "67.0.0" - ably_flutter: + ably_pubsub_device_flutter: dependency: "direct main" description: path: ".." diff --git a/test_integration/pubspec.yaml b/test_integration/pubspec.yaml index 5d487661e..b709d36fc 100644 --- a/test_integration/pubspec.yaml +++ b/test_integration/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: flutter: sdk: flutter - ably_flutter: + ably_pubsub_device_flutter: path: ".." args: ^2.3.1 diff --git a/test_integration/test_driver/test_implementation/crypto_tests.dart b/test_integration/test_driver/test_implementation/crypto_tests.dart index 201c6fd5a..6c052ef5f 100644 --- a/test_integration/test_driver/test_implementation/crypto_tests.dart +++ b/test_integration/test_driver/test_implementation/crypto_tests.dart @@ -79,8 +79,9 @@ 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; From ca1079a4b53277bf253fe26aed305f71b3de8643 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 18 Sep 2026 20:08:06 +0100 Subject: [PATCH 03/14] refactor!: rename Realtime client to PubSubClient Following the rename of this SDK to Ably Pub/Sub, and the removal of the REST client, the single remaining client is renamed from `Realtime` to `PubSubClient` to match the product name. The declaring file moves to `pub_sub_client.dart` accordingly. Only the class is renamed. The `realtime` properties on `Connection`, `RealtimeChannel`, `RealtimeChannels`, `Push` and `PushChannel`, the `realtimeInstances` getter, and the platform channel method names all keep their current names. Unit and integration tests are updated to construct `PubSubClient`; they cover the same behaviour as before. `UPDATING.md` gains a migration note for the new name. Co-Authored-By: Claude Opus 5 (1M context) --- PushNotifications.md | 4 ++-- README.md | 8 ++++---- UPDATING.md | 5 +++-- example/example.md | 6 +++--- example/lib/nested_realtime_events.dart | 2 +- .../push_notification_service.dart | 2 +- example/lib/ui/ably_service.dart | 4 ++-- example/lib/ui/realtime_presence_sliver.dart | 2 +- example/lib/ui/realtime_sliver.dart | 2 +- lib/src/authentication/src/client_options.dart | 3 +-- lib/src/platform/platform.dart | 2 +- lib/src/platform/src/realtime/connection.dart | 4 ++-- .../{realtime.dart => pub_sub_client.dart} | 18 +++++++++--------- .../src/realtime/realtime_channel.dart | 8 ++++---- .../src/realtime/realtime_channels.dart | 4 ++-- lib/src/push_notifications/src/push.dart | 2 +- .../push_notifications/src/push_channel.dart | 2 +- lib/src/realtime/src/realtime_auth.dart | 2 +- ...ably_pubsub_device_flutter_plugin_test.dart | 8 ++++---- test/realtime/channel_test.dart | 8 ++++---- test/realtime/channels_test.dart | 2 +- .../realtime/realtime_auth_client_id_test.dart | 4 ++-- .../test/realtime/realtime_auth_url_test.dart | 4 ++-- .../test/realtime/realtime_authorize_test.dart | 4 ++-- .../realtime_encrypted_publish_test.dart | 6 +++--- .../test/realtime/realtime_events_test.dart | 2 +- .../test/realtime/realtime_history_test.dart | 2 +- ...altime_history_with_auth_callback_test.dart | 2 +- .../realtime_presence_enter_update_leave.dart | 4 ++-- .../test/realtime/realtime_presence_get.dart | 4 ++-- .../realtime_presence_history_test.dart | 2 +- .../realtime/realtime_presence_subscribe.dart | 2 +- .../test/realtime/realtime_publish_test.dart | 6 +++--- ...altime_publish_with_auth_callback_test.dart | 2 +- .../lib/test/realtime/realtime_subscribe.dart | 4 ++-- .../lib/test/realtime/realtime_time_test.dart | 2 +- 36 files changed, 74 insertions(+), 74 deletions(-) rename lib/src/platform/src/realtime/{realtime.dart => pub_sub_client.dart} (88%) diff --git a/PushNotifications.md b/PushNotifications.md index 7b2d241ec..1e51b319e 100644 --- a/PushNotifications.md +++ b/PushNotifications.md @@ -97,7 +97,7 @@ If you invoke any methods from the `ably_pubsub_device_flutter` package before c ### Device activation -- Create a realtime client: e.g. `final realtime = ably.Realtime(options: clientOptions);` +- Create a realtime client: e.g. `final realtime = ably.PubSubClient(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. @@ -238,7 +238,7 @@ Requesting permissions: ```dart // Create an Ably client to access the push field -final realtime = ably.Realtime(options: clientOptions); +final realtime = ably.PubSubClient(options: clientOptions); final push = realtime.push; // Request permission from user on iOS bool permissionGranted = await push.requestPermission(); diff --git a/README.md b/README.md index 186eb2900..f42e0738d 100644 --- a/README.md +++ b/README.md @@ -73,16 +73,16 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' as a 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.PubSubClient(options: clientOptions); // Wait for connection to be established - await realtimeClient.connection + await pubSubClient.connection .on(ably.ConnectionEvent.connected) .first .then((stateChange) { @@ -90,7 +90,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 c517ce379..4496cd167 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -8,8 +8,9 @@ This guide lists the changes needed to upgrade from one version of Ably to a new - `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`. -- The REST client has been removed. Use the realtime client instead: - - Replace `ably.Rest(options: clientOptions)` / `ably.Rest.fromKey(key)` with `ably.Realtime(options: clientOptions)` / `ably.Realtime.fromKey(key)`, and use `realtime.channels`, `realtime.auth`, `realtime.push` and `realtime.time()` in place of their `Rest` equivalents. +- The `Realtime` client has been renamed to `PubSubClient`. Replace `ably.Realtime(options: clientOptions)` / `ably.Realtime.fromKey(key)` with `ably.PubSubClient(options: clientOptions)` / `ably.PubSubClient.fromKey(key)`. +- The REST client has been removed. Use `PubSubClient` instead: + - Replace `ably.Rest(options: clientOptions)` / `ably.Rest.fromKey(key)` with `ably.PubSubClient(options: clientOptions)` / `ably.PubSubClient.fromKey(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. diff --git a/example/example.md b/example/example.md index 2a006ad5c..ad759e783 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.PubSubClient(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.PubSubClient(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.PubSubClient(options: clientOptions); ``` #### Read Realtime time diff --git a/example/lib/nested_realtime_events.dart b/example/lib/nested_realtime_events.dart index ec1c65155..25ae97278 100644 --- a/example/lib/nested_realtime_events.dart +++ b/example/lib/nested_realtime_events.dart @@ -11,7 +11,7 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' /// /// 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 - α diff --git a/example/lib/push_notifications/push_notification_service.dart b/example/lib/push_notifications/push_notification_service.dart index ee4d3a779..2baf4e04e 100644 --- a/example/lib/push_notifications/push_notification_service.dart +++ b/example/lib/push_notifications/push_notification_service.dart @@ -9,7 +9,7 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' import 'package:rxdart/rxdart.dart'; class PushNotificationService { - final ably.Realtime _realtime; + final ably.PubSubClient _realtime; late ably.RealtimeChannel? _realtimeChannel; late ably.RealtimeChannel? _pushLogMetaChannel; late ably.PushChannel? _pushChannel; diff --git a/example/lib/ui/ably_service.dart b/example/lib/ui/ably_service.dart index c4aedee1d..e3abe0178 100644 --- a/example/lib/ui/ably_service.dart +++ b/example/lib/ui/ably_service.dart @@ -5,12 +5,12 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' as ably; class AblyService { - late final ably.Realtime realtime; + late final ably.PubSubClient realtime; late final PushNotificationService pushNotificationService; late final ApiKeyProvision apiKeyProvision; AblyService({required this.apiKeyProvision}) { - realtime = ably.Realtime( + realtime = ably.PubSubClient( options: ably.ClientOptions( key: apiKeyProvision.key, clientId: Constants.clientId, diff --git a/example/lib/ui/realtime_presence_sliver.dart b/example/lib/ui/realtime_presence_sliver.dart index 96877ed43..6cfac8e68 100644 --- a/example/lib/ui/realtime_presence_sliver.dart +++ b/example/lib/ui/realtime_presence_sliver.dart @@ -10,7 +10,7 @@ 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 = []; diff --git a/example/lib/ui/realtime_sliver.dart b/example/lib/ui/realtime_sliver.dart index dbc1b0e0b..41f765096 100644 --- a/example/lib/ui/realtime_sliver.dart +++ b/example/lib/ui/realtime_sliver.dart @@ -14,7 +14,7 @@ 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 = []; diff --git a/lib/src/authentication/src/client_options.dart b/lib/src/authentication/src/client_options.dart index 048b5570f..36442060a 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -1,7 +1,6 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; -/// Passes additional client-specific properties to the Realtime -/// [Realtime.new] +/// Passes additional client-specific properties to [PubSubClient.new] class ClientOptions extends AuthOptions { /// A client ID, used for identifying this client when publishing messages or /// for presence purposes. diff --git a/lib/src/platform/platform.dart b/lib/src/platform/platform.dart index f22a90fd3..e97b41ee9 100644 --- a/lib/src/platform/platform.dart +++ b/lib/src/platform/platform.dart @@ -8,7 +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/streams_channel.dart'; diff --git a/lib/src/platform/src/realtime/connection.dart b/lib/src/platform/src/realtime/connection.dart index 9cc1a2445..aeb2fa563 100644 --- a/lib/src/platform/src/realtime/connection.dart +++ b/lib/src/platform/src/realtime/connection.dart @@ -6,8 +6,8 @@ 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; diff --git a/lib/src/platform/src/realtime/realtime.dart b/lib/src/platform/src/realtime/pub_sub_client.dart similarity index 88% rename from lib/src/platform/src/realtime/realtime.dart rename to lib/src/platform/src/realtime/pub_sub_client.dart index 1f1addbac..76d562b80 100644 --- a/lib/src/platform/src/realtime/realtime.dart +++ b/lib/src/platform/src/realtime/pub_sub_client.dart @@ -7,10 +7,10 @@ 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. -class Realtime extends PlatformObject { - /// Constructs a `Realtime` object using an Ably [options] object or +class PubSubClient extends PlatformObject { + /// Constructs a `PubSubClient` object using an Ably [options] object or /// the Ably API [key] or token string used to validate the client. - Realtime({ + PubSubClient({ ClientOptions? options, final String? key, }) : assert(options != null || key != null), @@ -22,10 +22,10 @@ class Realtime extends PlatformObject { auth = RealtimeAuth(this); } - /// Constructs a `Realtime` object using an Ably API [key] or token string + /// Constructs a `PubSubClient` object using an Ably API [key] or token string /// used to validate the client. - factory Realtime.fromKey(String key) => - Realtime(options: ClientOptions(key: key)); + factory PubSubClient.fromKey(String key) => + PubSubClient(options: ClientOptions(key: key)); ///@nodoc @override @@ -104,9 +104,9 @@ class Realtime extends PlatformObject { invokeRequest(PlatformMethod.pushDevice); } -Map _realtimeInstances = {}; +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 779ee52d0..6b846ae5b 100644 --- a/lib/src/platform/src/realtime/realtime_channel.dart +++ b/lib/src/platform/src/realtime/realtime_channel.dart @@ -8,14 +8,14 @@ import 'package:ably_pubsub_device_flutter/src/platform/platform_internal.dart'; /// 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,11 +24,11 @@ class RealtimeChannel extends PlatformObject { RealtimePresence get presence => _presence; /// @nodoc - /// instantiates with [Realtime], [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) + RealtimeChannel(PubSubClient realtime, String channelName) : _realtime = realtime, _channelName = channelName, state = ChannelState.initialized, diff --git a/lib/src/platform/src/realtime/realtime_channels.dart b/lib/src/platform/src/realtime/realtime_channels.dart index 4dfce0122..94ec5dc2a 100644 --- a/lib/src/platform/src/realtime/realtime_channels.dart +++ b/lib/src/platform/src/realtime/realtime_channels.dart @@ -6,10 +6,10 @@ import 'package:meta/meta.dart'; 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 diff --git a/lib/src/push_notifications/src/push.dart b/lib/src/push_notifications/src/push.dart index 89cb1f7e2..f8934f7b4 100644 --- a/lib/src/push_notifications/src/push.dart +++ b/lib/src/push_notifications/src/push.dart @@ -17,7 +17,7 @@ class Push extends PlatformObject { /// @nodoc /// A realtime client used platform side to invoke push notification methods - final Realtime realtime; + final PubSubClient realtime; /// @nodoc /// Pass an Ably realtime client. diff --git a/lib/src/push_notifications/src/push_channel.dart b/lib/src/push_notifications/src/push_channel.dart index 88b187f4e..a304619ff 100644 --- a/lib/src/push_notifications/src/push_channel.dart +++ b/lib/src/push_notifications/src/push_channel.dart @@ -7,7 +7,7 @@ class PushChannel extends PlatformObject { /// @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 client. diff --git a/lib/src/realtime/src/realtime_auth.dart b/lib/src/realtime/src/realtime_auth.dart index 012fbe1ac..729ddb572 100644 --- a/lib/src/realtime/src/realtime_auth.dart +++ b/lib/src/realtime/src/realtime_auth.dart @@ -3,7 +3,7 @@ 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); diff --git a/test/ably_pubsub_device_flutter_plugin_test.dart b/test/ably_pubsub_device_flutter_plugin_test.dart index e9ce8ef83..82b13056b 100644 --- a/test/ably_pubsub_device_flutter_plugin_test.dart +++ b/test/ably_pubsub_device_flutter_plugin_test.dart @@ -56,27 +56,27 @@ void main() { final o = ClientOptions( realtimeHost: host, ); - final realtime = Realtime(options: o); + final realtime = PubSubClient(options: o); expect(await realtime.handle, counter); expect(realtime.options.realtimeHost, host); }); test('createRealtimeWithToken', () async { const key = 'TEST-KEY'; - final realtime = Realtime.fromKey(key); + final realtime = PubSubClient.fromKey(key); expect(await realtime.handle, counter); expect(realtime.options.tokenDetails!.token, key); }); test('createRealtimeWithKey', () async { const key = 'TEST:KEY'; - final realtime = Realtime.fromKey(key); + final realtime = PubSubClient.fromKey(key); expect(await realtime.handle, counter); expect(realtime.options.key, key); }); test('publishMessage', () async { - final realtime = Realtime.fromKey('TEST-KEY'); + final realtime = PubSubClient.fromKey('TEST-KEY'); await realtime.channels.get('test').publish(name: 'name', data: 'data'); expect(1, 1); }); diff --git a/test/realtime/channel_test.dart b/test/realtime/channel_test.dart index c2b956a68..ae1248dcb 100644 --- a/test/realtime/channel_test.dart +++ b/test/realtime/channel_test.dart @@ -20,7 +20,7 @@ void main() { group('realtime#channels#channel', () { test('publish realtime message without authCallback', () async { // setup - final realtime = Realtime(key: 'TEST-KEY'); + final realtime = PubSubClient(key: 'TEST-KEY'); final channel = realtime.channels.get('test'); // exercise @@ -48,7 +48,7 @@ void main() { authCallback: authCallback, authUrl: 'hasAuthCallback', ); - final realtime = Realtime(options: options, key: 'TEST-KEY'); + final realtime = PubSubClient(options: options, key: 'TEST-KEY'); final channel = realtime.channels.get('test'); @@ -74,7 +74,7 @@ void main() { authCallback: (tokenParams) async => 'token', authUrl: 'hasAuthCallback', ); - final realtime = Realtime(options: options, key: 'TEST-KEY'); + final realtime = PubSubClient(options: options, key: 'TEST-KEY'); final channel = realtime.channels.get('test'); // exercise @@ -111,7 +111,7 @@ void main() { authCallback: authCallback, authUrl: 'hasAuthCallback', ); - final realtime = Realtime(options: options, key: 'TEST-KEY'); + final realtime = PubSubClient(options: options, key: 'TEST-KEY'); final channel = realtime.channels.get('test'); // exercise diff --git a/test/realtime/channels_test.dart b/test/realtime/channels_test.dart index afad0dbd2..db254628f 100644 --- a/test/realtime/channels_test.dart +++ b/test/realtime/channels_test.dart @@ -11,7 +11,7 @@ void main() { setUp(() { manager = MockMethodCallManager(); - final realtime = Realtime.fromKey('TEST-KEY'); + final realtime = PubSubClient.fromKey('TEST-KEY'); channels = realtime.channels; }); 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 be31775f0..d8510aaa0 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 @@ -17,7 +17,7 @@ Future> testRealtimeAuthClientId({ autoConnect: false, ); - final ablyForToken = Realtime( + final ablyForToken = PubSubClient( options: clientOptionsForToken, ); /* get first token */ @@ -30,7 +30,7 @@ Future> testRealtimeAuthClientId({ autoConnect: false, clientId: 'testClientId', tokenDetails: firstToken); - final realtime = Realtime(options: clientOptions); + final realtime = PubSubClient(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 e7fd8e663..2c53b2c67 100644 --- a/test_integration/lib/test/realtime/realtime_auth_url_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_url_test.dart @@ -21,7 +21,7 @@ Future> testCreateRealtimeWithAuthUrl({ fallbackHosts: ['a.ably-realtime.com', 'b.ably-realtime.com'], ); - final ablyForToken = Realtime( + final ablyForToken = PubSubClient( options: clientOptionsForToken, ); final tokenDetails = await ablyForToken.auth.requestToken(); @@ -34,7 +34,7 @@ Future> testCreateRealtimeWithAuthUrl({ useTokenAuth: true, autoConnect: false, logLevel: LogLevel.error); - final realtime = Realtime(options: options); + final realtime = PubSubClient(options: options); final completer = Completer(); realtime.connection.on().listen((stateChange) { if (stateChange.current == ConnectionState.connected) { diff --git a/test_integration/lib/test/realtime/realtime_authorize_test.dart b/test_integration/lib/test/realtime/realtime_authorize_test.dart index 48e92e626..38f050949 100644 --- a/test_integration/lib/test/realtime/realtime_authorize_test.dart +++ b/test_integration/lib/test/realtime/realtime_authorize_test.dart @@ -17,7 +17,7 @@ Future> testRealtimeAuthroize({ autoConnect: false, ); - final ablyForToken = Realtime( + final ablyForToken = PubSubClient( options: clientOptionsForToken, ); /* get first token */ @@ -29,7 +29,7 @@ Future> testRealtimeAuthroize({ useTokenAuth: true, autoConnect: false, tokenDetails: firstToken); - final realtime = Realtime(options: clientOptions); + final realtime = PubSubClient(options: clientOptions); await realtime.connect(); const tokenParams = TokenParams(ttl: 20000); 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 fa245a876..bfedb849a 100644 --- a/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart @@ -17,7 +17,7 @@ Future> testRealtimeEncryptedPublish({ final channelOptions = RealtimeChannelOptions(cipherParams: cipherParams); - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', @@ -46,8 +46,8 @@ Future> testRealtimeEncryptedPublishSpec({ 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 = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_events_test.dart b/test_integration/lib/test/realtime/realtime_events_test.dart index d6baa6b25..8a4ec088e 100644 --- a/test_integration/lib/test/realtime/realtime_events_test.dart +++ b/test_integration/lib/test/realtime/realtime_events_test.dart @@ -18,7 +18,7 @@ Future> testRealtimeEvents({ final channelStateChanges = >[]; final filteredChannelStateChanges = >[]; - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_history_test.dart b/test_integration/lib/test/realtime/realtime_history_test.dart index cd0ea95c3..6e70878e2 100644 --- a/test_integration/lib/test/realtime/realtime_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_history_test.dart @@ -12,7 +12,7 @@ Future> testRealtimeHistory({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', 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 f60e2b01c..782d61e2e 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 @@ -10,7 +10,7 @@ Future> testRealtimeHistoryWithAuthCallback({ }) async { var authCallbackInvoked = false; - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( clientId: 'someClientId', logLevel: LogLevel.error, 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 1b0a7ebae..cef007852 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 @@ -27,7 +27,7 @@ Future> testRealtimePresenceEnterUpdateLeave({ final clientIds = [null, 'client-1', 'client-2']; final clientIDClashMatrix = >[]; for (final clientId in clientIds) { - final presence = Realtime(options: getClientOptions(appKey, clientId)) + final presence = PubSubClient(options: getClientOptions(appKey, clientId)) .channels .get('test') .presence; @@ -103,7 +103,7 @@ Future> testRealtimePresenceEnterUpdateLeave({ // Interact with different types of data. final actionMatrix = >[]; final realtimePresence = - Realtime(options: getClientOptions(appKey, 'test-client')) + PubSubClient(options: getClientOptions(appKey, 'test-client')) .channels .get('test') .presence; diff --git a/test_integration/lib/test/realtime/realtime_presence_get.dart b/test_integration/lib/test/realtime/realtime_presence_get.dart index 28009e04d..7a65280b4 100644 --- a/test_integration/lib/test/realtime/realtime_presence_get.dart +++ b/test_integration/lib/test/realtime/realtime_presence_get.dart @@ -25,13 +25,13 @@ Future> testRealtimePresenceGet({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime(options: getClientOptions(appKey)); + final realtime = PubSubClient(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 PubSubClient( 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 0f3a3e1d5..faf9fb06b 100644 --- a/test_integration/lib/test/realtime/realtime_presence_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_presence_history_test.dart @@ -20,7 +20,7 @@ Future> testRealtimePresenceHistory({ logLevel: LogLevel.error, ); - final realtime = Realtime(options: options); + final realtime = PubSubClient(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 9e9bd8296..605c6e35a 100644 --- a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart @@ -18,7 +18,7 @@ Future> testRealtimePresenceSubscribe({ }) async { reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final presence = Realtime( + final presence = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_publish_test.dart b/test_integration/lib/test/realtime/realtime_publish_test.dart index 839251f6e..46c3a9d58 100644 --- a/test_integration/lib/test/realtime/realtime_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_publish_test.dart @@ -13,7 +13,7 @@ Future> testRealtimePublish({ final appKey = await AppProvisioning().provisionApp(); final logMessages = >[]; - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', @@ -35,7 +35,7 @@ Future> testRealtimePublishSpec({ }) async { final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', @@ -81,7 +81,7 @@ Future> testRealtimePublishSpec({ ); // client options - no client id, message has client id - final realtime2 = Realtime( + final realtime2 = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', 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 942532737..c5bb77625 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 @@ -8,7 +8,7 @@ Future> testRealtimePublishWithAuthCallback({ Map? payload, }) async { var authCallbackInvoked = false; - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( logLevel: LogLevel.error, authCallback: (params) async { diff --git a/test_integration/lib/test/realtime/realtime_subscribe.dart b/test_integration/lib/test/realtime/realtime_subscribe.dart index 3004d2875..192a38384 100644 --- a/test_integration/lib/test/realtime/realtime_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_subscribe.dart @@ -5,8 +5,8 @@ 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 = PubSubClient( options: ClientOptions( key: apiKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_time_test.dart b/test_integration/lib/test/realtime/realtime_time_test.dart index 619a66e9a..ad645be3d 100644 --- a/test_integration/lib/test/realtime/realtime_time_test.dart +++ b/test_integration/lib/test/realtime/realtime_time_test.dart @@ -9,7 +9,7 @@ Future> testRealtimeTime({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = Realtime( + final realtime = PubSubClient( options: ClientOptions( key: appKey, environment: 'sandbox', From ef1b3b6a6eed9a1ea9fb33d9dfaf2abae38a4dfd Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 18 Sep 2026 20:24:46 +0100 Subject: [PATCH 04/14] refactor!: create PubSubClient via createClient() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make `PubSubClient`'s constructor private and expose a single top-level `createClient({required ClientOptions options})` function as the only way to obtain a client. A factory function keeps the construction path open for future changes — validation, caching, returning a subtype — without another breaking change to the public surface. This also removes the two key shorthands, `PubSubClient.fromKey(key)` and the `key` parameter. They duplicated `ClientOptions(key: ...)`, which `fromKey` delegated to anyway, and the either/or `options`/`key` signature was only enforced by an assert. Callers now pass the key through `ClientOptions`, so behaviour is unchanged. The four call sites that passed both `options` and `key` keep only `options`, which is what took effect before. `options` becomes a final field, as it is now always set at construction. Unit and integration tests construct clients through `createClient` and cover the same behaviour as before. Co-Authored-By: Claude Opus 5 (1M context) --- PushNotifications.md | 4 +-- README.md | 2 +- UPDATING.md | 6 +++-- example/example.md | 6 ++--- example/lib/ui/ably_service.dart | 2 +- .../authentication/src/client_options.dart | 2 +- .../platform/src/realtime/pub_sub_client.dart | 26 +++++++++---------- ...bly_pubsub_device_flutter_plugin_test.dart | 8 +++--- test/realtime/channel_test.dart | 8 +++--- test/realtime/channels_test.dart | 2 +- .../realtime_auth_client_id_test.dart | 4 +-- .../test/realtime/realtime_auth_url_test.dart | 4 +-- .../realtime/realtime_authorize_test.dart | 4 +-- .../realtime_encrypted_publish_test.dart | 4 +-- .../test/realtime/realtime_events_test.dart | 2 +- .../test/realtime/realtime_history_test.dart | 2 +- ...ltime_history_with_auth_callback_test.dart | 2 +- .../realtime_presence_enter_update_leave.dart | 4 +-- .../test/realtime/realtime_presence_get.dart | 4 +-- .../realtime_presence_history_test.dart | 2 +- .../realtime/realtime_presence_subscribe.dart | 2 +- .../test/realtime/realtime_publish_test.dart | 6 ++--- ...ltime_publish_with_auth_callback_test.dart | 2 +- .../lib/test/realtime/realtime_subscribe.dart | 2 +- .../lib/test/realtime/realtime_time_test.dart | 2 +- 25 files changed, 56 insertions(+), 56 deletions(-) diff --git a/PushNotifications.md b/PushNotifications.md index 1e51b319e..e8b9c4be2 100644 --- a/PushNotifications.md +++ b/PushNotifications.md @@ -97,7 +97,7 @@ If you invoke any methods from the `ably_pubsub_device_flutter` package before c ### Device activation -- Create a realtime client: e.g. `final realtime = ably.PubSubClient(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. @@ -238,7 +238,7 @@ Requesting permissions: ```dart // Create an Ably client to access the push field -final realtime = ably.PubSubClient(options: clientOptions); +final realtime = ably.createClient(options: clientOptions); final push = realtime.push; // Request permission from user on iOS bool permissionGranted = await push.requestPermission(); diff --git a/README.md b/README.md index f42e0738d..232321353 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ The following code connects to Ably's realtime messaging service, subscribes to clientId: 'me', ); - final pubSubClient = ably.PubSubClient(options: clientOptions); + final pubSubClient = ably.createClient(options: clientOptions); // Wait for connection to be established await pubSubClient.connection diff --git a/UPDATING.md b/UPDATING.md index 4496cd167..2f1e74a05 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -8,9 +8,11 @@ This guide lists the changes needed to upgrade from one version of Ably to a new - `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`. -- The `Realtime` client has been renamed to `PubSubClient`. Replace `ably.Realtime(options: clientOptions)` / `ably.Realtime.fromKey(key)` with `ably.PubSubClient(options: clientOptions)` / `ably.PubSubClient.fromKey(key)`. +- 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))`. - The REST client has been removed. Use `PubSubClient` instead: - - Replace `ably.Rest(options: clientOptions)` / `ably.Rest.fromKey(key)` with `ably.PubSubClient(options: clientOptions)` / `ably.PubSubClient.fromKey(key)`, and use `channels`, `auth`, `push` and `time()` on the `PubSubClient` in place of their `Rest` equivalents. + - 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. diff --git a/example/example.md b/example/example.md index ad759e783..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.PubSubClient realtime = ably.PubSubClient(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.PubSubClient realtime = ably.PubSubClient(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.PubSubClient realtime = ably.PubSubClient(options: clientOptions); +ably.PubSubClient realtime = ably.createClient(options: clientOptions); ``` #### Read Realtime time diff --git a/example/lib/ui/ably_service.dart b/example/lib/ui/ably_service.dart index e3abe0178..b918ec0aa 100644 --- a/example/lib/ui/ably_service.dart +++ b/example/lib/ui/ably_service.dart @@ -10,7 +10,7 @@ class AblyService { late final ApiKeyProvision apiKeyProvision; AblyService({required this.apiKeyProvision}) { - realtime = ably.PubSubClient( + realtime = ably.createClient( options: ably.ClientOptions( key: apiKeyProvision.key, clientId: Constants.clientId, diff --git a/lib/src/authentication/src/client_options.dart b/lib/src/authentication/src/client_options.dart index 36442060a..c18d4d5a5 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -1,6 +1,6 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart'; -/// Passes additional client-specific properties to [PubSubClient.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. diff --git a/lib/src/platform/src/realtime/pub_sub_client.dart b/lib/src/platform/src/realtime/pub_sub_client.dart index 76d562b80..4d7a80862 100644 --- a/lib/src/platform/src/realtime/pub_sub_client.dart +++ b/lib/src/platform/src/realtime/pub_sub_client.dart @@ -7,26 +7,17 @@ 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 { - /// Constructs a `PubSubClient` object using an Ably [options] object or - /// the Ably API [key] or token string used to validate the client. - PubSubClient({ - ClientOptions? options, - final String? key, - }) : assert(options != null || key != null), - options = options ?? ClientOptions(key: key), - super() { + PubSubClient._({required this.options}) : super() { _connection = Connection(this); _channels = RealtimeChannels(this); push = Push(realtime: this); auth = RealtimeAuth(this); } - /// Constructs a `PubSubClient` object using an Ably API [key] or token string - /// used to validate the client. - factory PubSubClient.fromKey(String key) => - PubSubClient(options: ClientOptions(key: key)); - ///@nodoc @override Future createPlatformInstance() async { @@ -64,7 +55,7 @@ class PubSubClient 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; @@ -104,6 +95,13 @@ class PubSubClient extends PlatformObject { invokeRequest(PlatformMethod.pushDevice); } +/// 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 diff --git a/test/ably_pubsub_device_flutter_plugin_test.dart b/test/ably_pubsub_device_flutter_plugin_test.dart index 82b13056b..b3fb051a2 100644 --- a/test/ably_pubsub_device_flutter_plugin_test.dart +++ b/test/ably_pubsub_device_flutter_plugin_test.dart @@ -56,27 +56,27 @@ void main() { final o = ClientOptions( realtimeHost: host, ); - final realtime = PubSubClient(options: o); + final realtime = createClient(options: o); expect(await realtime.handle, counter); expect(realtime.options.realtimeHost, host); }); test('createRealtimeWithToken', () async { const key = 'TEST-KEY'; - final realtime = PubSubClient.fromKey(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 = PubSubClient.fromKey(key); + final realtime = createClient(options: ClientOptions(key: key)); expect(await realtime.handle, counter); expect(realtime.options.key, key); }); test('publishMessage', () async { - final realtime = PubSubClient.fromKey('TEST-KEY'); + 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/realtime/channel_test.dart b/test/realtime/channel_test.dart index ae1248dcb..93adef034 100644 --- a/test/realtime/channel_test.dart +++ b/test/realtime/channel_test.dart @@ -20,7 +20,7 @@ void main() { group('realtime#channels#channel', () { test('publish realtime message without authCallback', () async { // setup - final realtime = PubSubClient(key: 'TEST-KEY'); + final realtime = createClient(options: ClientOptions(key: 'TEST-KEY')); final channel = realtime.channels.get('test'); // exercise @@ -48,7 +48,7 @@ void main() { authCallback: authCallback, authUrl: 'hasAuthCallback', ); - final realtime = PubSubClient(options: options, key: 'TEST-KEY'); + final realtime = createClient(options: options); final channel = realtime.channels.get('test'); @@ -74,7 +74,7 @@ void main() { authCallback: (tokenParams) async => 'token', authUrl: 'hasAuthCallback', ); - final realtime = PubSubClient(options: options, key: 'TEST-KEY'); + final realtime = createClient(options: options); final channel = realtime.channels.get('test'); // exercise @@ -111,7 +111,7 @@ void main() { authCallback: authCallback, authUrl: 'hasAuthCallback', ); - final realtime = PubSubClient(options: options, key: 'TEST-KEY'); + final realtime = createClient(options: options); final channel = realtime.channels.get('test'); // exercise diff --git a/test/realtime/channels_test.dart b/test/realtime/channels_test.dart index db254628f..1336af844 100644 --- a/test/realtime/channels_test.dart +++ b/test/realtime/channels_test.dart @@ -11,7 +11,7 @@ void main() { setUp(() { manager = MockMethodCallManager(); - final realtime = PubSubClient.fromKey('TEST-KEY'); + final realtime = createClient(options: ClientOptions(key: 'TEST-KEY')); channels = realtime.channels; }); 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 d8510aaa0..108e67480 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 @@ -17,7 +17,7 @@ Future> testRealtimeAuthClientId({ autoConnect: false, ); - final ablyForToken = PubSubClient( + final ablyForToken = createClient( options: clientOptionsForToken, ); /* get first token */ @@ -30,7 +30,7 @@ Future> testRealtimeAuthClientId({ autoConnect: false, clientId: 'testClientId', tokenDetails: firstToken); - final realtime = PubSubClient(options: clientOptions); + 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 2c53b2c67..4ac301158 100644 --- a/test_integration/lib/test/realtime/realtime_auth_url_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_url_test.dart @@ -21,7 +21,7 @@ Future> testCreateRealtimeWithAuthUrl({ fallbackHosts: ['a.ably-realtime.com', 'b.ably-realtime.com'], ); - final ablyForToken = PubSubClient( + final ablyForToken = createClient( options: clientOptionsForToken, ); final tokenDetails = await ablyForToken.auth.requestToken(); @@ -34,7 +34,7 @@ Future> testCreateRealtimeWithAuthUrl({ useTokenAuth: true, autoConnect: false, logLevel: LogLevel.error); - final realtime = PubSubClient(options: options); + final realtime = createClient(options: options); final completer = Completer(); realtime.connection.on().listen((stateChange) { if (stateChange.current == ConnectionState.connected) { diff --git a/test_integration/lib/test/realtime/realtime_authorize_test.dart b/test_integration/lib/test/realtime/realtime_authorize_test.dart index 38f050949..07c0629d9 100644 --- a/test_integration/lib/test/realtime/realtime_authorize_test.dart +++ b/test_integration/lib/test/realtime/realtime_authorize_test.dart @@ -17,7 +17,7 @@ Future> testRealtimeAuthroize({ autoConnect: false, ); - final ablyForToken = PubSubClient( + final ablyForToken = createClient( options: clientOptionsForToken, ); /* get first token */ @@ -29,7 +29,7 @@ Future> testRealtimeAuthroize({ useTokenAuth: true, autoConnect: false, tokenDetails: firstToken); - final realtime = PubSubClient(options: clientOptions); + final realtime = createClient(options: clientOptions); await realtime.connect(); const tokenParams = TokenParams(ttl: 20000); 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 bfedb849a..d4f0e0a42 100644 --- a/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart @@ -17,7 +17,7 @@ Future> testRealtimeEncryptedPublish({ final channelOptions = RealtimeChannelOptions(cipherParams: cipherParams); - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', @@ -47,7 +47,7 @@ Future> testRealtimeEncryptedPublishSpec({ final channelOptions = RealtimeChannelOptions(cipherParams: cipherParams); // PubSubClient instance where client id is specified in the instance itself - final realtimeWithClientId = PubSubClient( + final realtimeWithClientId = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_events_test.dart b/test_integration/lib/test/realtime/realtime_events_test.dart index 8a4ec088e..fd6a1af03 100644 --- a/test_integration/lib/test/realtime/realtime_events_test.dart +++ b/test_integration/lib/test/realtime/realtime_events_test.dart @@ -18,7 +18,7 @@ Future> testRealtimeEvents({ final channelStateChanges = >[]; final filteredChannelStateChanges = >[]; - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_history_test.dart b/test_integration/lib/test/realtime/realtime_history_test.dart index 6e70878e2..a5d3365b7 100644 --- a/test_integration/lib/test/realtime/realtime_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_history_test.dart @@ -12,7 +12,7 @@ Future> testRealtimeHistory({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', 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 782d61e2e..ed1328585 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 @@ -10,7 +10,7 @@ Future> testRealtimeHistoryWithAuthCallback({ }) async { var authCallbackInvoked = false; - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( clientId: 'someClientId', logLevel: LogLevel.error, 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 cef007852..8d804622f 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 @@ -27,7 +27,7 @@ Future> testRealtimePresenceEnterUpdateLeave({ final clientIds = [null, 'client-1', 'client-2']; final clientIDClashMatrix = >[]; for (final clientId in clientIds) { - final presence = PubSubClient(options: getClientOptions(appKey, clientId)) + final presence = createClient(options: getClientOptions(appKey, clientId)) .channels .get('test') .presence; @@ -103,7 +103,7 @@ Future> testRealtimePresenceEnterUpdateLeave({ // Interact with different types of data. final actionMatrix = >[]; final realtimePresence = - PubSubClient(options: getClientOptions(appKey, 'test-client')) + createClient(options: getClientOptions(appKey, 'test-client')) .channels .get('test') .presence; diff --git a/test_integration/lib/test/realtime/realtime_presence_get.dart b/test_integration/lib/test/realtime/realtime_presence_get.dart index 7a65280b4..f3ba9e847 100644 --- a/test_integration/lib/test/realtime/realtime_presence_get.dart +++ b/test_integration/lib/test/realtime/realtime_presence_get.dart @@ -25,13 +25,13 @@ Future> testRealtimePresenceGet({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = PubSubClient(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 PubSubClient( + 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 faf9fb06b..60085ae49 100644 --- a/test_integration/lib/test/realtime/realtime_presence_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_presence_history_test.dart @@ -20,7 +20,7 @@ Future> testRealtimePresenceHistory({ logLevel: LogLevel.error, ); - final realtime = PubSubClient(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 605c6e35a..d3b4d2ce7 100644 --- a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart @@ -18,7 +18,7 @@ Future> testRealtimePresenceSubscribe({ }) async { reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final presence = PubSubClient( + final presence = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_publish_test.dart b/test_integration/lib/test/realtime/realtime_publish_test.dart index 46c3a9d58..27e1eaed9 100644 --- a/test_integration/lib/test/realtime/realtime_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_publish_test.dart @@ -13,7 +13,7 @@ Future> testRealtimePublish({ final appKey = await AppProvisioning().provisionApp(); final logMessages = >[]; - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', @@ -35,7 +35,7 @@ Future> testRealtimePublishSpec({ }) async { final appKey = await AppProvisioning().provisionApp(); - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', @@ -81,7 +81,7 @@ Future> testRealtimePublishSpec({ ); // client options - no client id, message has client id - final realtime2 = PubSubClient( + final realtime2 = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', 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 c5bb77625..68ee75f70 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 @@ -8,7 +8,7 @@ Future> testRealtimePublishWithAuthCallback({ Map? payload, }) async { var authCallbackInvoked = false; - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( logLevel: LogLevel.error, authCallback: (params) async { diff --git a/test_integration/lib/test/realtime/realtime_subscribe.dart b/test_integration/lib/test/realtime/realtime_subscribe.dart index 192a38384..5812186f2 100644 --- a/test_integration/lib/test/realtime/realtime_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_subscribe.dart @@ -6,7 +6,7 @@ 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 = PubSubClient( + final realtime = createClient( options: ClientOptions( key: apiKey, environment: 'sandbox', diff --git a/test_integration/lib/test/realtime/realtime_time_test.dart b/test_integration/lib/test/realtime/realtime_time_test.dart index ad645be3d..6de1b0fca 100644 --- a/test_integration/lib/test/realtime/realtime_time_test.dart +++ b/test_integration/lib/test/realtime/realtime_time_test.dart @@ -9,7 +9,7 @@ Future> testRealtimeTime({ reporter.reportLog('init start'); final appKey = await AppProvisioning().provisionApp(); - final realtime = PubSubClient( + final realtime = createClient( options: ClientOptions( key: appKey, environment: 'sandbox', From d30af3e7906c43e0579220e600b85df6ab84be14 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 18 Sep 2026 20:34:24 +0100 Subject: [PATCH 05/14] refactor!: remove all deprecated API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear out the API that earlier versions marked deprecated, now that a major release allows it: - `Connection.recoveryKey`, superseded by `createRecoveryKey()`. - `ClientOptions.logHandler` and the `LogHandler` typedef. These never did anything — the codec carried a `TODO handle logHandler` where the encoding would have gone, and log output is handled by the underlying native SDK. - `ClientOptions.fallbackHostsUseDefault`, which had no replacement. - `ClientOptions.fromKey()` and `AuthOptions.fromKey()`, superseded by the constructors' named `key` parameter. `fallbackHostsUseDefault` was plumbed over the platform channel, so it also goes from `bin/codegen_context.dart` — regenerating the Dart, Java and Obj-C constants — from the Dart codec, and from the Java and Obj-C readers. Dropping the Obj-C read removes the deprecation warning ably-cocoa emitted for that property. Three doc comments referred to members this commit deletes, and are rewritten to describe what replaces them. Co-Authored-By: Claude Opus 5 (1M context) --- UPDATING.md | 5 ++++ .../ably/flutter/plugin/AblyMessageCodec.java | 1 - .../plugin/generated/PlatformConstants.java | 1 - bin/codegen_context.dart | 1 - ios/Classes/codec/AblyFlutterReader.m | 1 - ios/Classes/codec/AblyPlatformConstants.h | 1 - ios/Classes/codec/AblyPlatformConstants.m | 1 - lib/src/authentication/src/auth_options.dart | 13 --------- .../authentication/src/client_options.dart | 28 ------------------- lib/src/generated/platform_constants.dart | 1 - lib/src/logging/logging.dart | 1 - lib/src/logging/src/log_handler.dart | 16 ----------- lib/src/platform/src/codec.dart | 11 -------- lib/src/platform/src/realtime/connection.dart | 23 ++++++--------- test/models/client_options.dart | 2 -- 15 files changed, 13 insertions(+), 93 deletions(-) delete mode 100644 lib/src/logging/src/log_handler.dart diff --git a/UPDATING.md b/UPDATING.md index 2f1e74a05..67cd9fe1b 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -11,6 +11,11 @@ This guide lists the changes needed to upgrade from one version of Ably to a new - 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`. 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 8762f51ea..17ef43bd7 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java @@ -348,7 +348,6 @@ private PlatformClientOptions decodeClientOptions(Map jsonMap) { 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); 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 08dcdfe40..fc081f349 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 @@ -169,7 +169,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"; diff --git a/bin/codegen_context.dart b/bin/codegen_context.dart index e2f479702..f62454a89 100644 --- a/bin/codegen_context.dart +++ b/bin/codegen_context.dart @@ -235,7 +235,6 @@ const List> _objects = [ 'httpMaxRetryCount', 'realtimeRequestTimeout', 'fallbackHosts', - 'fallbackHostsUseDefault', 'fallbackRetryTimeout', 'defaultTokenParams', 'channelRetryTimeout', diff --git a/ios/Classes/codec/AblyFlutterReader.m b/ios/Classes/codec/AblyFlutterReader.m index c660d89ea..8bf9b1a7b 100644 --- a/ios/Classes/codec/AblyFlutterReader.m +++ b/ios/Classes/codec/AblyFlutterReader.m @@ -136,7 +136,6 @@ use an explicitly received null from Dart (manifesting this side as a nil id) 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); diff --git a/ios/Classes/codec/AblyPlatformConstants.h b/ios/Classes/codec/AblyPlatformConstants.h index 94983d097..beb9a8d81 100644 --- a/ios/Classes/codec/AblyPlatformConstants.h +++ b/ios/Classes/codec/AblyPlatformConstants.h @@ -159,7 +159,6 @@ 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; diff --git a/ios/Classes/codec/AblyPlatformConstants.m b/ios/Classes/codec/AblyPlatformConstants.m index 421b0ccb9..b609ff857 100644 --- a/ios/Classes/codec/AblyPlatformConstants.m +++ b/ios/Classes/codec/AblyPlatformConstants.m @@ -131,7 +131,6 @@ 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"; diff --git a/lib/src/authentication/src/auth_options.dart b/lib/src/authentication/src/auth_options.dart index 5b2ee2091..f246077be 100644 --- a/lib/src/authentication/src/auth_options.dart +++ b/lib/src/authentication/src/auth_options.dart @@ -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 c18d4d5a5..de2e59a4f 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -13,15 +13,6 @@ 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; @@ -92,11 +83,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; @@ -175,13 +161,11 @@ class ClientOptions extends AuthOptions { this.environment, this.fallbackHosts, int? fallbackRetryTimeout, - this.fallbackHostsUseDefault, int? httpMaxRetryCount, int? httpOpenTimeout, int? httpRequestTimeout, this.idempotentRestPublishing, String? key, - this.logHandler, LogLevel? logLevel, this.port, bool? queryTime, @@ -208,10 +192,6 @@ class ClientOptions extends AuthOptions { 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 this.autoConnect = autoConnect ?? this.autoConnect; this.channelRetryTimeout = channelRetryTimeout ?? this.channelRetryTimeout; this.disconnectedRetryTimeout = @@ -230,14 +210,6 @@ 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 diff --git a/lib/src/generated/platform_constants.dart b/lib/src/generated/platform_constants.dart index d0d89ccbe..e54a27094 100644 --- a/lib/src/generated/platform_constants.dart +++ b/lib/src/generated/platform_constants.dart @@ -174,7 +174,6 @@ class TxClientOptions { 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'; 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 cda64ce65..000000000 --- a/lib/src/logging/src/log_handler.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:ably_pubsub_device_flutter/ably_pubsub_device_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/platform/src/codec.dart b/lib/src/platform/src/codec.dart index be59f3e1a..c18176515 100644 --- a/lib/src/platform/src/codec.dart +++ b/lib/src/platform/src/codec.dart @@ -248,7 +248,6 @@ class Codec extends StandardMessageCodec { _writeToJson(jsonMap, TxClientOptions.clientId, v.clientId); _writeToJson( jsonMap, TxClientOptions.logLevel, _encodeLogLevel(v.logLevel)); - //TODO handle logHandler _writeToJson(jsonMap, TxClientOptions.tls, v.tls); _writeToJson(jsonMap, TxClientOptions.restHost, v.restHost); _writeToJson(jsonMap, TxClientOptions.realtimeHost, v.realtimeHost); @@ -271,11 +270,6 @@ class Codec extends StandardMessageCodec { _writeToJson(jsonMap, TxClientOptions.realtimeRequestTimeout, v.realtimeRequestTimeout); _writeToJson(jsonMap, TxClientOptions.fallbackHosts, v.fallbackHosts); - _writeToJson( - jsonMap, - TxClientOptions.fallbackHostsUseDefault, - // ignore: deprecated_member_use_from_same_package - v.fallbackHostsUseDefault); _writeToJson( jsonMap, TxClientOptions.fallbackRetryTimeout, v.fallbackRetryTimeout); _writeToJson(jsonMap, TxClientOptions.defaultTokenParams, @@ -592,7 +586,6 @@ class Codec extends StandardMessageCodec { TxClientOptions.clientId, ), logLevel: _decodeLogLevel(jsonMap[TxClientOptions.logLevel] as String?), - //TODO handle logHandler tls: jsonMap[TxClientOptions.tls] as bool, restHost: _readFromJson( jsonMap, @@ -635,10 +628,6 @@ class Codec extends StandardMessageCodec { jsonMap, TxClientOptions.fallbackHosts, ), - fallbackHostsUseDefault: _readFromJson( - jsonMap, - TxClientOptions.fallbackHostsUseDefault, - ), fallbackRetryTimeout: jsonMap[TxClientOptions.fallbackRetryTimeout] as int, defaultTokenParams: diff --git a/lib/src/platform/src/realtime/connection.dart b/lib/src/platform/src/realtime/connection.dart index aeb2fa563..c936b1c18 100644 --- a/lib/src/platform/src/realtime/connection.dart +++ b/lib/src/platform/src/realtime/connection.dart @@ -48,22 +48,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 +69,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. diff --git a/test/models/client_options.dart b/test/models/client_options.dart index 97461b8ad..2f047e54d 100644 --- a/test/models/client_options.dart +++ b/test/models/client_options.dart @@ -27,8 +27,6 @@ void main() { 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); From c49540614e9a5d09ad31e1d2f57d8bd5a1962948 Mon Sep 17 00:00:00 2001 From: Simon Woolf Date: Fri, 9 Oct 2026 04:09:51 +0100 Subject: [PATCH 06/14] feat!: make RealtimeChannels.release() async and surface platform errors Spec 6.3.0 replaces RTS4a, under which release() detaches a channel that is not already INITIALIZED, DETACHED or FAILED before removing it, with RTS4c-e: release() of a channel in any other state must fail with error 90011 and leave the channel in place. That check is done by the platform SDKs (ably-java, ably-cocoa) in their next major versions. release() previously fired the platform call without awaiting it and dropped the Dart-side channel unconditionally, so a platform error would have been lost as an unhandled async error while the Dart and platform collections diverged. It now returns a Future that completes with the platform's AblyException, and only removes the Dart-side channel once the platform release has succeeded. The state check is deliberately left to the platform SDK, because RealtimeChannel.state is updated asynchronously from platform events and so can lag behind the platform channel's state. Co-Authored-By: Claude Opus 5.5 (1M context) --- UPDATING.md | 1 + example/lib/ui/realtime_sliver.dart | 2 +- lib/src/common/src/channels.dart | 5 +-- .../src/realtime/realtime_channels.dart | 15 ++++--- test/mock_method_call_manager.dart | 5 +++ test/realtime/channels_test.dart | 40 ++++++++++++++++--- 6 files changed, 53 insertions(+), 15 deletions(-) diff --git a/UPDATING.md b/UPDATING.md index 67cd9fe1b..558ae4179 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -21,6 +21,7 @@ This guide lists the changes needed to upgrade from one version of Ably to a new - `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] diff --git a/example/lib/ui/realtime_sliver.dart b/example/lib/ui/realtime_sliver.dart index 41f765096..bd0e7a59e 100644 --- a/example/lib/ui/realtime_sliver.dart +++ b/example/lib/ui/realtime_sliver.dart @@ -142,7 +142,7 @@ class RealtimeSliver extends HookWidget { TextButton( onPressed: () async { await channel.detach(); - realtime.channels.release(Constants.channelName); + await realtime.channels.release(Constants.channelName); channel = realtime.channels.get(Constants.channelName); setupListeners( connectionState, channelState, connectionId, recoveryKey); diff --git a/lib/src/common/src/channels.dart b/lib/src/common/src/channels.dart index 26a799466..be893fc40 100644 --- a/lib/src/common/src/channels.dart +++ b/lib/src/common/src/channels.dart @@ -41,9 +41,8 @@ abstract class Channels extends Iterable { /// [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/platform/src/realtime/realtime_channels.dart b/lib/src/platform/src/realtime/realtime_channels.dart index 94ec5dc2a..5addd42c1 100644 --- a/lib/src/platform/src/realtime/realtime_channels.dart +++ b/lib/src/platform/src/realtime/realtime_channels.dart @@ -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/test/mock_method_call_manager.dart b/test/mock_method_call_manager.dart index affa3cfef..51c7f79ea 100644 --- a/test/mock_method_call_manager.dart +++ b/test/mock_method_call_manager.dart @@ -10,6 +10,7 @@ class MockMethodCallManager { bool isAuthenticated = false; final channels = {}; final publishedMessages = >[]; + PlatformException? releaseRealtimeChannelError; MockMethodCallManager() { final channel = @@ -22,6 +23,7 @@ class MockMethodCallManager { void reset() { channels.clear(); publishedMessages.clear(); + releaseRealtimeChannelError = null; handleCounter = 0; final channel = MethodChannel('io.ably.flutter.plugin', StandardMethodCodec(Codec())); @@ -69,6 +71,9 @@ class MockMethodCallManager { return null; case PlatformMethod.releaseRealtimeChannel: + if (releaseRealtimeChannelError != null) { + throw releaseRealtimeChannelError!; + } return null; default: diff --git a/test/realtime/channels_test.dart b/test/realtime/channels_test.dart index 1336af844..5b7afe2a7 100644 --- a/test/realtime/channels_test.dart +++ b/test/realtime/channels_test.dart @@ -1,4 +1,5 @@ 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'; @@ -87,30 +88,57 @@ void main() { group('#release', () { test( - '(RSN4a) Takes one argument, the channel name,' - ' and releases the corresponding channel entity', () { + '(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' + '(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)); + }); }); }); } From 7282656caeebc4508a00c1a15faec0d369afce93 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 3 Aug 2026 20:17:31 +0100 Subject: [PATCH 07/14] chore: raise minimum Flutter to 3.44 and iOS deployment target to 13.0 Two floors land in the same bump. The UIScene plugin APIs this SDK is about to adopt -- the `FlutterSceneLifeCycleDelegate` protocol and `FlutterPluginRegistrar.addSceneDelegate:` -- first shipped in Flutter 3.38, and `UIScene` itself is iOS 13+. Flutter 3.44 then made Swift Package Manager the default dependency manager rather than the opt-in experiment it had been since 3.24, and that is the floor the upcoming CocoaPods -> SwiftPM migration needs: below it a consumer would have to turn on an experimental feature by hand just to build this plugin. So raise the Flutter floor straight to 3.44 (Dart 3.12) rather than 3.38, and pull the toolchain and CI up to 3.47.3, the current stable. Because 3.44+ migrates an Xcode project to SwiftPM the first time it builds, the example and test_integration apps pin `enable-swift-package-manager: false` in their pubspecs for now. That keeps this commit a version bump and lets the migration land as its own reviewable change, which drops those pins along with the Podfiles. Alongside the version bumps: - Drop two lints the newer analyzer rejects: `package_api_docs`, which was removed, and `avoid_null_checks_in_equality_operators`, now deprecated. Drop the two redundant `!` assertions in `codec.dart` it also flags. - Silence `prefer_initializing_formals`. Dart 3.13 extended it to private fields initialised from a named parameter, but `this._field` in a named parameter list is only legal under the off-by-default `private-named-parameters` experiment, so its seven reports have no fix. - Silence `experimental_member_use` on the example app's deliberate use of the experimental `Push.reset`. - Take the `build/`, `android/` and `ios/` analyzer excludes that `flutter pub get` now writes into `analysis_options.yaml` itself, rather than have the file churn on every fetch. - Gitignore `ios/Flutter/ephemeral/`, which Flutter generates in both example projects. - Regenerating the CocoaPods projects also drops stale Firebase, Toast and nanopb framework entries from the example's `project.pbxproj`; those pods are no longer in its Podfile. Groundwork for #592 and #602. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/check.yaml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/flutter_example_app.yaml | 4 +- .github/workflows/flutter_integration.yaml | 4 +- .github/workflows/ios_unit_tests.yml | 2 +- .tool-versions | 2 +- PushNotifications.md | 2 +- README.md | 4 +- analysis_options.yaml | 9 +- example/ios/.gitignore | 1 + example/ios/Flutter/AppFrameworkInfo.plist | 2 - example/ios/Podfile | 9 +- example/ios/Podfile.lock | 6 +- example/ios/Runner.xcodeproj/project.pbxproj | 37 +++----- .../xcshareddata/xcschemes/Runner.xcscheme | 5 +- example/ios/Runner/Info.plist | 4 + .../push_notification_service.dart | 3 + example/pubspec.lock | 20 ++--- example/pubspec.yaml | 10 ++- ios/ably_pubsub_device_flutter.podspec | 3 +- lib/src/platform/src/codec.dart | 4 +- pubspec.lock | 64 +++++++------- pubspec.yaml | 13 ++- test_integration/ios/.gitignore | 1 + .../ios/Flutter/AppFrameworkInfo.plist | 2 - test_integration/ios/Podfile | 2 +- test_integration/ios/Podfile.lock | 6 +- .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 + test_integration/pubspec.lock | 88 +++++++++---------- test_integration/pubspec.yaml | 9 +- 31 files changed, 173 insertions(+), 155 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 12ba268e3..f4c42dc4f 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 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..d41e1f59e 100644 --- a/.github/workflows/flutter_example_app.yaml +++ b/.github/workflows/flutter_example_app.yaml @@ -17,7 +17,7 @@ jobs: - 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" @@ -39,7 +39,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 33c2ea700..8185c33d5 100644 --- a/.github/workflows/flutter_integration.yaml +++ b/.github/workflows/flutter_integration.yaml @@ -18,7 +18,7 @@ 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_pubsub_device_flutter, so before we run integration @@ -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' diff --git a/.github/workflows/ios_unit_tests.yml b/.github/workflows/ios_unit_tests.yml index 867f82a0c..926196fb2 100644 --- a/.github/workflows/ios_unit_tests.yml +++ b/.github/workflows/ios_unit_tests.yml @@ -18,7 +18,7 @@ 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 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/PushNotifications.md b/PushNotifications.md index e8b9c4be2..6204c01c3 100644 --- a/PushNotifications.md +++ b/PushNotifications.md @@ -25,7 +25,7 @@ See [issue #226](https://github.com/ably/ably-flutter/issues/226). - Android API level 19+ (Android 4.4+) - Android devices - Android emulator (with Google APIs) -- iOS 10+ +- iOS 13+ - 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. diff --git a/README.md b/README.md index 232321353..f7b9b1994 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ 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 | +| iOS | iOS 13 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. diff --git a/analysis_options.yaml b/analysis_options.yaml index ae1b09a80..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 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..10f3c9b47 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, '13.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 ccf7e3196..99ad51036 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -42,14 +42,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Ably: 1756d6590a572809c10fc4bcc32e05fe9e7867d7 - ably_pubsub_device_flutter: 90395fd33ab6c8423c44369dc5fabe54af8e6ddf + ably_pubsub_device_flutter: 03c57b59bcbf92ba2bd6910f10ed4e155bced176 AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 msgpack: c85f6251873059738472ae136951cec5f30f3251 -PODFILE CHECKSUM: 5dac7b8eff57b86c98038531a0cc7562fb467acc +PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 519b37005..ffa37a6e8 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 */ @@ -157,7 +157,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -203,10 +203,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -224,39 +226,21 @@ "${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_pubsub_device_flutter/ably_pubsub_device_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_pubsub_device_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 +249,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -374,7 +359,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 = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -400,7 +385,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -465,7 +450,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 = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -514,7 +499,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 = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -540,7 +525,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -576,7 +561,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..9c12df59c 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index ed3e8743b..ca132f81e 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -49,5 +49,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/push_notifications/push_notification_service.dart b/example/lib/push_notifications/push_notification_service.dart index 2baf4e04e..90364f9c6 100644 --- a/example/lib/push_notifications/push_notification_service.dart +++ b/example/lib/push_notifications/push_notification_service.dart @@ -86,6 +86,9 @@ class PushNotificationService { 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 { diff --git a/example/pubspec.lock b/example/pubspec.lock index 5c7517383..58f917ee3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -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 e51dbd61a..65584517a 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: @@ -28,4 +28,10 @@ dev_dependencies: flutter_lints: ^1.0.4 flutter: + # Flutter 3.44 turns Swift Package Manager on by default, and building this + # app would migrate its Xcode project off CocoaPods on the spot. Hold that + # off so the migration can land as its own reviewable commit rather than as + # a side effect of a version bump. + config: + enable-swift-package-manager: false uses-material-design: true diff --git a/ios/ably_pubsub_device_flutter.podspec b/ios/ably_pubsub_device_flutter.podspec index 230ef3b95..d4b172f7b 100644 --- a/ios/ably_pubsub_device_flutter.podspec +++ b/ios/ably_pubsub_device_flutter.podspec @@ -20,7 +20,8 @@ Pod::Spec.new do |s| s.dependency 'Flutter' s.dependency 'Ably', '1.2.53' s.platform = :ios - s.ios.deployment_target = '10.0' + # UIScene, and therefore `FlutterSceneLifeCycleDelegate`, is iOS 13+ only. + s.ios.deployment_target = '13.0' # Flutter 3.x ships both arm64 and x86_64 simulator slices; no arch restriction needed. s.pod_target_xcconfig = { diff --git a/lib/src/platform/src/codec.dart b/lib/src/platform/src/codec.dart index c18176515..c62056f10 100644 --- a/lib/src/platform/src/codec.dart +++ b/lib/src/platform/src/codec.dart @@ -32,7 +32,7 @@ class _CodecPair { ); } if (value == null) return null; - return _encoder!(value as T); + return _encoder(value as T); } /// @nodoc @@ -46,7 +46,7 @@ class _CodecPair { ); } if (jsonMap == null) return null; - return _decoder!(jsonMap); + return _decoder(jsonMap); } } 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 fc46f7e33..0fb76ebfa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,8 +4,17 @@ 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_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/Podfile b/test_integration/ios/Podfile index 2c068c404..10f3c9b47 100644 --- a/test_integration/ios/Podfile +++ b/test_integration/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/test_integration/ios/Podfile.lock b/test_integration/ios/Podfile.lock index f91c3e575..68d22c9f4 100644 --- a/test_integration/ios/Podfile.lock +++ b/test_integration/ios/Podfile.lock @@ -27,11 +27,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Ably: 1756d6590a572809c10fc4bcc32e05fe9e7867d7 - ably_pubsub_device_flutter: 90395fd33ab6c8423c44369dc5fabe54af8e6ddf + ably_pubsub_device_flutter: 03c57b59bcbf92ba2bd6910f10ed4e155bced176 AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 msgpack: c85f6251873059738472ae136951cec5f30f3251 -PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 +PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b COCOAPODS: 1.16.2 diff --git a/test_integration/ios/Runner.xcodeproj/project.pbxproj b/test_integration/ios/Runner.xcodeproj/project.pbxproj index b2609ce9a..2006eada7 100644 --- a/test_integration/ios/Runner.xcodeproj/project.pbxproj +++ b/test_integration/ios/Runner.xcodeproj/project.pbxproj @@ -342,7 +342,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 = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -428,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 = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -477,7 +477,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 = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 4f746537f..fc5ae0316 100644 --- a/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/test_integration/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> @@ -45,6 +46,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/pubspec.lock b/test_integration/pubspec.lock index e0d15731b..9b9bec96f 100644 --- a/test_integration/pubspec.lock +++ b/test_integration/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "93.0.0" ably_pubsub_device_flutter: dependency: "direct main" description: @@ -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 b709d36fc..a1645636c 100644 --- a/test_integration/pubspec.yaml +++ b/test_integration/pubspec.yaml @@ -4,7 +4,8 @@ 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: @@ -24,4 +25,10 @@ dev_dependencies: flutter_lints: ^1.0.4 flutter: + # Flutter 3.44 turns Swift Package Manager on by default, and building this + # app would migrate its Xcode project off CocoaPods on the spot. Hold that + # off so the migration can land as its own reviewable commit rather than as + # a side effect of a version bump. + config: + enable-swift-package-manager: false uses-material-design: true From 6bbed6ae1ae31c06b47abbe862fe8bd664ad2424 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 1 Oct 2026 02:43:55 +0100 Subject: [PATCH 08/14] feat!: distribute the iOS plugin as a Swift package instead of a CocoaPod Flutter 3.44 made Swift Package Manager the default, and the previous commit raised the floor to it, so the plugin can now ship a `Package.swift` and drop its podspec. SwiftPM does not allow Objective-C and Swift in the same target, and this plugin's two languages call into each other: the Objective-C plugin class registers Swift handlers, and those handlers take the plugin class. The package breaks the cycle with three targets: - AblyFlutterCommon (Objective-C): the shared types -- instance store, message wrappers, platform constants, streams channel -- plus a new `AblyFlutterPluginContext` protocol carrying the instance store and method channel that the handlers need. - AblyFlutterSwift: the existing Swift handlers, encoders and push glue, now taking `AblyFlutterPluginContext` instead of `AblyFlutter` and importing their dependencies explicitly. - ably_pubsub_device_flutter (Objective-C): the `AblyFlutter` plugin class, which conforms to the protocol, and the codec. `ably-cocoa` stays pinned at exactly 1.2.53, now resolved through SwiftPM. SwiftPM cannot read `pubspec.yaml` the way the podspec did, so the plugin version reported in the `ably-flutter` agent moves into `AblyFlutterVersion.h`. A Dart test fails if it drifts from `pubspec.yaml`, and the release skill and CONTRIBUTING.md now bump it. `getVersion` now reports "ably-cocoa " rather than "CocoaPod ". The example and test_integration apps drop their `enable-swift-package-manager: false` pins and are migrated by Flutter. test_integration no longer uses CocoaPods at all; the example keeps its Podfile for flutter_local_notifications, fluttertoast and device_info_plus, which only support CocoaPods at their pinned versions. When Flutter migrates a plugin's own example app, it references the package both directly, giving it the identity `ably_pubsub_device_flutter`, and through a symlink named after the repository's checkout directory, and resolution fails if the two differ. The iOS example CI job therefore checks out into `ably_pubsub_device_flutter`, and CONTRIBUTING.md asks contributors to clone into a directory of that name. Apps consuming the plugin from pub.dev are unaffected. The XCTest suite moves into the package as a test target. Standalone, the package's `../FlutterFramework` dependency does not exist, so the new `ios/run_unit_tests.sh` generates one wrapping the SDK's engine and runs the tests; the iOS unit test workflow now calls it in place of `pod lib lint`. BREAKING CHANGE: apps that have turned Swift Package Manager off can no longer install the plugin, as there is no podspec. See UPDATING.md. Co-Authored-By: Claude Opus 5.5 --- .claude/skills/release/SKILL.md | 14 +-- .github/workflows/flutter_example_app.yaml | 7 ++ .github/workflows/ios_unit_tests.yml | 26 +----- CONTRIBUTING.md | 42 +++++++-- UPDATING.md | 4 + bin/README.md | 4 +- bin/codegen.dart | 4 +- example/ios/Podfile | 2 +- example/ios/Podfile.lock | 25 +----- example/ios/Runner.xcodeproj/project.pbxproj | 46 +++++++--- .../xcshareddata/xcschemes/Runner.xcscheme | 18 ++++ example/pubspec.yaml | 6 -- ios/.gitignore | 9 +- ios/Assets/.gitkeep | 0 ios/Classes/handlers/FlutterHandler.swift | 5 -- ios/ably_pubsub_device_flutter.podspec | 38 -------- ios/ably_pubsub_device_flutter/Package.swift | 74 ++++++++++++++++ .../AblyFlutterClientOptions.m | 0 .../AblyFlutterCommon}/AblyFlutterMessage.m | 0 .../AblyFlutterCommon}/AblyInstanceStore.m | 1 - .../AblyPlatformConstants.m | 0 .../AblyFlutterCommon}/AblyStreamsChannel.m | 0 .../AblyFlutterClientOptions.h | 0 .../AblyFlutterCommon}/AblyFlutterMessage.h | 0 .../AblyFlutterPluginContext.h | 22 +++++ .../AblyFlutterCommon}/AblyInstanceStore.h | 0 .../AblyPlatformConstants.h | 0 .../AblyFlutterCommon}/AblyStreamsChannel.h | 0 .../AblyFlutterSwift}/Notification.swift | 0 .../PushNotificationEventHandlers.swift | 5 +- .../AblyFlutterSwift}/RemoteMessage.swift | 2 +- .../AblyFlutterSwift}/codec/CryptoCodec.swift | 4 +- .../codec/encoders/ErrorInfo.swift | 2 + .../encoders/PushNotificationEncoders.swift | 1 + .../handlers/AuthHandlers.swift | 3 + .../handlers/CryptoHandlers.swift | 4 +- .../handlers/FlutterHandler.swift | 7 ++ .../PushActivationEventHandlers.swift | 4 +- .../handlers/PushHandlers.swift | 7 +- .../ably_pubsub_device_flutter}/AblyFlutter.m | 10 +-- .../AblyFlutterStreamHandler.h | 1 + .../AblyFlutterStreamHandler.m | 5 +- .../PrivacyInfo.xcprivacy | 0 .../codec/AblyFlutterReader.h | 3 +- .../codec/AblyFlutterReader.m | 7 +- .../codec/AblyFlutterReaderWriter.h | 0 .../codec/AblyFlutterReaderWriter.m | 0 .../codec/AblyFlutterVersion.h | 6 ++ .../codec/AblyFlutterWriter.h | 0 .../codec/AblyFlutterWriter.m | 7 +- .../ably_pubsub_device_flutter}/AblyFlutter.h | 4 +- .../AblyStreamsChannelTests.m | 2 +- ios/run_unit_tests.sh | 60 +++++++++++++ test/ios_plugin_version_test.dart | 23 +++++ test_integration/ios/Flutter/Debug.xcconfig | 1 - test_integration/ios/Flutter/Release.xcconfig | 1 - test_integration/ios/Podfile | 41 --------- test_integration/ios/Podfile.lock | 37 -------- .../ios/Runner.xcodeproj/project.pbxproj | 87 ++++++------------- .../xcshareddata/swiftpm/Package.resolved | 41 +++++++++ .../xcshareddata/xcschemes/Runner.xcscheme | 18 ++++ .../contents.xcworkspacedata | 3 - .../xcshareddata/swiftpm/Package.resolved | 41 +++++++++ test_integration/pubspec.yaml | 6 -- 64 files changed, 472 insertions(+), 318 deletions(-) delete mode 100644 ios/Assets/.gitkeep delete mode 100644 ios/Classes/handlers/FlutterHandler.swift delete mode 100644 ios/ably_pubsub_device_flutter.podspec create mode 100644 ios/ably_pubsub_device_flutter/Package.swift rename ios/{Classes/types => ably_pubsub_device_flutter/Sources/AblyFlutterCommon}/AblyFlutterClientOptions.m (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterCommon}/AblyFlutterMessage.m (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterCommon}/AblyInstanceStore.m (96%) rename ios/{Classes/codec => ably_pubsub_device_flutter/Sources/AblyFlutterCommon}/AblyPlatformConstants.m (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterCommon}/AblyStreamsChannel.m (100%) rename ios/{Classes/types => ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon}/AblyFlutterClientOptions.h (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon}/AblyFlutterMessage.h (100%) create mode 100644 ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterPluginContext.h rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon}/AblyInstanceStore.h (100%) rename ios/{Classes/codec => ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon}/AblyPlatformConstants.h (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon}/AblyStreamsChannel.h (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/Notification.swift (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/PushNotificationEventHandlers.swift (98%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/RemoteMessage.swift (96%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/codec/CryptoCodec.swift (98%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/codec/encoders/ErrorInfo.swift (92%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/codec/encoders/PushNotificationEncoders.swift (99%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/handlers/AuthHandlers.swift (98%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/handlers/CryptoHandlers.swift (97%) create mode 100644 ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/FlutterHandler.swift rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/handlers/PushActivationEventHandlers.swift (98%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/AblyFlutterSwift}/handlers/PushHandlers.swift (96%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/AblyFlutter.m (99%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/AblyFlutterStreamHandler.h (97%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/AblyFlutterStreamHandler.m (98%) rename ios/{Resources => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/PrivacyInfo.xcprivacy (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/codec/AblyFlutterReader.h (87%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/codec/AblyFlutterReader.m (98%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/codec/AblyFlutterReaderWriter.h (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/codec/AblyFlutterReaderWriter.m (100%) create mode 100644 ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterVersion.h rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/codec/AblyFlutterWriter.h (100%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter}/codec/AblyFlutterWriter.m (98%) rename ios/{Classes => ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/include/ably_pubsub_device_flutter}/AblyFlutter.h (82%) rename ios/{Tests => ably_pubsub_device_flutter/Tests/AblyFlutterCommonTests}/AblyStreamsChannelTests.m (99%) create mode 100755 ios/run_unit_tests.sh create mode 100644 test/ios_plugin_version_test.dart delete mode 100644 test_integration/ios/Podfile delete mode 100644 test_integration/ios/Podfile.lock create mode 100644 test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index aba319242..51b9d83d2 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -19,25 +19,17 @@ Then perform these steps in order: 2. Update the version in these files: - `pubspec.yaml` — the `version` property - `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/flutter_example_app.yaml b/.github/workflows/flutter_example_app.yaml index d41e1f59e..7b78a0a30 100644 --- a/.github/workflows/flutter_example_app.yaml +++ b/.github/workflows/flutter_example_app.yaml @@ -11,9 +11,15 @@ jobs: ios: runs-on: "macos-latest" 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: @@ -21,6 +27,7 @@ jobs: 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 diff --git a/.github/workflows/ios_unit_tests.yml b/.github/workflows/ios_unit_tests.yml index 926196fb2..db1ac9429 100644 --- a/.github/workflows/ios_unit_tests.yml +++ b/.github/workflows/ios_unit_tests.yml @@ -21,30 +21,6 @@ jobs: 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_pubsub_device_flutter-Unit-Tests scheme) without booting a simulator - run: | - cd ios - pod lib lint ably_pubsub_device_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_pubsub_device_flutter-Unit-Tests \ - -destination "id=$SIMULATOR_ID,arch=arm64" \ - CODE_SIGNING_ALLOWED=NO + run: ios/run_unit_tests.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index feaf7be23..c85b41338 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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: @@ -89,10 +106,10 @@ After the app is launched, it can be re-launched from XCode/Android Studio with 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. -To test `ably-cocoa` changes, in `Podfile`, below `target 'Runner' do`, add: +To test `ably-cocoa` changes, in [ios/ably_pubsub_device_flutter/Package.swift](ios/ably_pubsub_device_flutter/Package.swift), temporarily replace the `ably-cocoa` dependency with a local one: -```ruby - pod 'Ably', :path => 'local/path/to/ably-cocoa' +```swift +.package(name: "ably-cocoa", path: "local/path/to/ably-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). @@ -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/UPDATING.md b/UPDATING.md index 558ae4179..fbacb7959 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -8,6 +8,10 @@ This guide lists the changes needed to upgrade from one version of Ably to a new - `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 `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))`. 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/example/ios/Podfile b/example/ios/Podfile index 10f3c9b47..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, '13.0' +platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 99ad51036..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_pubsub_device_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_pubsub_device_flutter (from `.symlinks/plugins/ably_pubsub_device_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_pubsub_device_flutter: - :path: ".symlinks/plugins/ably_pubsub_device_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_pubsub_device_flutter: 03c57b59bcbf92ba2bd6910f10ed4e155bced176 - AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 - Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 - msgpack: c85f6251873059738472ae136951cec5f30f3251 -PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b +PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index ffa37a6e8..3356fbd5a 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -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,6 +166,9 @@ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastUpgradeCheck = 1510; @@ -224,23 +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}/ably_pubsub_device_flutter/ably_pubsub_device_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", ); 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}/ably_pubsub_device_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", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -359,7 +365,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -385,7 +391,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -450,7 +456,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -499,7 +505,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -525,7 +531,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -561,7 +567,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -603,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 9c12df59c..5db441f58 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + Void - -import Foundation diff --git a/ios/ably_pubsub_device_flutter.podspec b/ios/ably_pubsub_device_flutter.podspec deleted file mode 100644 index d4b172f7b..000000000 --- a/ios/ably_pubsub_device_flutter.podspec +++ /dev/null @@ -1,38 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint ably_pubsub_device_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_pubsub_device_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 - # UIScene, and therefore `FlutterSceneLifeCycleDelegate`, is iOS 13+ only. - s.ios.deployment_target = '13.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_pubsub_device_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..4012448b9 --- /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: [ + // UIScene, and therefore `FlutterSceneLifeCycleDelegate`, is iOS 13+ only. + .iOS("13.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-cocoa", exact: "1.2.53"), + ], + targets: [ + .target( + name: "ably_pubsub_device_flutter", + dependencies: [ + "AblyFlutterCommon", + "AblyFlutterSwift", + .product(name: "FlutterFramework", package: "FlutterFramework"), + .product(name: "Ably", package: "ably-cocoa"), + ], + resources: [ + .process("PrivacyInfo.xcprivacy") + ], + cSettings: [ + .headerSearchPath("include/ably_pubsub_device_flutter") + ] + ), + .target( + name: "AblyFlutterSwift", + dependencies: [ + "AblyFlutterCommon", + .product(name: "FlutterFramework", package: "FlutterFramework"), + .product(name: "Ably", package: "ably-cocoa"), + ] + ), + .target( + name: "AblyFlutterCommon", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework"), + .product(name: "Ably", package: "ably-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 96% rename from ios/Classes/AblyInstanceStore.m rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m index 1482d9173..3ba6b37f1 100644 --- a/ios/Classes/AblyInstanceStore.m +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m @@ -1,7 +1,6 @@ @import Ably; #import "AblyInstanceStore.h" -#import @implementation AblyInstanceStore { NSMutableDictionary* _realtimeInstances; diff --git a/ios/Classes/codec/AblyPlatformConstants.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m similarity index 100% rename from ios/Classes/codec/AblyPlatformConstants.m rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m 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 100% rename from ios/Classes/types/AblyFlutterClientOptions.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterClientOptions.h 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 100% rename from ios/Classes/AblyInstanceStore.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h diff --git a/ios/Classes/codec/AblyPlatformConstants.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h similarity index 100% rename from ios/Classes/codec/AblyPlatformConstants.h rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h 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 98% rename from ios/Classes/codec/CryptoCodec.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift index 70434af82..9696a30b3 100644 --- a/ios/Classes/codec/CryptoCodec.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift @@ -1,5 +1,7 @@ -import Foundation import Ably +import AblyFlutterCommon +import Flutter +import Foundation public class CryptoCodec: NSObject { diff --git a/ios/Classes/codec/encoders/ErrorInfo.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift similarity index 92% rename from ios/Classes/codec/encoders/ErrorInfo.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift index a57f0d014..42c355a72 100644 --- a/ios/Classes/codec/encoders/ErrorInfo.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift @@ -1,3 +1,5 @@ +import Ably +import AblyFlutterCommon import Foundation public class Encoders: NSObject { diff --git a/ios/Classes/codec/encoders/PushNotificationEncoders.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift similarity index 99% rename from ios/Classes/codec/encoders/PushNotificationEncoders.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift index 630be4236..a1f3212d2 100644 --- a/ios/Classes/codec/encoders/PushNotificationEncoders.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift @@ -1,4 +1,5 @@ import Ably +import AblyFlutterCommon import Foundation import UserNotifications diff --git a/ios/Classes/handlers/AuthHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift similarity index 98% rename from ios/Classes/handlers/AuthHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift index 2f88b4749..0d5a74b77 100644 --- a/ios/Classes/handlers/AuthHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift @@ -5,6 +5,9 @@ // Created by Ikbal Kaya on 17/03/2023. // +import Ably +import AblyFlutterCommon +import Flutter import Foundation public class AuthHandlers: NSObject { diff --git a/ios/Classes/handlers/CryptoHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift similarity index 97% rename from ios/Classes/handlers/CryptoHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift index 33812ac8c..8a6e900ff 100644 --- a/ios/Classes/handlers/CryptoHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift @@ -1,5 +1,7 @@ -import Foundation import Ably +import AblyFlutterCommon +import Flutter +import Foundation public class CryptoHandlers: NSObject { @objc 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 98% rename from ios/Classes/handlers/PushActivationEventHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift index aeb242c38..d39d4b1e0 100644 --- a/ios/Classes/handlers/PushActivationEventHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift @@ -1,5 +1,7 @@ -import Foundation import Ably +import AblyFlutterCommon +import Flutter +import Foundation // 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. diff --git a/ios/Classes/handlers/PushHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift similarity index 96% rename from ios/Classes/handlers/PushHandlers.swift rename to ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift index d08d4ddcb..52d54ae4c 100644 --- a/ios/Classes/handlers/PushHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift @@ -1,3 +1,6 @@ +import Ably +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) } @@ -170,7 +173,7 @@ public class PushHandlers: NSObject { /// /// The dart side can provide a handle (Int) which gets a ARTRealtime 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) -> ARTPushChannel? { let ablyMessage = call.arguments as! AblyFlutterMessage /// Using `guard let handle` in that case caused compilation errors for some users diff --git a/ios/Classes/AblyFlutter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m similarity index 99% rename from ios/Classes/AblyFlutter.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m index c7a36b4dc..81bcda56a 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 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-cocoa " stringByAppendingString:[ARTDefault libraryVersion]]); }; static const FlutterHandler _resetAblyClients = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { 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 98% rename from ios/Classes/AblyFlutterStreamHandler.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m index a8d0cd4db..44240f5fd 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 Ably; +@import AblyFlutterCommon; #import "AblyFlutterStreamHandler.h" #import "AblyFlutter.h" -#import "AblyFlutterMessage.h" -#import "codec/AblyPlatformConstants.h" @implementation _AblyConnectionStateChange 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..1536ad474 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 Ably; 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 98% rename from ios/Classes/codec/AblyFlutterReader.m rename to ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m index 8bf9b1a7b..d5f38e94f 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 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; 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 fec6bb98c..294aa74db 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 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 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..d13b508b1 --- /dev/null +++ b/ios/run_unit_tests.sh @@ -0,0 +1,60 @@ +#!/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("13.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 + simulator_id="$(xcrun simctl list devices available | grep -E 'iPhone' | head -n 1 | sed -E 's/.*\(([A-F0-9-]+)\).*/\1/')" +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/test/ios_plugin_version_test.dart b/test/ios_plugin_version_test.dart new file mode 100644 index 000000000..1a2205121 --- /dev/null +++ b/test/ios_plugin_version_test.dart @@ -0,0 +1,23 @@ +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_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 10f3c9b47..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, '13.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 68d22c9f4..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_pubsub_device_flutter (1.2.44): - - Ably (= 1.2.53) - - Flutter - - AblyDeltaCodec (1.3.3) - - Flutter (1.0.0) - - msgpack (0.4.0) - -DEPENDENCIES: - - ably_pubsub_device_flutter (from `.symlinks/plugins/ably_pubsub_device_flutter/ios`) - - Flutter (from `Flutter`) - -SPEC REPOS: - trunk: - - Ably - - AblyDeltaCodec - - msgpack - -EXTERNAL SOURCES: - ably_pubsub_device_flutter: - :path: ".symlinks/plugins/ably_pubsub_device_flutter/ios" - Flutter: - :path: Flutter - -SPEC CHECKSUMS: - Ably: 1756d6590a572809c10fc4bcc32e05fe9e7867d7 - ably_pubsub_device_flutter: 03c57b59bcbf92ba2bd6910f10ed4e155bced176 - AblyDeltaCodec: add5d06a756b3581b12aab5b5500a320b8c55bea - Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 - msgpack: c85f6251873059738472ae136951cec5f30f3251 - -PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b - -COCOAPODS: 1.16.2 diff --git a/test_integration/ios/Runner.xcodeproj/project.pbxproj b/test_integration/ios/Runner.xcodeproj/project.pbxproj index 2006eada7..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 = 13.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 = 13.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 = 13.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..e7524d8a7 --- /dev/null +++ b/test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,41 @@ +{ + "pins" : [ + { + "identity" : "ably-cocoa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/ably-cocoa", + "state" : { + "revision" : "39da0cf191c9028d52fdc512911fb9fb27795b96", + "version" : "1.2.53" + } + }, + { + "identity" : "ably-cocoa-plugin-support", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/ably-cocoa-plugin-support", + "state" : { + "revision" : "8db4632b0664b7272d54f7b612ddad0a18d1758f", + "version" : "0.2.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 fc5ae0316..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 @@ + + + + + + + + + + - - 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..e7524d8a7 --- /dev/null +++ b/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,41 @@ +{ + "pins" : [ + { + "identity" : "ably-cocoa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/ably-cocoa", + "state" : { + "revision" : "39da0cf191c9028d52fdc512911fb9fb27795b96", + "version" : "1.2.53" + } + }, + { + "identity" : "ably-cocoa-plugin-support", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ably/ably-cocoa-plugin-support", + "state" : { + "revision" : "8db4632b0664b7272d54f7b612ddad0a18d1758f", + "version" : "0.2.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/pubspec.yaml b/test_integration/pubspec.yaml index a1645636c..68e4eb2d1 100644 --- a/test_integration/pubspec.yaml +++ b/test_integration/pubspec.yaml @@ -25,10 +25,4 @@ dev_dependencies: flutter_lints: ^1.0.4 flutter: - # Flutter 3.44 turns Swift Package Manager on by default, and building this - # app would migrate its Xcode project off CocoaPods on the spot. Hold that - # off so the migration can land as its own reviewable commit rather than as - # a side effect of a version bump. - config: - enable-swift-package-manager: false uses-material-design: true From 467e593082fb102684002470ee808466f7c7a3a1 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 30 Oct 2026 09:36:19 +0000 Subject: [PATCH 09/14] feat!: upgrade to the v2 native SDKs and replace host options with endpoint The iOS plugin now depends on ably-pubsub-cocoa 2.0.0 (AblyPubSubDevice) and the Android plugin on io.ably.pubsub:device 2.0.0, following both SDKs' v2 migration guides. Breaking changes: - ClientOptions.environment, restHost and realtimeHost are replaced by endpoint, which is all the v2 SDKs support. - Minimum iOS version is 15 and minimum Android API level is 24; building for iOS needs Xcode 26. - Releasing a channel that cannot be released returns error 90011 to Dart on both platforms. Also: - List push subscriptions with exactly one of deviceId or clientId, which the new iOS SDK otherwise raises an exception for. - Report failed push registration updates through didUpdateAblyPush. - Raise the example and test apps to Gradle 8.14.3 and AGP 8.12.1. - Use the nonprod:sandbox endpoint in the example and integration tests. - Run the iOS CI jobs on macos-26. - Document the migration in UPDATING.md. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/flutter_example_app.yaml | 2 +- .github/workflows/flutter_integration.yaml | 2 +- .github/workflows/ios_unit_tests.yml | 2 +- CONTRIBUTING.md | 10 +-- PushNotifications.md | 6 +- README.md | 6 +- UPDATING.md | 9 +++ android/build.gradle | 6 +- .../plugin/AblyEventStreamHandler.java | 24 +++---- .../io/ably/flutter/plugin/AblyFlutter.java | 2 +- .../flutter/plugin/AblyInstanceStore.java | 29 ++++---- .../ably/flutter/plugin/AblyMessageCodec.java | 50 +++++++------ .../flutter/plugin/AblyMethodCallHandler.java | 71 +++++++++++++------ .../flutter/plugin/AuthMethodHandler.java | 4 +- .../plugin/BackgroundMethodCallHandler.java | 2 +- .../plugin/MainThreadMethodResult.java | 2 +- .../ably/flutter/plugin/StreamsChannel.java | 2 +- .../dto/EnrichedConnectionStateChange.java | 2 +- .../plugin/generated/PlatformConstants.java | 4 +- .../push/FirebaseMessagingReceiver.java | 2 +- .../push/ManualFlutterApplicationRunner.java | 2 +- .../push/PushActivationEventHandlers.java | 4 +- .../plugin/push/PushActivationReceiver.java | 4 +- .../push/PushMessagingEventHandlers.java | 2 +- .../plugin/push/PushMessagingService.java | 4 +- .../plugin/types/PlatformClientOptions.java | 2 +- .../plugin/util/CipherParamsStorage.java | 2 +- bin/codegen_context.dart | 4 +- example/README.md | 2 +- example/android/gradle.properties | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 2 +- example/lib/app_provisioning.dart | 16 ++--- example/lib/constants.dart | 2 +- example/lib/ui/ably_service.dart | 4 +- ios/ably_pubsub_device_flutter/Package.swift | 12 ++-- .../AblyFlutterCommon/AblyInstanceStore.m | 14 ++-- .../AblyFlutterCommon/AblyPlatformConstants.m | 4 +- .../AblyFlutterClientOptions.h | 2 +- .../AblyFlutterCommon/AblyInstanceStore.h | 6 +- .../AblyFlutterCommon/AblyPlatformConstants.h | 4 +- .../AblyFlutterSwift/codec/CryptoCodec.swift | 38 +++++----- .../codec/encoders/ErrorInfo.swift | 4 +- .../encoders/PushNotificationEncoders.swift | 30 ++++++-- .../handlers/AuthHandlers.swift | 32 ++++----- .../handlers/CryptoHandlers.swift | 8 +-- .../PushActivationEventHandlers.swift | 15 ++-- .../handlers/PushHandlers.swift | 39 +++++----- .../ably_pubsub_device_flutter/AblyFlutter.m | 53 ++++++++------ .../AblyFlutterStreamHandler.m | 16 ++--- .../codec/AblyFlutterReader.h | 2 +- .../codec/AblyFlutterReader.m | 8 +-- .../codec/AblyFlutterWriter.m | 2 +- ios/run_unit_tests.sh | 2 +- .../authentication/src/client_options.dart | 27 ++++--- lib/src/generated/platform_constants.dart | 4 +- lib/src/platform/src/codec.dart | 16 +---- ...bly_pubsub_device_flutter_plugin_test.dart | 6 +- test/models/client_options.dart | 4 +- test_integration/android/gradle.properties | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- test_integration/android/settings.gradle | 2 +- .../xcshareddata/swiftpm/Package.resolved | 17 ++--- .../xcshareddata/swiftpm/Package.resolved | 17 ++--- test_integration/lib/app_provisioning.dart | 16 ++--- .../realtime_auth_client_id_test.dart | 4 +- .../test/realtime/realtime_auth_url_test.dart | 4 +- .../realtime/realtime_authorize_test.dart | 4 +- .../realtime_encrypted_publish_test.dart | 4 +- .../test/realtime/realtime_events_test.dart | 2 +- .../test/realtime/realtime_history_test.dart | 2 +- .../realtime_presence_enter_update_leave.dart | 2 +- .../test/realtime/realtime_presence_get.dart | 2 +- .../realtime_presence_history_test.dart | 2 +- .../realtime/realtime_presence_subscribe.dart | 2 +- .../test/realtime/realtime_publish_test.dart | 6 +- .../lib/test/realtime/realtime_subscribe.dart | 2 +- .../lib/test/realtime/realtime_time_test.dart | 2 +- 78 files changed, 385 insertions(+), 356 deletions(-) diff --git a/.github/workflows/flutter_example_app.yaml b/.github/workflows/flutter_example_app.yaml index 7b78a0a30..e223b4962 100644 --- a/.github/workflows/flutter_example_app.yaml +++ b/.github/workflows/flutter_example_app.yaml @@ -9,7 +9,7 @@ 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 diff --git a/.github/workflows/flutter_integration.yaml b/.github/workflows/flutter_integration.yaml index 8185c33d5..40778145c 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: diff --git a/.github/workflows/ios_unit_tests.yml b/.github/workflows/ios_unit_tests.yml index db1ac9429..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: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c85b41338..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 @@ -104,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 [ios/ably_pubsub_device_flutter/Package.swift](ios/ably_pubsub_device_flutter/Package.swift), temporarily replace the `ably-cocoa` dependency with a local one: +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: ```swift -.package(name: "ably-cocoa", path: "local/path/to/ably-cocoa"), +.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 diff --git a/PushNotifications.md b/PushNotifications.md index 6204c01c3..27f18f976 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 13+ +- 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. @@ -488,7 +488,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. diff --git a/README.md b/README.md index f7b9b1994..0d2c31498 100644 --- a/README.md +++ b/README.md @@ -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 13 or newer | +| 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. diff --git a/UPDATING.md b/UPDATING.md index fbacb7959..eb0f679e5 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -12,6 +12,15 @@ This guide lists the changes needed to upgrade from one version of Ably to a new - 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. + - 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))`. 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/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 710831a5d..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,12 +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.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 @@ -29,7 +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 realtimeInstances = new LongSparseArray<>(); + private final LongSparseArray realtimeInstances = new LongSparseArray<>(); private final LongSparseArray> paginatedResults = new LongSparseArray<>(); private final AtomicLong nextHandle = new AtomicLong(1); @@ -52,14 +53,14 @@ interface ClientHandle { long getHandle(); /** - * 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 * 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; } @@ -80,7 +81,9 @@ public long getHandle() { @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; @@ -99,7 +102,7 @@ synchronized ClientHandle reserveClientHandle() { return new ReservedClientHandle(nextHandle.getAndIncrement()); } - synchronized AblyRealtime getRealtime(final long handle) { + synchronized PubSubRealtimeClient getRealtime(final long handle) { return realtimeInstances.get(handle); } @@ -131,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) { 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 17ef43bd7..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 { @@ -330,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. @@ -341,7 +340,6 @@ 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); 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 83a32c311..929382e1c 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java @@ -20,23 +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.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; @@ -147,6 +148,25 @@ public void onError(ErrorInfo reason) { }; } + /** + * 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); + } + + @Override + public void onError(ErrorInfo reason) { + handleAblyException(result, AblyException.fromErrorInfo(reason)); + } + }; + } + private void resetAblyClients(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { streamsChannel.reset(); instanceStore.reset(); @@ -386,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); } @@ -396,8 +416,13 @@ private void releaseRealtimeChannel(@NonNull MethodCall call, @NonNull MethodCha final AblyFlutterMessage> ablyMessage = (AblyFlutterMessage>) call.arguments; final String channelName = (String) ablyMessage.message.get(PlatformConstants.TxTransportKeys.channelName); - instanceStore.getRealtime(ablyMessage.handle).channels.release(channelName); - result.success(null); + try { + // Throws 90011 unless the channel is INITIALIZED, DETACHED or FAILED (RTS4c-e). + instanceStore.getRealtime(ablyMessage.handle).channels.release(channelName); + result.success(null); + } catch (AblyException e) { + handleAblyException(result, e); + } } private void realtimeTime(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) { @@ -407,11 +432,11 @@ private void realtimeTime(@NonNull MethodCall methodCall, @NonNull MethodChannel 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) { @@ -533,7 +558,7 @@ 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; 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 b16d10eb9..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,8 +10,8 @@ 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; 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 fc081f349..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 @@ -150,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"; @@ -159,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"; 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 19728db41..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,6 +1,6 @@ package io.ably.flutter.plugin.types; -import io.ably.lib.types.ClientOptions; +import io.ably.pubsub.types.ClientOptions; public class PlatformClientOptions { 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/codegen_context.dart b/bin/codegen_context.dart index f62454a89..b6c4014b7 100644 --- a/bin/codegen_context.dart +++ b/bin/codegen_context.dart @@ -216,8 +216,7 @@ const List> _objects = [ 'clientId', 'logLevel', 'tls', - 'restHost', - 'realtimeHost', + 'endpoint', 'port', 'tlsPort', 'autoConnect', @@ -225,7 +224,6 @@ const List> _objects = [ 'queueMessages', 'echoMessages', 'recover', - 'environment', 'idempotentRestPublishing', 'disconnectedRetryTimeout', 'suspendedRetryTimeout', 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/lib/app_provisioning.dart b/example/lib/app_provisioning.dart index 7aa1b1c42..b5f263df1 100644 --- a/example/lib/app_provisioning.dart +++ b/example/lib/app_provisioning.dart @@ -10,11 +10,11 @@ import 'package:http/retry.dart'; /// A copy of this class is also present in test_integration package, to avoid /// 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 @@ -58,7 +58,7 @@ class AppProvisioning { /// URL used for app provisioning requests String get _provisioningUrl => - 'https://${environmentPrefix}rest.ably.io/apps'; + 'https://$host/apps'; /// URL used for getting token request String get _tokenRequestAuthUrl => 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/ui/ably_service.dart b/example/lib/ui/ably_service.dart index b918ec0aa..42aa7c475 100644 --- a/example/lib/ui/ably_service.dart +++ b/example/lib/ui/ably_service.dart @@ -15,9 +15,9 @@ class AblyService { 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, ), ); diff --git a/ios/ably_pubsub_device_flutter/Package.swift b/ios/ably_pubsub_device_flutter/Package.swift index 4012448b9..32c7d1c9f 100644 --- a/ios/ably_pubsub_device_flutter/Package.swift +++ b/ios/ably_pubsub_device_flutter/Package.swift @@ -16,8 +16,8 @@ import PackageDescription let package = Package( name: "ably_pubsub_device_flutter", platforms: [ - // UIScene, and therefore `FlutterSceneLifeCycleDelegate`, is iOS 13+ only. - .iOS("13.0") + // ably-pubsub-cocoa 2.0 requires iOS 15. + .iOS("15.0") ], products: [ .library(name: "ably-pubsub-device-flutter", targets: ["ably_pubsub_device_flutter"]) @@ -25,7 +25,7 @@ let package = Package( 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-cocoa", exact: "1.2.53"), + .package(url: "https://github.com/ably/ably-pubsub-cocoa", exact: "2.0.0"), ], targets: [ .target( @@ -34,7 +34,7 @@ let package = Package( "AblyFlutterCommon", "AblyFlutterSwift", .product(name: "FlutterFramework", package: "FlutterFramework"), - .product(name: "Ably", package: "ably-cocoa"), + .product(name: "AblyPubSubDevice", package: "ably-pubsub-cocoa"), ], resources: [ .process("PrivacyInfo.xcprivacy") @@ -48,14 +48,14 @@ let package = Package( dependencies: [ "AblyFlutterCommon", .product(name: "FlutterFramework", package: "FlutterFramework"), - .product(name: "Ably", package: "ably-cocoa"), + .product(name: "AblyPubSubDevice", package: "ably-pubsub-cocoa"), ] ), .target( name: "AblyFlutterCommon", dependencies: [ .product(name: "FlutterFramework", package: "FlutterFramework"), - .product(name: "Ably", package: "ably-cocoa"), + .product(name: "AblyPubSubDevice", package: "ably-pubsub-cocoa"), ], cSettings: [ // Lets the implementation files import their public headers by diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m index 3ba6b37f1..31edc9f7d 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyInstanceStore.m @@ -1,9 +1,9 @@ -@import Ably; +@import AblyPubSubDevice; #import "AblyInstanceStore.h" @implementation AblyInstanceStore { - NSMutableDictionary* _realtimeInstances; + NSMutableDictionary* _realtimeInstances; NSMutableDictionary* _paginatedResults; long long _nextHandle; } @@ -34,11 +34,11 @@ -(instancetype)init { return self; } --(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]; } @@ -59,13 +59,13 @@ -(void) didRegisterForRemoteNotificationsWithDeviceToken:(NSData *const) deviceT _didRegisterForRemoteNotificationsWithDeviceToken_deviceToken = deviceToken; 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]; diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m index b609ff857..9c9bab43a 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/AblyPlatformConstants.m @@ -112,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"; @@ -121,7 +120,6 @@ 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"; diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterClientOptions.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyFlutterClientOptions.h index 0a79fd86e..e93b13a70 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/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/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h index cac168bec..b738f9374 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyInstanceStore.h @@ -1,7 +1,7 @@ @import Foundation; @import Flutter; -@class ARTRealtime; +@class ARTPubSubClient; #import "AblyFlutterClientOptions.h" NS_ASSUME_NONNULL_BEGIN @@ -12,9 +12,9 @@ NS_ASSUME_NONNULL_BEGIN -(NSNumber *) getNextHandle; --(void)setRealtime:(ARTRealtime *const)realtime with:(NSNumber *const)handle; +-(void)setRealtime:(ARTPubSubClient *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/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h index beb9a8d81..ab333649e 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterCommon/include/AblyFlutterCommon/AblyPlatformConstants.h @@ -140,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; @@ -149,7 +148,6 @@ 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; diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift index 9696a30b3..b15ee2843 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/CryptoCodec.swift @@ -1,4 +1,4 @@ -import Ably +import AblyPubSubDevice import AblyFlutterCommon import Flutter import Foundation @@ -6,14 +6,14 @@ import Foundation 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, @@ -21,19 +21,19 @@ 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) } } @@ -41,33 +41,33 @@ public class CryptoCodec: NSObject { } } -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/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift index 42c355a72..09974e826 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/ErrorInfo.swift @@ -1,10 +1,10 @@ -import Ably +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/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift index a1f3212d2..e129be189 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/codec/encoders/PushNotificationEncoders.swift @@ -1,4 +1,4 @@ -import Ably +import AblyPubSubDevice import AblyFlutterCommon import Foundation import UserNotifications @@ -6,7 +6,7 @@ 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, @@ -18,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, @@ -42,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, @@ -165,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 index 0d5a74b77..31803ca33 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/AuthHandlers.swift @@ -5,7 +5,7 @@ // Created by Ikbal Kaya on 17/03/2023. // -import Ably +import AblyPubSubDevice import AblyFlutterCommon import Flutter import Foundation @@ -18,14 +18,14 @@ public class AuthHandlers: NSObject { 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? + var authOptions: AuthOptions? + var tokenParams: TokenParams? if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams + authOptions = dataMap[TxTransportKeys_options] as? AuthOptions + tokenParams = dataMap[TxTransportKeys_params] as? TokenParams } - realtime?.auth.createTokenRequest(tokenParams, options: authOptions, callback: { (tokenRequest: ARTTokenRequest?, error: Error?) in + realtime?.auth.createTokenRequest(tokenParams, options: authOptions, callback: { (tokenRequest: TokenRequest?, error: Error?) in if let error = error { result(error) return @@ -47,15 +47,15 @@ public class AuthHandlers: NSObject { 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? + var authOptions: AuthOptions? + var tokenParams: TokenParams? if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams + authOptions = dataMap[TxTransportKeys_options] as? AuthOptions + tokenParams = dataMap[TxTransportKeys_params] as? TokenParams } - realtime?.auth.authorize(tokenParams, options: authOptions, callback: { (details:ARTTokenDetails?, error: Error?) in + realtime?.auth.authorize(tokenParams, options: authOptions, callback: { (details:TokenDetails?, error: Error?) in if let error = error { result(error) return @@ -70,14 +70,14 @@ public class AuthHandlers: NSObject { 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? + var authOptions: AuthOptions? + var tokenParams: TokenParams? if let dataMap = ablyMessage.message as? Dictionary { - authOptions = dataMap[TxTransportKeys_options] as? ARTAuthOptions - tokenParams = dataMap[TxTransportKeys_params] as? ARTTokenParams + authOptions = dataMap[TxTransportKeys_options] as? AuthOptions + tokenParams = dataMap[TxTransportKeys_params] as? TokenParams } - realtime?.auth.requestToken(tokenParams, with: authOptions, callback: { (details: ARTTokenDetails?, error: Error?) in + realtime?.auth.requestToken(tokenParams, with: authOptions, callback: { (details: TokenDetails?, error: Error?) in if let error = error { result(error) return diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift index 8a6e900ff..4eb8583fd 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/CryptoHandlers.swift @@ -1,4 +1,4 @@ -import Ably +import AblyPubSubDevice import AblyFlutterCommon import Flutter import Foundation @@ -12,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)) @@ -29,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/PushActivationEventHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift index d39d4b1e0..d67dd309d 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushActivationEventHandlers.swift @@ -1,4 +1,4 @@ -import Ably +import AblyPubSubDevice import AblyFlutterCommon import Flutter import Foundation @@ -7,7 +7,7 @@ import Foundation // 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 @@ -25,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. @@ -33,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 } @@ -46,7 +46,7 @@ public class PushActivationEventHandlers: NSObject, ARTPushRegistererDelegate { } } - public func didDeactivateAblyPush(_ error: ARTErrorInfo?) { + public func didDeactivateAblyPush(_ error: ErrorInfo?) { defer { flutterResultForDeactivate = nil } @@ -59,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/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift index 52d54ae4c..0c18420c5 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift @@ -1,4 +1,4 @@ -import Ably +import AblyPubSubDevice import AblyFlutterCommon import Flutter import Foundation @@ -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)) + }) } } @@ -155,8 +158,8 @@ public class PushHandlers: NSObject { } } - /// Gets the client.push property from ARTRealtime 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) @@ -168,12 +171,12 @@ public class PushHandlers: NSObject { return nil; } - /// Gets the client.channels.get(channelName).push property from ARTRealtime + /// 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 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: AblyFlutterPluginContext, 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 diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m index 81bcda56a..4b2ece333 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m @@ -1,4 +1,4 @@ -@import Ably; +@import AblyPubSubDevice; @import AblyFlutterCommon; @import AblyFlutterSwift; @@ -29,7 +29,7 @@ -(void)reset; }; static const FlutterHandler _getVersion = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { - result([@"ably-cocoa " stringByAppendingString:[ARTDefault libraryVersion]]); + result([@"ably-pubsub-cocoa " stringByAppendingString:[ARTDefault libraryVersion]]); }; static const FlutterHandler _resetAblyClients = ^void(AblyFlutter *const ably, FlutterMethodCall *const call, const FlutterResult result) { @@ -70,7 +70,7 @@ -(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). @@ -80,9 +80,9 @@ -(void)reset; 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); @@ -112,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){ @@ -135,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){ @@ -159,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){ @@ -184,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) { @@ -208,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) { @@ -226,7 +226,7 @@ -(void)reset; }; if (dataQuery) { - [channel history:dataQuery callback:callback error: nil]; + [channel history:dataQuery callback:callback]; } else { [channel history:callback]; } @@ -239,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) { @@ -268,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) { @@ -285,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]; } @@ -299,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) { @@ -324,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) { @@ -349,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) { @@ -372,17 +372,24 @@ -(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]; - [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){ @@ -396,7 +403,7 @@ -(void)reset; 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); }; @@ -451,7 +458,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([ diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m index 44240f5fd..7cb3f86a9 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutterStreamHandler.m @@ -1,4 +1,4 @@ -@import Ably; +@import AblyPubSubDevice; @import AblyFlutterCommon; #import "AblyFlutterStreamHandler.h" @@ -47,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" @@ -62,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]; @@ -80,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]; @@ -98,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]; @@ -138,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]; @@ -146,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]; @@ -154,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/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h index 1536ad474..ff7c14c5a 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h @@ -1,6 +1,6 @@ @import Foundation; @import Flutter; -@import Ably; +@import AblyPubSubDevice; NS_ASSUME_NONNULL_BEGIN diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m index d5f38e94f..c53feed0a 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m @@ -1,4 +1,4 @@ -@import Ably; +@import AblyPubSubDevice; @import AblyFlutterCommon; @import AblyFlutterSwift; #import "AblyFlutterReader.h" @@ -125,14 +125,12 @@ 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); ON_VALUE(^(const id value) { @@ -255,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]; }; diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m index 294aa74db..4bbae55ed 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterWriter.m @@ -1,4 +1,4 @@ -@import Ably; +@import AblyPubSubDevice; @import UserNotifications; @import AblyFlutterCommon; @import AblyFlutterSwift; diff --git a/ios/run_unit_tests.sh b/ios/run_unit_tests.sh index d13b508b1..e870db14c 100755 --- a/ios/run_unit_tests.sh +++ b/ios/run_unit_tests.sh @@ -37,7 +37,7 @@ import PackageDescription let package = Package( name: "FlutterFramework", - platforms: [.iOS("13.0")], + platforms: [.iOS("15.0")], products: [ .library(name: "FlutterFramework", targets: ["FlutterFramework"]) ], diff --git a/lib/src/authentication/src/client_options.dart b/lib/src/authentication/src/client_options.dart index de2e59a4f..dc41c19c5 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -17,13 +17,18 @@ class ClientOptions extends AuthOptions { /// `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. @@ -72,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. /// @@ -158,7 +159,7 @@ class ClientOptions extends AuthOptions { this.defaultTokenParams, int? disconnectedRetryTimeout, bool? echoMessages, - this.environment, + this.endpoint, this.fallbackHosts, int? fallbackRetryTimeout, int? httpMaxRetryCount, @@ -170,10 +171,8 @@ class ClientOptions extends AuthOptions { this.port, bool? queryTime, bool? queueMessages, - this.realtimeHost, this.realtimeRequestTimeout, this.recover, - this.restHost, int? suspendedRetryTimeout, bool? tls, this.tlsPort, diff --git a/lib/src/generated/platform_constants.dart b/lib/src/generated/platform_constants.dart index e54a27094..1da98d7fb 100644 --- a/lib/src/generated/platform_constants.dart +++ b/lib/src/generated/platform_constants.dart @@ -155,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'; @@ -164,7 +163,6 @@ 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'; diff --git a/lib/src/platform/src/codec.dart b/lib/src/platform/src/codec.dart index c62056f10..55bd7ad74 100644 --- a/lib/src/platform/src/codec.dart +++ b/lib/src/platform/src/codec.dart @@ -249,8 +249,7 @@ class Codec extends StandardMessageCodec { _writeToJson( 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); @@ -259,7 +258,6 @@ class Codec extends StandardMessageCodec { _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.httpOpenTimeout, v.httpOpenTimeout); @@ -587,13 +585,9 @@ class Codec extends StandardMessageCodec { ), logLevel: _decodeLogLevel(jsonMap[TxClientOptions.logLevel] as String?), tls: jsonMap[TxClientOptions.tls] as bool, - restHost: _readFromJson( + endpoint: _readFromJson( jsonMap, - TxClientOptions.restHost, - ), - realtimeHost: _readFromJson( - jsonMap, - TxClientOptions.realtimeHost, + TxClientOptions.endpoint, ), port: _readFromJson( jsonMap, @@ -611,10 +605,6 @@ class Codec extends StandardMessageCodec { jsonMap, TxClientOptions.recover, ), - environment: _readFromJson( - jsonMap, - TxClientOptions.environment, - ), idempotentRestPublishing: _readFromJson( jsonMap, TxClientOptions.idempotentRestPublishing, diff --git a/test/ably_pubsub_device_flutter_plugin_test.dart b/test/ably_pubsub_device_flutter_plugin_test.dart index b3fb051a2..aedfe191d 100644 --- a/test/ably_pubsub_device_flutter_plugin_test.dart +++ b/test/ably_pubsub_device_flutter_plugin_test.dart @@ -52,13 +52,13 @@ void main() { }); test(PlatformMethod.createRealtime, () async { - const host = 'http://realtime.ably.io/'; + const endpoint = 'nonprod:sandbox'; final o = ClientOptions( - realtimeHost: host, + endpoint: endpoint, ); final realtime = createClient(options: o); expect(await realtime.handle, counter); - expect(realtime.options.realtimeHost, host); + expect(realtime.options.endpoint, endpoint); }); test('createRealtimeWithToken', () async { diff --git a/test/models/client_options.dart b/test/models/client_options.dart index 2f047e54d..ba5552c5a 100644 --- a/test/models/client_options.dart +++ b/test/models/client_options.dart @@ -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,7 +19,6 @@ 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); 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/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e7524d8a7..a0a678bef 100644 --- a/test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/test_integration/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,21 +1,12 @@ { "pins" : [ { - "identity" : "ably-cocoa", + "identity" : "ably-pubsub-cocoa", "kind" : "remoteSourceControl", - "location" : "https://github.com/ably/ably-cocoa", + "location" : "https://github.com/ably/ably-pubsub-cocoa", "state" : { - "revision" : "39da0cf191c9028d52fdc512911fb9fb27795b96", - "version" : "1.2.53" - } - }, - { - "identity" : "ably-cocoa-plugin-support", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ably/ably-cocoa-plugin-support", - "state" : { - "revision" : "8db4632b0664b7272d54f7b612ddad0a18d1758f", - "version" : "0.2.0" + "revision" : "c897becb1b0c644a9c67db89e7b247a083b6c540", + "version" : "2.0.0" } }, { diff --git a/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved index e7524d8a7..a0a678bef 100644 --- a/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/test_integration/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,21 +1,12 @@ { "pins" : [ { - "identity" : "ably-cocoa", + "identity" : "ably-pubsub-cocoa", "kind" : "remoteSourceControl", - "location" : "https://github.com/ably/ably-cocoa", + "location" : "https://github.com/ably/ably-pubsub-cocoa", "state" : { - "revision" : "39da0cf191c9028d52fdc512911fb9fb27795b96", - "version" : "1.2.53" - } - }, - { - "identity" : "ably-cocoa-plugin-support", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ably/ably-cocoa-plugin-support", - "state" : { - "revision" : "8db4632b0664b7272d54f7b612ddad0a18d1758f", - "version" : "0.2.0" + "revision" : "c897becb1b0c644a9c67db89e7b247a083b6c540", + "version" : "2.0.0" } }, { diff --git a/test_integration/lib/app_provisioning.dart b/test_integration/lib/app_provisioning.dart index 0277c1666..bba683c6d 100644 --- a/test_integration/lib/app_provisioning.dart +++ b/test_integration/lib/app_provisioning.dart @@ -10,11 +10,11 @@ import 'package:http/retry.dart'; /// A copy of this class is also present in example app, to avoid 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 @@ -58,7 +58,7 @@ class AppProvisioning { /// URL used for app provisioning requests String get _provisioningUrl => - 'https://${environmentPrefix}rest.ably.io/apps'; + 'https://$host/apps'; /// URL used for getting token request String get _tokenRequestAuthUrl => 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 108e67480..9fe3601d8 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 @@ -12,7 +12,7 @@ Future> testRealtimeAuthClientId({ //init ably for token final clientOptionsForToken = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', logLevel: LogLevel.error, autoConnect: false, ); @@ -25,7 +25,7 @@ Future> testRealtimeAuthClientId({ final clientOptions = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', useTokenAuth: true, autoConnect: false, clientId: 'testClientId', 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 4ac301158..5385ba7f4 100644 --- a/test_integration/lib/test/realtime/realtime_auth_url_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_url_test.dart @@ -15,7 +15,7 @@ Future> testCreateRealtimeWithAuthUrl({ final clientOptionsForToken = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', logLevel: LogLevel.error, autoConnect: false, fallbackHosts: ['a.ably-realtime.com', 'b.ably-realtime.com'], @@ -30,7 +30,7 @@ Future> testCreateRealtimeWithAuthUrl({ 'https://echo.ably.io/?body=${Uri.encodeComponent(tokenDetails.token!)}'; final options = ClientOptions( authUrl: authUrl, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', useTokenAuth: true, autoConnect: false, logLevel: LogLevel.error); diff --git a/test_integration/lib/test/realtime/realtime_authorize_test.dart b/test_integration/lib/test/realtime/realtime_authorize_test.dart index 07c0629d9..542e40309 100644 --- a/test_integration/lib/test/realtime/realtime_authorize_test.dart +++ b/test_integration/lib/test/realtime/realtime_authorize_test.dart @@ -12,7 +12,7 @@ Future> testRealtimeAuthroize({ //init ably for token final clientOptionsForToken = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', logLevel: LogLevel.error, autoConnect: false, ); @@ -25,7 +25,7 @@ Future> testRealtimeAuthroize({ final clientOptions = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', useTokenAuth: true, autoConnect: false, tokenDetails: firstToken); 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 d4f0e0a42..f753907be 100644 --- a/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart @@ -20,7 +20,7 @@ Future> testRealtimeEncryptedPublish({ final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', ), ); @@ -50,7 +50,7 @@ Future> testRealtimeEncryptedPublishSpec({ final realtimeWithClientId = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: clientId, logLevel: LogLevel.error, ), diff --git a/test_integration/lib/test/realtime/realtime_events_test.dart b/test_integration/lib/test/realtime/realtime_events_test.dart index fd6a1af03..8b42126ff 100644 --- a/test_integration/lib/test/realtime/realtime_events_test.dart +++ b/test_integration/lib/test/realtime/realtime_events_test.dart @@ -21,7 +21,7 @@ Future> testRealtimeEvents({ final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', autoConnect: false, ), diff --git a/test_integration/lib/test/realtime/realtime_history_test.dart b/test_integration/lib/test/realtime/realtime_history_test.dart index a5d3365b7..bcc222ce2 100644 --- a/test_integration/lib/test/realtime/realtime_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_history_test.dart @@ -15,7 +15,7 @@ Future> testRealtimeHistory({ 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_presence_enter_update_leave.dart b/test_integration/lib/test/realtime/realtime_presence_enter_update_leave.dart index 8d804622f..2b3822794 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 @@ -12,7 +12,7 @@ ClientOptions getClientOptions( ]) => ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: clientId, logLevel: LogLevel.error, ); diff --git a/test_integration/lib/test/realtime/realtime_presence_get.dart b/test_integration/lib/test/realtime/realtime_presence_get.dart index f3ba9e847..33c45c5b5 100644 --- a/test_integration/lib/test/realtime/realtime_presence_get.dart +++ b/test_integration/lib/test/realtime/realtime_presence_get.dart @@ -13,7 +13,7 @@ ClientOptions getClientOptions( ]) => ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: clientId, logLevel: LogLevel.error, ); 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 60085ae49..bf99eec88 100644 --- a/test_integration/lib/test/realtime/realtime_presence_history_test.dart +++ b/test_integration/lib/test/realtime/realtime_presence_history_test.dart @@ -15,7 +15,7 @@ Future> testRealtimePresenceHistory({ final options = ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ); diff --git a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart index d3b4d2ce7..4976ea2a0 100644 --- a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart @@ -21,7 +21,7 @@ Future> testRealtimePresenceSubscribe({ final presence = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), diff --git a/test_integration/lib/test/realtime/realtime_publish_test.dart b/test_integration/lib/test/realtime/realtime_publish_test.dart index 27e1eaed9..68ca34d4c 100644 --- a/test_integration/lib/test/realtime/realtime_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_publish_test.dart @@ -16,7 +16,7 @@ Future> testRealtimePublish({ final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), @@ -38,7 +38,7 @@ Future> testRealtimePublishSpec({ final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), @@ -84,7 +84,7 @@ Future> testRealtimePublishSpec({ final realtime2 = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', ), ); diff --git a/test_integration/lib/test/realtime/realtime_subscribe.dart b/test_integration/lib/test/realtime/realtime_subscribe.dart index 5812186f2..d094387f5 100644 --- a/test_integration/lib/test/realtime/realtime_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_subscribe.dart @@ -9,7 +9,7 @@ Future _createRealtime(String apiKey) async { final realtime = createClient( options: ClientOptions( key: apiKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', autoConnect: false, ), diff --git a/test_integration/lib/test/realtime/realtime_time_test.dart b/test_integration/lib/test/realtime/realtime_time_test.dart index 6de1b0fca..3c8e26d85 100644 --- a/test_integration/lib/test/realtime/realtime_time_test.dart +++ b/test_integration/lib/test/realtime/realtime_time_test.dart @@ -12,7 +12,7 @@ Future> testRealtimeTime({ final realtime = createClient( options: ClientOptions( key: appKey, - environment: 'sandbox', + endpoint: 'nonprod:sandbox', clientId: 'someClientId', logLevel: LogLevel.error, ), From 0bb0dc8e0950d59155be338596ecf60d8bec81e9 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 30 Oct 2026 09:36:32 +0000 Subject: [PATCH 10/14] feat: support the UIScene life cycle on iOS The plugin registers as a scene delegate as well as an application delegate, following the pattern firebase_messaging adopted: - Launch setup (registering for remote notifications) runs once, from didFinishLaunching, scene:willConnectToSession:, or plugin registration when a scene is already connected. - A notification tap that launches the app is read from the scene's connection options, so notificationTapLaunchedAppFromTerminated keeps working. - scene:willConnectToSession: returns NO so Flutter's cold-start deep link handling still runs. The example and integration test apps are migrated to UIScene, and PushNotifications.md documents it and refers to the Swift AppDelegate. Co-Authored-By: Claude Opus 5.5 --- PushNotifications.md | 20 ++++--- example/ios/Runner/AppDelegate.swift | 11 ++-- example/ios/Runner/Info.plist | 21 ++++++++ .../ably_pubsub_device_flutter/AblyFlutter.m | 53 +++++++++++++++++-- test_integration/ios/Runner/AppDelegate.swift | 9 ++-- test_integration/ios/Runner/Info.plist | 29 ++++++++-- 6 files changed, 124 insertions(+), 19 deletions(-) diff --git a/PushNotifications.md b/PushNotifications.md index 27f18f976..7a3719ae2 100644 --- a/PushNotifications.md +++ b/PushNotifications.md @@ -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 @@ -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 @@ -164,7 +172,7 @@ If you're working in Swift then this will look something like this in `AppDelega ```swift import ably_pubsub_device_flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { AblyInstanceStore.sharedInstance().didRegisterForRemoteNotifications(withDeviceToken: deviceToken) @@ -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: @@ -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. 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 ca132f81e..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 diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m index 4b2ece333..1a06b74cd 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m @@ -474,11 +474,17 @@ -(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; } @synthesize instanceStore = _instanceStore; @@ -504,6 +510,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) { @@ -575,6 +591,7 @@ -(instancetype)initWithChannel:(FlutterMethodChannel *const)channel }; [registrar addApplicationDelegate:self]; + [registrar addSceneDelegate:self]; return self; } @@ -597,10 +614,22 @@ -(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. +-(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) { @@ -610,6 +639,24 @@ - (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]; + + UNNotificationResponse *const response = connectionOptions.notificationResponse; + if (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. -(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 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 - From 65684d54de6f26af7c96b0ac459569e8419d8132 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 30 Oct 2026 09:36:35 +0000 Subject: [PATCH 11/14] fix: address review findings in the v2 and UIScene changes - iOS: call the auth callback once when the Dart authCallback returns null, instead of reporting an error and then an empty token. - iOS: always complete notificationTapLaunchedAppFromTerminated, with nil when there was no launch tap, and clear the tap once returned, as Android does. - iOS: only treat a notification tap as launching the app from the first scene connection; later scenes belong to an already running app. - Require exactly one of deviceId or clientId in listSubscriptions on both platforms, since ably-pubsub-cocoa supports no other filter. - Return a clear error when releasing a channel on an unknown client handle, instead of an empty error on iOS and a crash on Android. - Document why launch setup registers for remote notifications even when AblyFlutterHandlePushNotifications is NO. - Drop production fallback hosts from the sandbox auth URL test. - Make run_unit_tests.sh fail clearly when no iPhone simulator exists. Co-Authored-By: Claude Opus 5.5 --- UPDATING.md | 1 + .../flutter/plugin/AblyMethodCallHandler.java | 7 +++- .../handlers/PushHandlers.swift | 7 ++-- .../ably_pubsub_device_flutter/AblyFlutter.m | 22 ++++++++++- ios/run_unit_tests.sh | 10 ++++- .../push_notifications/src/push_channel.dart | 22 +++++------ .../push_notifications/push_channel_test.dart | 37 +++++++++++++++++++ .../test/realtime/realtime_auth_url_test.dart | 1 - 8 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 test/push_notifications/push_channel_test.dart diff --git a/UPDATING.md b/UPDATING.md index eb0f679e5..9b69a35d9 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -20,6 +20,7 @@ This guide lists the changes needed to upgrade from one version of Ably to a new - 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)`. 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 929382e1c..211ef41bf 100644 --- a/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java +++ b/android/src/main/java/io/ably/flutter/plugin/AblyMethodCallHandler.java @@ -415,10 +415,15 @@ 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; + } try { // Throws 90011 unless the channel is INITIALIZED, DETACHED or FAILED (RTS4c-e). - instanceStore.getRealtime(ablyMessage.handle).channels.release(channelName); + realtime.channels.release(channelName); result.success(null); } catch (AblyException e) { handleAblyException(result, e); diff --git a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift index 0c18420c5..287cd5484 100644 --- a/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift +++ b/ios/ably_pubsub_device_flutter/Sources/AblyFlutterSwift/handlers/PushHandlers.swift @@ -153,9 +153,10 @@ 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 PubSubClient when the call contains a handle. diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m index 1a06b74cd..43d65743a 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/AblyFlutter.m @@ -61,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 { @@ -373,6 +373,12 @@ -(void)reset; AblyInstanceStore *const instanceStore = [ably instanceStore]; 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; + } NSError *error; if ([realtime.channels release:channelName error:&error]) { @@ -485,6 +491,8 @@ @implementation AblyFlutter { 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; @@ -616,6 +624,10 @@ -(void)reset { /// 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; @@ -649,8 +661,14 @@ - (BOOL)scene:(UIScene *)scene 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 (response) { + if (isLaunchScene && response) { PushHandlers.pushNotificationTapLaunchedAppFromTerminatedData = response.notification.request.content.userInfo; } diff --git a/ios/run_unit_tests.sh b/ios/run_unit_tests.sh index e870db14c..8f69d7a66 100755 --- a/ios/run_unit_tests.sh +++ b/ios/run_unit_tests.sh @@ -50,7 +50,15 @@ EOF simulator_id="${1:-}" if [[ -z "$simulator_id" ]]; then - simulator_id="$(xcrun simctl list devices available | grep -E 'iPhone' | head -n 1 | sed -E 's/.*\(([A-F0-9-]+)\).*/\1/')" + # 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" diff --git a/lib/src/push_notifications/src/push_channel.dart b/lib/src/push_notifications/src/push_channel.dart index a304619ff..aa7b0b9b8 100644 --- a/lib/src/push_notifications/src/push_channel.dart +++ b/lib/src/push_notifications/src/push_channel.dart @@ -39,24 +39,22 @@ class PushChannel extends PlatformObject { /// 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. + // 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'", ); } 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_integration/lib/test/realtime/realtime_auth_url_test.dart b/test_integration/lib/test/realtime/realtime_auth_url_test.dart index 5385ba7f4..49bf99c11 100644 --- a/test_integration/lib/test/realtime/realtime_auth_url_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_url_test.dart @@ -18,7 +18,6 @@ Future> testCreateRealtimeWithAuthUrl({ endpoint: 'nonprod:sandbox', logLevel: LogLevel.error, autoConnect: false, - fallbackHosts: ['a.ably-realtime.com', 'b.ably-realtime.com'], ); final ablyForToken = createClient( From d5498055c5e3d2d40221b8420fa3a55d33ef40f1 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 2 Nov 2026 09:34:50 +0000 Subject: [PATCH 12/14] ci: check that the package can be published Run `flutter pub publish --dry-run` in the check workflow, so problems that would block the release workflow's publish to pub.dev fail on pull requests first. Also repeat the root .gitignore entries in .pubignore. pub reads .pubignore instead of .gitignore, so a publish from a local checkout included the gitignored build/ directory, taking the archive to 93 MB against pub.dev's 100 MB limit. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/check.yaml | 7 +++++++ .pubignore | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index f4c42dc4f..c8aba2fd2 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -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/.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/ From fc02ea1517d28f0642fb590c65ef56021c6c643f Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 2 Nov 2026 09:35:15 +0000 Subject: [PATCH 13/14] style: reformat Dart code for the 3.12 language version Raising the SDK constraint to >=3.12.0 switched dart format to the tall style, so the check workflow's formatting step failed. This is the output of `dart format .` with no other changes. Co-Authored-By: Claude Opus 5.5 --- bin/codegen_context.dart | 129 ++--- bin/templates/platformconstants.dart.dart | 3 +- bin/templates/platformconstants.h.dart | 3 +- bin/templates/platformconstants.java.dart | 3 +- bin/templates/platformconstants.m.dart | 3 +- example/lib/app_provisioning.dart | 50 +- example/lib/main.dart | 40 +- example/lib/nested_realtime_events.dart | 21 +- example/lib/op_state.dart | 7 +- ...droid_push_notification_configuration.dart | 3 +- .../push_notification_handlers.dart | 35 +- .../push_notification_message_examples.dart | 80 +-- .../push_notification_service.dart | 48 +- example/lib/ui/ably_service.dart | 38 +- example/lib/ui/api_key_service.dart | 15 +- example/lib/ui/auth.dart | 31 +- example/lib/ui/bool_stream_button.dart | 13 +- example/lib/ui/paginated_result_viewer.dart | 28 +- .../push_notifications_activation_sliver.dart | 114 ++-- ...push_notifications_device_information.dart | 103 ++-- ..._notifications_ios_permissions_sliver.dart | 56 +- .../push_notifications_publishing_sliver.dart | 69 +-- .../push_notifications_received_sliver.dart | 103 ++-- .../push_notifications_sliver.dart | 82 +-- ...sh_notifications_subscriptions_sliver.dart | 304 +++++----- .../push_realtime_client_received_sliver.dart | 63 ++- example/lib/ui/realtime_presence_sliver.dart | 146 ++--- example/lib/ui/realtime_sliver.dart | 353 ++++++------ example/lib/ui/system_details_sliver.dart | 37 +- example/lib/ui/text_row.dart | 17 +- example/lib/ui/utilities.dart | 22 +- lib/src/authentication/src/auth_options.dart | 4 +- .../authentication/src/client_options.dart | 38 +- lib/src/authentication/src/token_details.dart | 10 +- lib/src/authentication/src/token_request.dart | 15 +- lib/src/crypto/src/crypto.dart | 29 +- lib/src/error/src/ably_exception.dart | 12 +- lib/src/error/src/error_info.dart | 3 +- lib/src/message/src/delta_extras.dart | 9 +- lib/src/message/src/message.dart | 56 +- lib/src/message/src/message_data.dart | 2 +- lib/src/message/src/message_extras.dart | 10 +- lib/src/message/src/presence_message.dart | 66 ++- lib/src/platform/src/ably_event_message.dart | 5 +- lib/src/platform/src/ably_message.dart | 25 +- .../background_android_isolate_platform.dart | 8 +- .../platform/src/cipher_params_internal.dart | 19 +- lib/src/platform/src/codec.dart | 521 ++++++++++-------- lib/src/platform/src/method_call_handler.dart | 18 +- lib/src/platform/src/paginated_result.dart | 22 +- lib/src/platform/src/platform.dart | 48 +- lib/src/platform/src/platform_object.dart | 30 +- .../push_notification_events_internal.dart | 18 +- lib/src/platform/src/realtime/connection.dart | 15 +- lib/src/platform/src/realtime/presence.dart | 24 +- .../platform/src/realtime/pub_sub_client.dart | 14 +- .../src/realtime/realtime_channel.dart | 43 +- lib/src/platform/src/streams_channel.dart | 109 ++-- .../src/admin/push_device_registrations.dart | 14 +- .../src/device_push_details.dart | 6 +- .../src/ios_notification_settings.dart | 18 +- .../push_notifications/src/local_device.dart | 14 +- .../push_notifications/src/notification.dart | 11 +- lib/src/push_notifications/src/push.dart | 3 +- .../push_notifications/src/push_channel.dart | 26 +- .../src/push_channel_subscriptions.dart | 4 +- .../src/push_notification_events.dart | 3 +- .../src/remote_message.dart | 31 +- .../realtime/src/connection_state_change.dart | 7 +- lib/src/realtime/src/realtime_auth.dart | 42 +- .../src/realtime_channel_options.dart | 6 +- .../realtime/src/realtime_history_params.dart | 9 +- test/ably_event_listener_test.dart | 5 +- ...bly_pubsub_device_flutter_plugin_test.dart | 40 +- test/ios_plugin_version_test.dart | 17 +- test/mock_method_call_manager.dart | 26 +- test/models/message_test.dart | 43 +- test/models/presence_message_test.dart | 70 ++- test/realtime/channel_test.dart | 33 +- test/realtime/channels_test.dart | 24 +- test_integration/lib/app_provisioning.dart | 50 +- test_integration/lib/config/test_factory.dart | 9 +- test_integration/lib/driver_data_handler.dart | 18 +- .../lib/factory/error_handler.dart | 16 +- test_integration/lib/factory/reporter.dart | 6 +- test_integration/lib/test/basic_test.dart | 5 +- ...ypto_ensure_supported_key_length_test.dart | 5 +- .../crypto/crypto_get_default_params.dart | 5 +- .../realtime_auth_client_id_test.dart | 17 +- .../test/realtime/realtime_auth_url_test.dart | 21 +- .../realtime/realtime_authorize_test.dart | 35 +- .../realtime_encrypted_publish_test.dart | 44 +- .../test/realtime/realtime_events_test.dart | 11 +- ...ltime_history_with_auth_callback_test.dart | 4 +- .../realtime_presence_enter_update_leave.dart | 28 +- .../test/realtime/realtime_presence_get.dart | 13 +- .../realtime/realtime_presence_subscribe.dart | 12 +- .../test/realtime/realtime_publish_test.dart | 23 +- ...ltime_publish_with_auth_callback_test.dart | 4 +- .../lib/test/realtime/realtime_subscribe.dart | 4 +- test_integration/lib/test_dispatcher.dart | 130 ++--- test_integration/lib/utils/data.dart | 40 +- test_integration/lib/utils/encoders.dart | 113 ++-- test_integration/lib/utils/realtime.dart | 15 +- test_integration/test_driver/runner.dart | 8 +- .../basic_platform_tests.dart | 6 +- .../test_implementation/crypto_tests.dart | 25 +- .../test_implementation/helper_tests.dart | 3 +- .../test_implementation/realtime_tests.dart | 441 ++++++++------- .../test_implementation/utils.dart | 28 +- .../test_driver/tests_abstract.dart | 15 +- .../test_driver/tests_config.dart | 67 +-- 112 files changed, 2533 insertions(+), 2332 deletions(-) diff --git a/bin/codegen_context.dart b/bin/codegen_context.dart index b6c4014b7..fbfcd9049 100644 --- a/bin/codegen_context.dart +++ b/bin/codegen_context.dart @@ -78,7 +78,7 @@ const List> _platformMethods = [ {'name': 'onRealtimePresenceMessage', 'value': 'onRealtimePresenceMessage'}, { 'name': 'publishRealtimeChannelMessage', - 'value': 'publishRealtimeChannelMessage' + 'value': 'publishRealtimeChannelMessage', }, {'name': 'releaseRealtimeChannel', 'value': 'releaseRealtimeChannel'}, {'name': 'realtimeHistory', 'value': 'realtimeHistory'}, @@ -86,7 +86,7 @@ const List> _platformMethods = [ {'name': 'realtimeAuthAuthorize', 'value': 'realtimeAuthAuthorize'}, { 'name': 'realtimeAuthCreateTokenRequest', - 'value': 'realtimeAuthCreateTokenRequest' + 'value': 'realtimeAuthCreateTokenRequest', }, {'name': 'realtimeAuthRequestToken', 'value': 'realtimeAuthRequestToken'}, {'name': 'realtimeAuthGetClientId', 'value': 'realtimeAuthGetClientId'}, @@ -107,7 +107,7 @@ const List> _platformMethods = [ // Used only on iOS { 'name': 'pushGetNotificationSettings', - 'value': 'pushGetNotificationSettings' + 'value': 'pushGetNotificationSettings', }, // Used only on iOS {'name': 'pushOpenSettingsFor', 'value': 'pushOpenSettingsFor'}, @@ -118,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'}, @@ -130,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'}, @@ -166,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', @@ -184,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', @@ -238,11 +238,11 @@ const List> _objects = [ 'channelRetryTimeout', 'transportParams', 'dartVersion', - ] + ], }, { 'name': 'RealtimeChannelOptions', - 'properties': ['params', 'modes', 'cipherParams'] + 'properties': ['params', 'modes', 'cipherParams'], }, { 'name': 'CipherParams', @@ -255,8 +255,8 @@ const List> _objects = [ 'expires', 'issued', 'capability', - 'clientId' - ] + 'clientId', + ], }, { 'name': 'AuthOptions', @@ -270,8 +270,8 @@ const List> _objects = [ 'authHeaders', 'authParams', 'queryTime', - 'useTokenAuth' - ] + 'useTokenAuth', + ], }, { 'name': 'TokenParams', @@ -280,8 +280,8 @@ const List> _objects = [ 'clientId', 'nonce', 'timestamp', - 'ttl' - ] + 'ttl', + ], }, { 'name': 'TokenRequest', @@ -292,8 +292,8 @@ const List> _objects = [ 'mac', 'nonce', 'timestamp', - 'ttl' - ] + 'ttl', + ], }, { 'name': 'EnumConstants', @@ -321,8 +321,8 @@ const List> _objects = [ 'presence', 'publish', 'subscribe', - 'presenceSubscribe' - ] + 'presenceSubscribe', + ], }, { 'name': 'FormFactorEnum', @@ -334,8 +334,8 @@ const List> _objects = [ 'watch', 'car', 'embedded', - 'other' - ] + 'other', + ], }, { 'name': 'LogLevelEnum', @@ -343,11 +343,11 @@ 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', @@ -358,12 +358,12 @@ const List> _objects = [ 'retryIn', 'reason', 'connectionId', - 'connectionKey' - ] + 'connectionKey', + ], }, { 'name': 'ChannelStateChange', - 'properties': ['current', 'previous', 'event', 'resumed', 'reason'] + 'properties': ['current', 'previous', 'event', 'resumed', 'reason'], }, { 'name': 'Message', @@ -375,8 +375,8 @@ const List> _objects = [ 'encoding', 'data', 'name', - 'extras' - ] + 'extras', + ], }, { 'name': 'PresenceMessage', @@ -389,29 +389,19 @@ const List> _objects = [ 'encoding', 'extras', 'timestamp', - ] + ], }, { 'name': 'PaginatedResult', - 'properties': ['items', 'type', 'hasNext'] + 'properties': ['items', 'type', 'hasNext'], }, { 'name': 'RealtimeHistoryParams', - 'properties': [ - 'start', - 'end', - 'direction', - 'limit', - 'untilAttach', - ] + 'properties': ['start', 'end', 'direction', 'limit', 'untilAttach'], }, { 'name': 'RealtimePresenceParams', - 'properties': [ - 'waitForSync', - 'clientId', - 'connectionId', - ] + 'properties': ['waitForSync', 'clientId', 'connectionId'], }, { 'name': 'DeviceDetails', @@ -421,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', @@ -447,7 +437,7 @@ const List> _objects = [ 'providesAppNotificationSettings', 'provisional', 'announcement', - ] + ], }, { 'name': 'UNNotificationSettings', @@ -466,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', @@ -484,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 @@ -516,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 4f46c5b33..b9f7dd00a 100644 --- a/bin/templates/platformconstants.dart.dart +++ b/bin/templates/platformconstants.dart.dart @@ -1,6 +1,7 @@ String getPrefix(String str) => (str.length > 26) ? '\n ' : ' '; -String $(Map c) => ''' +String $(Map c) => + ''' // ignore_for_file: public_member_api_docs class CodecTypes { diff --git a/bin/templates/platformconstants.h.dart b/bin/templates/platformconstants.h.dart index 45f75a43f..d7aab37b1 100644 --- a/bin/templates/platformconstants.h.dart +++ b/bin/templates/platformconstants.h.dart @@ -1,4 +1,5 @@ -String $(Map c) => ''' +String $(Map c) => + ''' @import Foundation; typedef NS_ENUM(UInt8, CodecType) { diff --git a/bin/templates/platformconstants.java.dart b/bin/templates/platformconstants.java.dart index 7746af19f..266d05a47 100644 --- a/bin/templates/platformconstants.java.dart +++ b/bin/templates/platformconstants.java.dart @@ -1,4 +1,5 @@ -String $(Map c) => ''' +String $(Map c) => + ''' package io.ably.flutter.plugin.generated; diff --git a/bin/templates/platformconstants.m.dart b/bin/templates/platformconstants.m.dart index c2def17d4..2fa5dd92d 100644 --- a/bin/templates/platformconstants.m.dart +++ b/bin/templates/platformconstants.m.dart @@ -1,4 +1,5 @@ -String $(Map c) => ''' +String $(Map c) => + ''' #import "AblyPlatformConstants.h" diff --git a/example/lib/app_provisioning.dart b/example/lib/app_provisioning.dart index b5f263df1..0ed307ee2 100644 --- a/example/lib/app_provisioning.dart +++ b/example/lib/app_provisioning.dart @@ -57,8 +57,7 @@ class AppProvisioning { ); /// URL used for app provisioning requests - String get _provisioningUrl => - 'https://$host/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/main.dart b/example/lib/main.dart index b03292aa4..d076ccf05 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -21,31 +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(), - 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 25ae97278..818842da4 100644 --- a/example/lib/nested_realtime_events.dart +++ b/example/lib/nested_realtime_events.dart @@ -16,16 +16,20 @@ void listenRealtimeConnection(ably.PubSubClient realtime) { // 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(); } @@ -33,8 +37,10 @@ void listenRealtimeConnection(ably.PubSubClient 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) { @@ -55,7 +61,8 @@ void listenRealtimeConnection(ably.PubSubClient 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 73dfe776e..f688a8beb 100644 --- a/example/lib/push_notifications/push_notification_handlers.dart +++ b/example/lib/push_notifications/push_notification_handlers.dart @@ -10,7 +10,7 @@ class PushNotificationHandlers { static final notificationEvents = ably.Push.notificationEvents; static final BehaviorSubject> - _receivedMessagesBehaviorSubject = + _receivedMessagesBehaviorSubject = BehaviorSubject>.seeded([]); static ValueStream> receivedMessagesStream = @@ -18,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'); @@ -40,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; }); @@ -59,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'); @@ -74,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 bbb0be931..24f78115f 100644 --- a/example/lib/push_notifications/push_notification_message_examples.dart +++ b/example/lib/push_notifications/push_notification_message_examples.dart @@ -3,48 +3,54 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' 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 90364f9c6..0161962fb 100644 --- a/example/lib/push_notifications/push_notification_service.dart +++ b/example/lib/push_notifications/push_notification_service.dart @@ -24,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); @@ -55,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) { @@ -66,12 +66,14 @@ class PushNotificationService { } /// Only valid on iOS - Future requestNotificationPermission( - {bool provisional = false, - bool providesAppNotificationSettings = true}) async { + Future requestNotificationPermission({ + bool provisional = false, + bool providesAppNotificationSettings = true, + }) async { final granted = await _realtime.push.requestPermission( - provisional: provisional, - providesAppNotificationSettings: providesAppNotificationSettings); + provisional: provisional, + providesAppNotificationSettings: providesAppNotificationSettings, + ); _userNotificationPermissionGrantedSubject.add(granted); await updateNotificationSettings(); } @@ -136,19 +138,22 @@ class PushNotificationService { Future publishNotificationMessageToChannel() async { await ensureRealtimeClientConnected(); await _realtimeChannel!.publish( - message: PushNotificationMessageExamples.pushNotificationMessage); + message: PushNotificationMessageExamples.pushNotificationMessage, + ); } Future publishDataMessageToChannel() async { await ensureRealtimeClientConnected(); - await _realtimeChannel! - .publish(message: PushNotificationMessageExamples.pushDataMessage); + await _realtimeChannel!.publish( + message: PushNotificationMessageExamples.pushDataMessage, + ); } Future publishDataNotificationMessageToChannel() async { await ensureRealtimeClientConnected(); await _realtimeChannel!.publish( - message: PushNotificationMessageExamples.pushDataNotificationMessage); + message: PushNotificationMessageExamples.pushDataNotificationMessage, + ); } void close() { @@ -163,8 +168,9 @@ class PushNotificationService { void _getChannels() { _hasPushChannelSubject.add(false); - _realtimeChannel = - _realtime.channels.get(Constants.channelNameForPushNotifications); + _realtimeChannel = _realtime.channels.get( + Constants.channelNameForPushNotifications, + ); _pushChannel = _realtimeChannel!.push; _pushLogMetaChannel = _realtime.channels.get(Constants.pushMetaChannelName); _hasPushChannelSubject.add(true); @@ -175,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!, @@ -187,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 42aa7c475..5f2788ecb 100644 --- a/example/lib/ui/ably_service.dart +++ b/example/lib/ui/ably_service.dart @@ -26,27 +26,31 @@ class AblyService { } 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 03b0c5951..59e1a10d8 100644 --- a/example/lib/ui/paginated_result_viewer.dart +++ b/example/lib/ui/paginated_result_viewer.dart @@ -25,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; @@ -44,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; @@ -74,7 +75,7 @@ class PaginatedResultViewer extends HookWidget { onPressed: () => getFirstPaginatedResult(currentPaginatedResult, pageNumber), icon: const Icon(Icons.refresh), - ) + ), ], ), if (subtitle != null) @@ -89,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( @@ -113,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 97ee29a86..b05f7dc25 100644 --- a/example/lib/ui/push_notifications/push_notifications_activation_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_activation_sliver.dart @@ -16,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}', + ), ), ); } @@ -67,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(); @@ -88,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; }); @@ -109,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.', + ), ], ), ); @@ -140,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'), @@ -174,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 abcef9286..1ca8d919f 100644 --- a/example/lib/ui/push_notifications/push_notifications_device_information.dart +++ b/example/lib/ui/push_notifications/push_notifications_device_information.dart @@ -10,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 73a77f0da..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 @@ -14,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) { @@ -43,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)'), ); @@ -81,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, @@ -115,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 066750cb4..d93522047 100644 --- a/example/lib/ui/push_notifications/push_notifications_received_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_received_sliver.dart @@ -5,60 +5,65 @@ import 'package:ably_pubsub_device_flutter/ably_pubsub_device_flutter.dart' 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 380f0bb22..283e24756 100644 --- a/example/lib/ui/push_notifications/push_notifications_subscriptions_sliver.dart +++ b/example/lib/ui/push_notifications/push_notifications_subscriptions_sliver.dart @@ -12,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', + ), + ], + ), ), ); } @@ -181,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 6cfac8e68..2472de803 100644 --- a/example/lib/ui/realtime_presence_sliver.dart +++ b/example/lib/ui/realtime_presence_sliver.dart @@ -24,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, @@ -75,10 +76,10 @@ class RealtimePresenceSliver extends HookWidget { 1, 'ably', null, - {'a': 'b'} + {'a': 'b'}, ], { - 'c': ['a', 'b'] + 'c': ['a', 'b'], }, ]; @@ -89,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) { @@ -129,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( @@ -142,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), @@ -158,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 bd0e7a59e..59be800de 100644 --- a/example/lib/ui/realtime_sliver.dart +++ b/example/lib/ui/realtime_sliver.dart @@ -19,8 +19,8 @@ class RealtimeSliver extends HookWidget { 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); } @@ -31,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; @@ -103,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); @@ -128,9 +134,7 @@ class RealtimeSliver extends HookWidget { } } : null, - child: const Text( - 'Publish', - ), + child: const Text('Publish'), ); Widget buildReleaseRealtimeChannelButton( @@ -138,55 +142,53 @@ class RealtimeSliver extends HookWidget { ValueNotifier channelState, ValueNotifier connectionId, ValueNotifier recoveryKey, - ) => - 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'), - ); + ) => 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); @@ -210,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( @@ -226,92 +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.'), - ], - ), - 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) { @@ -328,8 +341,8 @@ List _messagesToPublish = [ 'I am': null, 'and': { 'also': 'nested', - 'too': {'deep': true} - } + 'too': {'deep': true}, + }, }, [ 42, @@ -340,8 +353,8 @@ List _messagesToPublish = [ 'I am': null, 'and': { 'also': 'nested', - 'too': {'deep': true} - } - } - ] + 'too': {'deep': true}, + }, + }, + ], ]; diff --git a/example/lib/ui/system_details_sliver.dart b/example/lib/ui/system_details_sliver.dart index ae106c7bf..3a5a27a9e 100644 --- a/example/lib/ui/system_details_sliver.dart +++ b/example/lib/ui/system_details_sliver.dart @@ -10,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) { @@ -40,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 2d044998a..37d778fc2 100644 --- a/example/lib/ui/utilities.dart +++ b/example/lib/ui/utilities.dart @@ -7,20 +7,24 @@ 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/lib/src/authentication/src/auth_options.dart b/lib/src/authentication/src/auth_options.dart index f246077be..d3a4f2915 100644 --- a/lib/src/authentication/src/auth_options.dart +++ b/lib/src/authentication/src/auth_options.dart @@ -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 diff --git a/lib/src/authentication/src/client_options.dart b/lib/src/authentication/src/client_options.dart index dc41c19c5..7c4cdfb70 100644 --- a/lib/src/authentication/src/client_options.dart +++ b/lib/src/authentication/src/client_options.dart @@ -181,16 +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, - ) { + 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 = @@ -209,13 +209,13 @@ class ClientOptions extends AuthOptions { this.useBinaryProtocol = useBinaryProtocol ?? this.useBinaryProtocol; } -// 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_request.dart b/lib/src/authentication/src/token_request.dart index e455a70c8..47f5aa38c 100644 --- a/lib/src/authentication/src/token_request.dart +++ b/lib/src/authentication/src/token_request.dart @@ -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/crypto/src/crypto.dart b/lib/src/crypto/src/crypto.dart index b08aa36ed..602288dd8 100644 --- a/lib/src/crypto/src/crypto.dart +++ b/lib/src/crypto/src/crypto.dart @@ -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 9cc9ac8c2..a886cbed7 100644 --- a/lib/src/error/src/ably_exception.dart +++ b/lib/src/error/src/ably_exception.dart @@ -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/message/src/delta_extras.dart b/lib/src/message/src/delta_extras.dart index a7b9edacc..a85ff7dfe 100644 --- a/lib/src/message/src/delta_extras.dart +++ b/lib/src/message/src/delta_extras.dart @@ -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 fae192dc1..71493ca35 100644 --- a/lib/src/message/src/message.dart +++ b/lib/src/message/src/message.dart @@ -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 // @@ -84,22 +84,20 @@ class Message with ObjectHash { Message.fromEncoded( Map jsonObject, [ 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; + ]) : 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 @@ -108,11 +106,11 @@ class Message with ObjectHash { static List fromEncodedArray( List> jsonArray, [ RealtimeChannelOptions? channelOptions, - ]) => - jsonArray.map((e) => Message.fromEncoded(e, channelOptions)).toList(); + ]) => 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 30005f2e5..179164447 100644 --- a/lib/src/message/src/message_extras.dart +++ b/lib/src/message/src/message_extras.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 7288487f0..d08d0d4a0 100644 --- a/lib/src/message/src/presence_message.dart +++ b/lib/src/message/src/presence_message.dart @@ -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 // @@ -93,23 +93,22 @@ class PresenceMessage with ObjectHash { PresenceMessage.fromEncoded( Map jsonObject, [ 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; + ]) : 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]. @@ -121,16 +120,15 @@ class PresenceMessage with ObjectHash { static List fromEncodedArray( List> jsonArray, [ RealtimeChannelOptions? channelOptions, - ]) => - jsonArray - .map((jsonObject) => PresenceMessage.fromEncoded( - jsonObject, - channelOptions, - )) - .toList(); + ]) => 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/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 aa81aa7a9..bea8d5171 100644 --- a/lib/src/platform/src/ably_message.dart +++ b/lib/src/platform/src/ably_message.dart @@ -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 3f01fe936..6a872bf41 100644 --- a/lib/src/platform/src/background_android_isolate_platform.dart +++ b/lib/src/platform/src/background_android_isolate_platform.dart @@ -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 330ccdb8c..5a778dfbc 100644 --- a/lib/src/platform/src/cipher_params_internal.dart +++ b/lib/src/platform/src/cipher_params_internal.dart @@ -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 55bd7ad74..c57092d49 100644 --- a/lib/src/platform/src/codec.dart +++ b/lib/src/platform/src/codec.dart @@ -27,9 +27,7 @@ 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); @@ -41,9 +39,7 @@ 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); @@ -66,46 +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.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.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, @@ -114,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, + ), }; } @@ -236,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); @@ -247,33 +282,60 @@ class Codec extends StandardMessageCodec { // ClientOptions _writeToJson(jsonMap, TxClientOptions.clientId, v.clientId); _writeToJson( - jsonMap, TxClientOptions.logLevel, _encodeLogLevel(v.logLevel)); + jsonMap, + TxClientOptions.logLevel, + _encodeLogLevel(v.logLevel), + ); _writeToJson(jsonMap, TxClientOptions.tls, v.tls); _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.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.realtimeRequestTimeout, - v.realtimeRequestTimeout); + jsonMap, + TxClientOptions.httpMaxRetryCount, + v.httpMaxRetryCount, + ); + _writeToJson( + jsonMap, + TxClientOptions.realtimeRequestTimeout, + v.realtimeRequestTimeout, + ); _writeToJson(jsonMap, TxClientOptions.fallbackHosts, v.fallbackHosts); _writeToJson( - jsonMap, TxClientOptions.fallbackRetryTimeout, v.fallbackRetryTimeout); - _writeToJson(jsonMap, TxClientOptions.defaultTokenParams, - _encodeTokenParams(v.defaultTokenParams)); + jsonMap, + TxClientOptions.fallbackRetryTimeout, + v.fallbackRetryTimeout, + ); + _writeToJson( + 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; @@ -318,7 +380,10 @@ 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; } @@ -342,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, @@ -357,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; @@ -388,30 +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'); } } 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; } @@ -461,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); @@ -537,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( + final tokenDetails = toJsonMap( + _readFromJson>( jsonMap, - TxClientOptions.authUrl, + TxClientOptions.tokenDetails, ), - authMethod: _readFromJson( - jsonMap, - TxClientOptions.authMethod, - ), - 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, @@ -569,42 +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?), tls: jsonMap[TxClientOptions.tls] as bool, - endpoint: _readFromJson( - jsonMap, - TxClientOptions.endpoint, - ), - 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, - ), + recover: _readFromJson(jsonMap, TxClientOptions.recover), idempotentRestPublishing: _readFromJson( jsonMap, TxClientOptions.idempotentRestPublishing, @@ -620,8 +660,9 @@ class Codec extends StandardMessageCodec { ), 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, @@ -685,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, @@ -701,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, + ), ), ); } @@ -726,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, @@ -737,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) { @@ -797,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) { @@ -848,7 +908,8 @@ class Codec extends StandardMessageCodec { return UNShowPreviewsSetting.never; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNShowPreviewsSetting is invalid: $setting', ); } @@ -863,7 +924,8 @@ class Codec extends StandardMessageCodec { return UNAlertStyle.none; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNAlertStyle is invalid: $style', ); } @@ -882,7 +944,8 @@ class Codec extends StandardMessageCodec { return UNAuthorizationStatus.ephemeral; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNAuthorizationStatus is invalid: $status', ); } @@ -897,7 +960,8 @@ class Codec extends StandardMessageCodec { return UNNotificationSetting.notSupported; } throw AblyException( - message: 'Platform communication error. ' + message: + 'Platform communication error. ' 'UNNotificationSetting is invalid: $setting', ); } @@ -909,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 @@ -941,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', ); } @@ -1030,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( @@ -1066,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, @@ -1151,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) @@ -1172,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/method_call_handler.dart b/lib/src/platform/src/method_call_handler.dart index 15c7faad4..94fff180d 100644 --- a/lib/src/platform/src/method_call_handler.dart +++ b/lib/src/platform/src/method_call_handler.dart @@ -24,16 +24,18 @@ 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}', + ); } }); } @@ -45,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', ); } @@ -83,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 4331ce441..926a8d8b2 100644 --- a/lib/src/platform/src/paginated_result.dart +++ b/lib/src/platform/src/paginated_result.dart @@ -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 2e00089dd..bfee2e266 100644 --- a/lib/src/platform/src/platform.dart +++ b/lib/src/platform/src/platform.dart @@ -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 00bdc8919..852038a51 100644 --- a/lib/src/platform/src/platform_object.dart +++ b/lib/src/platform/src/platform_object.dart @@ -44,20 +44,22 @@ abstract class PlatformObject { /// This method should be protected since it's only used to cover edge /// 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_notification_events_internal.dart b/lib/src/platform/src/push_notification_events_internal.dart index 916618a03..49d5e3246 100644 --- a/lib/src/platform/src/push_notification_events_internal.dart +++ b/lib/src/platform/src/push_notification_events_internal.dart @@ -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 c936b1c18..151c52d93 100644 --- a/lib/src/platform/src/realtime/connection.dart +++ b/lib/src/platform/src/realtime/connection.dart @@ -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; @@ -85,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 013cafa83..75eba37e8 100644 --- a/lib/src/platform/src/realtime/presence.dart +++ b/lib/src/platform/src/realtime/presence.dart @@ -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/pub_sub_client.dart b/lib/src/platform/src/realtime/pub_sub_client.dart index 4d7a80862..1a10de8f5 100644 --- a/lib/src/platform/src/realtime/pub_sub_client.dart +++ b/lib/src/platform/src/realtime/pub_sub_client.dart @@ -21,10 +21,10 @@ class PubSubClient extends PlatformObject { ///@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) { @@ -35,11 +35,7 @@ class PubSubClient 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; } diff --git a/lib/src/platform/src/realtime/realtime_channel.dart b/lib/src/platform/src/realtime/realtime_channel.dart index 6b846ae5b..34844d64e 100644 --- a/lib/src/platform/src/realtime/realtime_channel.dart +++ b/lib/src/platform/src/realtime/realtime_channel.dart @@ -29,10 +29,10 @@ class RealtimeChannel extends PlatformObject { /// sets default [state] to [ChannelState.initialized] and start listening /// for updates to the channel [state]/ RealtimeChannel(PubSubClient realtime, String channelName) - : _realtime = realtime, - _channelName = channelName, - state = ChannelState.initialized, - super() { + : _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/streams_channel.dart b/lib/src/platform/src/streams_channel.dart index ceba85bd2..ae2cccac1 100644 --- a/lib/src/platform/src/streams_channel.dart +++ b/lib/src/platform/src/streams_channel.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_device_registrations.dart b/lib/src/push_notifications/src/admin/push_device_registrations.dart index 41566f547..16dcfe61f 100644 --- a/lib/src/push_notifications/src/admin/push_device_registrations.dart +++ b/lib/src/push_notifications/src/admin/push_device_registrations.dart @@ -5,17 +5,12 @@ 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_push_details.dart b/lib/src/push_notifications/src/device_push_details.dart index 64c8d98d1..4bd64a026 100644 --- a/lib/src/push_notifications/src/device_push_details.dart +++ b/lib/src/push_notifications/src/device_push_details.dart @@ -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/ios_notification_settings.dart b/lib/src/push_notifications/src/ios_notification_settings.dart index 659a376a0..d30b00c6e 100644 --- a/lib/src/push_notifications/src/ios_notification_settings.dart +++ b/lib/src/push_notifications/src/ios_notification_settings.dart @@ -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 271b25fcf..6dc076e52 100644 --- a/lib/src/push_notifications/src/local_device.dart +++ b/lib/src/push_notifications/src/local_device.dart @@ -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 af6064477..9f62f9cbc 100644 --- a/lib/src/push_notifications/src/notification.dart +++ b/lib/src/push_notifications/src/notification.dart @@ -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 f8934f7b4..f0275abe7 100644 --- a/lib/src/push_notifications/src/push.dart +++ b/lib/src/push_notifications/src/push.dart @@ -91,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.'); } diff --git a/lib/src/push_notifications/src/push_channel.dart b/lib/src/push_notifications/src/push_channel.dart index aa7b0b9b8..7d0f304f6 100644 --- a/lib/src/push_notifications/src/push_channel.dart +++ b/lib/src/push_notifications/src/push_channel.dart @@ -18,24 +18,28 @@ class PushChannel extends PlatformObject { 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. /// @@ -49,7 +53,8 @@ class PushChannel extends PlatformObject { /// Returns a [PaginatedResult] object containing a list of /// [PushChannelSubscription] objects. Future> listSubscriptions( - Map params) async { + 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')) { @@ -64,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_subscriptions.dart b/lib/src/push_notifications/src/push_channel_subscriptions.dart index 0c65f33b6..20f72f28b 100644 --- a/lib/src/push_notifications/src/push_channel_subscriptions.dart +++ b/lib/src/push_notifications/src/push_channel_subscriptions.dart @@ -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 8dd88da65..501a98a00 100644 --- a/lib/src/push_notifications/src/push_notification_events.dart +++ b/lib/src/push_notifications/src/push_notification_events.dart @@ -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 64d176c7e..8ecc10069 100644 --- a/lib/src/push_notifications/src/remote_message.dart +++ b/lib/src/push_notifications/src/remote_message.dart @@ -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/connection_state_change.dart b/lib/src/realtime/src/connection_state_change.dart index 4437060ec..dac2bb859 100644 --- a/lib/src/realtime/src/connection_state_change.dart +++ b/lib/src/realtime/src/connection_state_change.dart @@ -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 729ddb572..c30f6e976 100644 --- a/lib/src/realtime/src/realtime_auth.dart +++ b/lib/src/realtime/src/realtime_auth.dart @@ -9,33 +9,33 @@ class RealtimeAuth extends Auth { 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 4be33eb67..7e3ed45f4 100644 --- a/lib/src/realtime/src/realtime_channel_options.dart +++ b/lib/src/realtime/src/realtime_channel_options.dart @@ -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 61f139ad3..bcbbeb115 100644 --- a/lib/src/realtime/src/realtime_history_params.dart +++ b/lib/src/realtime/src/realtime_history_params.dart @@ -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/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_pubsub_device_flutter_plugin_test.dart b/test/ably_pubsub_device_flutter_plugin_test.dart index aedfe191d..4bd9ad18e 100644 --- a/test/ably_pubsub_device_flutter_plugin_test.dart +++ b/test/ably_pubsub_device_flutter_plugin_test.dart @@ -4,8 +4,10 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - final channel = - MethodChannel('io.ably.flutter.plugin', StandardMethodCodec(Codec())); + final channel = MethodChannel( + 'io.ably.flutter.plugin', + StandardMethodCodec(Codec()), + ); TestWidgetsFlutterBinding.ensureInitialized(); var counter = 0; @@ -17,24 +19,24 @@ void main() { setUp(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (methodCall) async { - switch (methodCall.method) { - case PlatformMethod.resetAblyClients: - return true; + switch (methodCall.method) { + case PlatformMethod.resetAblyClients: + return true; - case PlatformMethod.getPlatformVersion: - return _platformVersion; - case PlatformMethod.getVersion: - return _nativeLibraryVersion; + case PlatformMethod.getPlatformVersion: + return _platformVersion; + case PlatformMethod.getVersion: + return _nativeLibraryVersion; - case PlatformMethod.createRealtime: - return ++counter; + case PlatformMethod.createRealtime: + return ++counter; - case PlatformMethod.publishRealtimeChannelMessage: - case PlatformMethod.connectRealtime: - default: - return null; - } - }); + case PlatformMethod.publishRealtimeChannelMessage: + case PlatformMethod.connectRealtime: + default: + return null; + } + }); Platform(methodChannel: channel); }); @@ -53,9 +55,7 @@ void main() { test(PlatformMethod.createRealtime, () async { const endpoint = 'nonprod:sandbox'; - final o = ClientOptions( - endpoint: endpoint, - ); + final o = ClientOptions(endpoint: endpoint); final realtime = createClient(options: o); expect(await realtime.handle, counter); expect(realtime.options.endpoint, endpoint); diff --git a/test/ios_plugin_version_test.dart b/test/ios_plugin_version_test.dart index 1a2205121..ff0f968a7 100644 --- a/test/ios_plugin_version_test.dart +++ b/test/ios_plugin_version_test.dart @@ -8,14 +8,17 @@ import 'package:flutter_test/flutter_test.dart'; /// 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 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())! + .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 51c7f79ea..ead1a658c 100644 --- a/test/mock_method_call_manager.dart +++ b/test/mock_method_call_manager.dart @@ -13,8 +13,10 @@ class MockMethodCallManager { 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); @@ -25,8 +27,10 @@ class MockMethodCallManager { 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); @@ -55,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, ), ); @@ -77,8 +81,10 @@ class MockMethodCallManager { 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/message_test.dart b/test/models/message_test.dart index 9ac0ec7da..f3bef5a5f 100644 --- a/test/models/message_test.dart +++ b/test/models/message_test.dart @@ -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 b32593113..c62c072ce 100644 --- a/test/models/presence_message_test.dart +++ b/test/models/presence_message_test.dart @@ -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/realtime/channel_test.dart b/test/realtime/channel_test.dart index 93adef034..b78265de5 100644 --- a/test/realtime/channel_test.dart +++ b/test/realtime/channel_test.dart @@ -25,8 +25,12 @@ void main() { // 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'); }); @@ -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'); }); @@ -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'); }); @@ -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/realtime/channels_test.dart b/test/realtime/channels_test.dart index 5b7afe2a7..5f1fbcef9 100644 --- a/test/realtime/channels_test.dart +++ b/test/realtime/channels_test.dart @@ -65,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']), ); }); @@ -87,8 +84,7 @@ void main() { }); group('#release', () { - test( - '(RTS4d) Releasing a channel the platform SDK releases' + test('(RTS4d) Releasing a channel the platform SDK releases' ' removes it from the collection', () async { channels ..get('channel-1') @@ -99,8 +95,7 @@ void main() { expect(channels.exists('channel-1'), false); }); - test( - '(RTS4c) Calling release() with a channel name' + test('(RTS4c) Calling release() with a channel name' ' that does not correspond to an extant channel' ' must return without error', () async { channels @@ -113,14 +108,14 @@ void main() { expect(channels.exists('channel-2'), true); }); - test( - '(RTS4e) If the platform SDK fails to release the channel,' + 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' + 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', @@ -132,8 +127,11 @@ void main() { throwsA( isA() .having((e) => e.errorInfo?.code, 'errorInfo.code', 90011) - .having((e) => e.errorInfo?.statusCode, 'errorInfo.statusCode', - 400), + .having( + (e) => e.errorInfo?.statusCode, + 'errorInfo.statusCode', + 400, + ), ), ); expect(channels.exists('channel-1'), true); diff --git a/test_integration/lib/app_provisioning.dart b/test_integration/lib/app_provisioning.dart index bba683c6d..741cb8c9a 100644 --- a/test_integration/lib/app_provisioning.dart +++ b/test_integration/lib/app_provisioning.dart @@ -57,8 +57,7 @@ class AppProvisioning { ); /// URL used for app provisioning requests - String get _provisioningUrl => - 'https://$host/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 77e278a6a..acb2fbfe8 100644 --- a/test_integration/lib/config/test_factory.dart +++ b/test_integration/lib/config/test_factory.dart @@ -23,10 +23,11 @@ import 'package:ably_flutter_integration_test/test/realtime/realtime_publish_wit import 'package:ably_flutter_integration_test/test/realtime/realtime_subscribe.dart'; import 'package:ably_flutter_integration_test/test/realtime/realtime_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 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 a4a58a140..1280bea56 100644 --- a/test_integration/lib/test/basic_test.dart +++ b/test_integration/lib/test/basic_test.dart @@ -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 ded4fa532..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 @@ -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_get_default_params.dart b/test_integration/lib/test/crypto/crypto_get_default_params.dart index e94d381b0..6afa5d94b 100644 --- a/test_integration/lib/test/crypto/crypto_get_default_params.dart +++ b/test_integration/lib/test/crypto/crypto_get_default_params.dart @@ -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 9fe3601d8..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 @@ -17,19 +17,18 @@ Future> testRealtimeAuthClientId({ autoConnect: false, ); - final ablyForToken = createClient( - options: clientOptionsForToken, - ); + final ablyForToken = createClient(options: clientOptionsForToken); /* get first token */ final firstToken = await ablyForToken.auth.requestToken(); final clientOptions = ClientOptions( - key: appKey, - endpoint: 'nonprod:sandbox', - useTokenAuth: true, - autoConnect: false, - clientId: 'testClientId', - tokenDetails: firstToken); + key: appKey, + endpoint: 'nonprod:sandbox', + useTokenAuth: true, + autoConnect: false, + clientId: 'testClientId', + tokenDetails: firstToken, + ); final realtime = createClient(options: clientOptions); await realtime.connect(); 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 49bf99c11..09f7ac5e1 100644 --- a/test_integration/lib/test/realtime/realtime_auth_url_test.dart +++ b/test_integration/lib/test/realtime/realtime_auth_url_test.dart @@ -20,19 +20,18 @@ Future> testCreateRealtimeWithAuthUrl({ autoConnect: false, ); - final ablyForToken = createClient( - 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, - endpoint: 'nonprod:sandbox', - useTokenAuth: true, - autoConnect: false, - logLevel: LogLevel.error); + 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) { @@ -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 542e40309..18f5d01ea 100644 --- a/test_integration/lib/test/realtime/realtime_authorize_test.dart +++ b/test_integration/lib/test/realtime/realtime_authorize_test.dart @@ -17,32 +17,33 @@ Future> testRealtimeAuthroize({ autoConnect: false, ); - final ablyForToken = createClient( - options: clientOptionsForToken, - ); + final ablyForToken = createClient(options: clientOptionsForToken); /* get first token */ final firstToken = await ablyForToken.auth.requestToken(); final clientOptions = ClientOptions( - key: appKey, - endpoint: 'nonprod:sandbox', - useTokenAuth: true, - autoConnect: false, - tokenDetails: firstToken); + 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 f753907be..90f91cd0a 100644 --- a/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_encrypted_publish_test.dart @@ -12,8 +12,9 @@ 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); @@ -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,8 +40,9 @@ 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); @@ -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 8b42126ff..05282608c 100644 --- a/test_integration/lib/test/realtime/realtime_events_test.dart +++ b/test_integration/lib/test/realtime/realtime_events_test.dart @@ -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_with_auth_callback_test.dart b/test_integration/lib/test/realtime/realtime_history_with_auth_callback_test.dart index ed1328585..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 @@ -16,9 +16,7 @@ Future> testRealtimeHistoryWithAuthCallback({ 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 2b3822794..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 @@ -9,13 +9,12 @@ final logMessages = >[]; ClientOptions getClientOptions( String appKey, [ String? clientId = 'someClientId', -]) => - ClientOptions( - key: appKey, - endpoint: 'nonprod: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 = createClient(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 = - createClient(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 33c45c5b5..e3881d446 100644 --- a/test_integration/lib/test/realtime/realtime_presence_get.dart +++ b/test_integration/lib/test/realtime/realtime_presence_get.dart @@ -10,13 +10,12 @@ final logMessages = >[]; ClientOptions getClientOptions( String appKey, [ String clientId = 'someClientId', -]) => - ClientOptions( - key: appKey, - endpoint: 'nonprod:sandbox', - clientId: clientId, - logLevel: LogLevel.error, - ); +]) => ClientOptions( + key: appKey, + endpoint: 'nonprod:sandbox', + clientId: clientId, + logLevel: LogLevel.error, +); Future> testRealtimePresenceGet({ required Reporter reporter, diff --git a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart index 4976ea2a0..052b498a5 100644 --- a/test_integration/lib/test/realtime/realtime_presence_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_presence_subscribe.dart @@ -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 68ca34d4c..c88ce5abd 100644 --- a/test_integration/lib/test/realtime/realtime_publish_test.dart +++ b/test_integration/lib/test/realtime/realtime_publish_test.dart @@ -23,10 +23,7 @@ Future> testRealtimePublish({ ); 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({ @@ -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); } @@ -82,10 +82,7 @@ Future> testRealtimePublishSpec({ // client options - no client id, message has client id final realtime2 = createClient( - options: ClientOptions( - key: appKey, - endpoint: 'nonprod:sandbox', - ), + 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 68ee75f70..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 @@ -13,9 +13,7 @@ Future> testRealtimePublishWithAuthCallback({ 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 d094387f5..99b0e6d50 100644 --- a/test_integration/lib/test/realtime/realtime_subscribe.dart +++ b/test_integration/lib/test/realtime/realtime_subscribe.dart @@ -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_dispatcher.dart b/test_integration/lib/test_dispatcher.dart index 2d934ac0f..196b560e0 100644 --- a/test_integration/lib/test_dispatcher.dart +++ b/test_integration/lib/test_dispatcher.dart @@ -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 9954da4be..dc99f541f 100644 --- a/test_integration/lib/utils/encoders.dart +++ b/test_integration/lib/utils/encoders.dart @@ -2,83 +2,78 @@ 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 b0823a607..acf56b7da 100644 --- a/test_integration/lib/utils/realtime.dart +++ b/test_integration/lib/utils/realtime.dart @@ -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/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 6c052ef5f..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', () { @@ -87,8 +91,9 @@ void testCryptoGetDefaultParams(FlutterDriver Function() getDriver) { 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/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 18517ad35..0762b1a97 100644 --- a/test_integration/test_driver/tests_config.dart +++ b/test_integration/test_driver/tests_config.dart @@ -8,41 +8,42 @@ 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.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) { From f385ea655d007b47213a30780cf9970852df87cc Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 2 Nov 2026 09:55:52 +0000 Subject: [PATCH 14/14] ci: run the Android integration tests on an x86_64 emulator Flutter 3.47 reports 32-bit x86 Android devices as unsupported, so `flutter drive` filtered out the x86 emulator the jobs started and found no device to run on. Use the x86_64 system image instead. This, not memory pressure, was why the emulator was missing, so restore test_integration's Gradle heap to the 8G it had before 94284481. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/flutter_integration.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/flutter_integration.yaml b/.github/workflows/flutter_integration.yaml index 40778145c..fe1308953 100644 --- a/.github/workflows/flutter_integration.yaml +++ b/.github/workflows/flutter_integration.yaml @@ -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