Ably Flutter Plugin
A Flutter plugin wrapping the ably-cocoa (iOS) and ably-java (Android) client library SDKs for Ably, the platform that powers synchronized digital experiences in realtime.
Ably provides the best infrastructure and APIs to power realtime experiences at scale, delivering billions of realtime messages everyday to millions of end users. We handle the complexity of realtime messaging so you can focus on your code.
Supported Platforms
iOS
iOS 10 or newer.
Android
API Level 19 (Android 4.4, KitKat) or newer.
This project uses Java 8 language features, utilising Desugaring
to support lower versions of the Android runtime (i.e. API Levels prior to 24)
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 accordingly.
Known Limitations
Features that we do not currently support, but we do plan to add in the future:
- Symmetric encryption (#104)
- Ably token generation (#105)
- REST and Realtime Stats (#106)
- Custom transportParams (#108)
- Push Notifications Admin (#109)
- Remember fallback host during failures (#47)
Example app
Running example app
- To run the example app, you need an Ably API key. Create a free account on ably.com and then use your API key from there in the example app.
- Clone the project
Android Studio / IntelliJ Idea
Under the run/ debug configuration drop down menu, click Edit Configurations...
. Duplicate the Example App (Duplicate and modify)
configuration. Leave the "Store as project file" unchecked to avoid committing your Ably API key into a repository. Update this new run configuration's additional run args
with your ably API key. Run or debug the your new run/ debug configuration.
Visual Studio Code
- Under
Run and Debug
,- Select the gear icon to view
launch.json
- Add your Ably API key to the
configurations.args
, i.e. replacereplace_with_your_api_key
with your own Ably API key. - To choose a specific device when more than one are connected: to launch on a specific device, make sure it is the only device plugged in. To run on a specific device when you have multiple plugged in, add another element to the
configuration.args
value, with--device-id=replace_with_device_id
- Make sure to replace
replace_with_your_device
with your device ID fromflutter devices
- Make sure to replace
- Select the gear icon to view
- select the
example
configuration
Command Line using the Flutter Tool
- Change into the example app directory:
cd example
- Install dependencies:
flutter pub get
- Launch the application:
flutter run --dart-define ABLY_API_KEY=put_your_ably_api_key_here
, remembering to replaceput_your_ably_api_key_here
with your own API key.- To choose a specific device when more than one are connected: get your device ID using
flutter devices
, and then runningflutter run --dart-define=ABLY_API_KEY=put_your_ably_api_key_here --device-id replace_with_device_id
- To choose a specific device when more than one are connected: get your device ID using
Push Notifications
See PushNotifications.md for detailed information on getting PN working with the example app.
Troubleshooting
- Running on simulator on M1 macs:
- Flutter has added support for running apps on the iOS simulator running on M1 architecture, but this is not yet available on the stable branch. In the mean time, you can change the iOS target to build for Mac in Xcode.
fatal error: 'ruby/config.h' file not found
: Ruby is required to install cocoapods and other tools which are used in the build process, and your machine may not have a supported version. To install an up-to-date version of Ruby:- Run
brew install rbenv ruby-build
- Install rbenv:
- Run
rbenv init
(and follow its recommended instructions) - Run
rbenv install 3.0.1
- Run
- Run
sudo gem install cocoapods ffi
- Why do we need
ffi
?: https://github.com/CocoaPods/CocoaPods/issues/9907#issuecomment-729980327
- Why do we need
- Run
Usage
Specify Dependency
Package home:
pub.dev/packages/ably_flutter
See:
Adding a package dependency to an app
Import the package
Configure a Client Options object
For guidance on selecting an authentication method (basic authentication vs. token authentication), read Selecting an authentication mechanism.
Authenticating using basic authentication/ API key (for running example app/ test apps and not for production)
Authenticating using token authentication
Using the REST API
Creating the REST client instance:
Getting a channel instance
Publishing messages using REST:
Get REST history:
Get REST Channel Presence:
Get REST Presence History:
Using the Realtime API
Creating the Realtime client instance:
Listening for connection state change events:
Listening for a particular connection state change event (e.g. connected
):
Creating a Realtime channel instance:
Listening for channel events:
Attaching to the channel:
Detaching from the channel:
Subscribing to messages on the channel:
Use channel.subscribe(name: "event1")
or channel.subscribe(names: ["event1", "event2"])
to listen to specific named messages.
UnSubscribing from receiving messages on the channel:
Publishing channel messages
Get Realtime history
Enter Realtime Presence:
Update Realtime Presence:
Leave Realtime Presence:
Get Realtime Presence members:
Get Realtime Presence history
Subscribe to Realtime Presence messages
Push Notifications
See PushNotifications.md for detailed information on using PN with this plugin.
Caveats
RTE6a compliance
Using the Streams based approach doesn't fully conform with
RTE6a
from our
client library features specification.
The Problem
In the example above, the 2nd listener is cancelled when the 1st listener is notified about the "connected" event.
As per
RTE6a,
the 2nd listener should also be triggered.
It will not be as the 2nd listener was registered after the 1st listener and stream subscription is cancelled immediately after 1st listener is triggered.
This wouldn't have happened if the 2nd listener had been registered before the 1st was.
However, using a neat little workaround will fix this...
The Workaround - Cancelling using delay
Instead of await subscriptionToBeCancelled.cancel();
, use