Courier is a library for creating long running connections using MQTT which is the industry standard for IoT Messaging. Long running connection is a persistent connection established between client & server for bi-directional communication. A long running connection is maintained for as long as possible with the help of keepalive packets for instant updates. This also saves battery and data on mobile devices.
Find the detailed documentation here - https://gojek.github.io/courier-flutter/
End-to-end courier example - https://gojek.github.io/courier/docs/Introduction
Setup Courier to subscribe, send, and receive message with bi-directional long running connection between iOS device and broker.
A sample application is added here which makes Courier connection with a HiveMQ public broker. It demonstrates multiple functionalities of Courier like Connect, Disconnect, Publish, Subscribe and Unsubscribe.
Running sample app
- Clone the project from GitHub
- Open
courier_dart_sdk_demo
folder - Run
flutter run
Run this command:
With Flutter:
$ flutter pub add courier_flutter
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):
dependencies:
courier_flutter: ^0.0.4
Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:courier_dart_sdk/courier_client.dart';
Add the following snippet to Podfile in your project's iOS folder:
pod 'CourierCore', '0.0.14', :modular_headers => true
pod 'CourierMqtt', '0.0.14', :modular_headers => true
CourierClient
is the class that we use for any MQTT tasks such as connection, subscription, send and receive message. To initialize an instance of CourierClient we can use the static method like so.
final CourierClient courierClient = CourierClient.create(
dio: Dio(),
config: CourierConfiguration(
tokenApi: apiUrl,
authResponseMapper: CourierResponseMapper(),
authRetryPolicy: DefaultAuthRetryPolicy(),
readTimeoutSeconds: 60,
)
);
-
Dio : We use dio package for making HTTP request. This will provide you flexibility to use your own Dio instance in case you have various custom headers need to be sent to the server (e.g Authentication, etc).
-
tokenApi: An endpoint URL that returns JSON containing credential for mapping to
CourierConnectOptions
. -
authResponseMapper: Instance of
AuthResponseMapper
for mapping JSON returned bytokenAPI
URL toCourierConnectOptions
. -
authRetryPolicy: Retry policy used to handle retry when tokenAPI URL fails
To connect to MQTT broker you need to provide an endpoint URL that returns JSON containing these payload.
{
"clientId": "randomcourier1234567",
"username": "randomcourier1234567",
"password": "randomcourier4321",
"host": "broker.mqttdashboard.com",
"port": 1883,
"cleanSession": true,
"keepAlive": 45
}
You need to create and implement AuthResponseMapper
to map the JSON to the CourierConnectOptions
instance.
class CourierResponseMapper implements AuthResponseMapper {
@override
CourierConnectOptions map(Map<String, dynamic> response) => CourierConnectOptions(
clientId: response["clientId"],
username: response["username"],
host: response["host"],
port: response["port"],
cleanSession: response["cleanSession"],
keepAliveSeconds: response["keepAlive"],
password: response['password']
);
}
You need to pass the tokenAPI
URL and authResponseMapper
when initializing the CourierClient
like so.
final CourierClient courierClient = CourierClient.create(
dio: Dio(),
config: CourierConfiguration(
tokenApi: "https://example.com/courier-credentials/",
authResponseMapper: CourierResponseMapper(),
//...
)
);
We use dio package for making HTTP request. This will provide you flexibility to use your own Dio instance in case you have various custom headers need to be sent to the server (e.g Authentication, etc).
To connect to the broker, you simply need to invoke connect
method
courierClient.connect();
To disconnect, you just need to invoke disconnect
method
courierClient.disconnect();
As MQTT supports QoS 1 and QoS 2 message to ensure deliverability when there is no internet connection and user reconnected back to broker, we also persists those message in local cache. To disconnect and remove all of this cache, you can invoke.
courierClient.destroy();
Courier internally handles reconnection in case of bad/lost internet connection.
The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. There are 3 QoS levels in MQTT:
- At most once (0)
- At least once (1)
- Exactly once (2).
When you talk about QoS in MQTT, you need to consider the two sides of message delivery:
- Message delivery form the publishing client to the broker.
- Message delivery from the broker to the subscribing client.
You can read more about the detail of QoS in MQTT from HiveMQ site.
To subscribe to a topic from the broker, invoke susbscribe
method on CourierClient passing the topic string and QoS.
courierClient.subscribe("chat/user1", QoS.one);
After you have subscribed to the topic, you need to listen to a message stream passing the associated topic. The type of the parameter in the listen callback is byte array UInt8List
.
courierClient.courierMessageStream("chat/user1").listen((message) {
print("Message received: ${event}");
});
To unsubscribe from a topic, simply invoke unsubscribe
passing the topic string.
courierClient.unsubscribe("chat/user/1");
To publish message to the broker, you need to serialize your data to byte array UInt8List
// Example of Custom TestData class
Uint8List testDataEncoder(TestData testData) => testData.toBytes();
Next, you need to initalize CourierMessage
instance passing the bytes
, topic
string, and qos
like so.
final message = CourierMessage(
bytes: testDataEncoder(TestData("Hello World")),
topic: "/chat/user1",
qos: QoS.one
)
Finally, you need to invoke publishCourierMessage
on CourierClient
passing the message.
courierClient.publishCourierMessage(mesage);
Courier provides ability to register and listen to library events emitted by the Client (connect attempt/success/failure, message send/receive, subscribe/unsubscribe). All you need to do is listen to courierEventStream
from CourieClient
.
courierClient.courierEventStream().listen((event) {
print("Event received: ${event}");
});
The type of the parameter is an abstract class CourierEvent
with these public interfaces:
- name: Name of the event.
- connectionInfo Connection Info tied to the event
- getEventPropertiesMap: A key value map containing the properties related to the event.
You can try to Cast the CourierEvent
to concrete implementation that provides additional properties such as:
- MQTTConnectAttemptEvent
- MQTTConnectSuccessEvent
- MQTTConnectFailureEvent
- ...
Read our contribution guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Courier iOS library.
All Courier modules are MIT Licensed.