Skip to content

Messages

Messages are a way for plugins to communicate between each other. With the event system of PlatformHub, you no longer need to implement the logic for interacting between libraries and getting data from one library and sending it to another library in the host app.

The legacy implementation of library contracts in the host app is no longer needed so there is minimal code in the host app.

To get data from one plugin to another plugin, you send a message with a payload.

Plugins can send messages with or without payloads to other plugins.

Message Types

There are multiple types of messages that can be sent and received by a plugin. We can categorise them in 3 groups:

  • Direct messages: Messages that are sent in an imperative way to another plugin. The can be sent at any point in the app life time. They can be sync or async messages. These are Commands, Queries and Navigation Queries.

  • Event streams: Can either be subscribed to at startup or on demand by plugins at runtime, creating a "network" of subscriptions to different publishers from different plugins across the app. As soon as these subscriptions are effective, a publisher can start broadcasting events to the plugins subscribed to it.

  • Topic streams: Allows plugin to publish/subscribe to given topic name / message name.

Each type of message can only be sent in certain scenarios. Below are the type of messages that can be sent and received:

Event Stream

The event stream message type is used for broadcasting data. Multiple plugins can subscribe to a given event stream and when the appropriate event/data is published all active subscribers will be notified.

Events can be used to return data triggered by commands, or to notify plugins about other activity e.g. a reachability plugin might notify plugins about connectivity status changes by broadcasting an event over a stream to which the other plugins are continuously subscribed.

The lifetime of an event stream will depend on its subscribe type and implementation.

Some Events Streams are subscribed automatically at PlatformHub start-up. Others are manually subscribed by plugins on demand at runtime. These are differentiated in the plugin Policy by declaring them under the relevant group.

Topic Stream

Direct Messages:

ViewQuery (iOS)

This is a type of message that is used for navigating between plugins. For a plugin to navigate to another plugin it has to send a navigation query or view query to the plugin it wants to navigate to. ViewQuery is synchronous message type. It should be used for cross plugin navigation only, internal navigation within a plugin should use the platform specific navigation i.e. for android plugin (navigation component) and the equivalent to iOS.

Query

The query message type is use for sending synchronous messages and getting some data back at the same time. Query messages have a return value type of JSONObject which contains the data you get from the receiving plugin. Query is used if you require a return value without waiting for any long running process. Its returned value is optional and discardable, which means a Query can be also used to just send a message to another plugin and expect nothing to be returned.

Command

The Command message type is a fire and forget message which doesn't return any value. This is useful when you want to send data to another plugin but you don't care about the result. It can also be used to instruct another plugin to perform some asynchronous operation, as a result, you will be notified through an event stream subscription.

Notes:

  • To be able to send any of these message types, make sure they are declared under the correct category in the plugin policy.
  • Command, Query and NavigationQuery/ViewController have a single receiver but can have multiple senders.
  • Event Stream on the other hand is a one to many subscription. it can have multiple subscribers at the same time. Note that the publisher returned by a Command could potentially be subscribed by other plugins that called/will call that same Command.

Payload

Payloads are how plugins send data to each other. They can only be in the form of JSON, encapsulated in a JSONObject, which is a struct with several initialisers for converting arrays, dictionaries, data & strings to JSON, plus convenience functions for retrieving the encapsulated data.

JSON payloads discourage the sending of complex objects which would otherwise result in the wrong usage of the PlatformHub architecture. For example sending an image encoded as a Base64 string in the payload is not best practice, instead use the CMS (Content Manager Storage).

Messages' payloads are optional so you do not need to send them if there is no requirement for any data.

Payloads should contain primitive data - complex objects or files such as images must not be serialised into strings and embedded in the JSON payload.

Sending Messages

For a plugin to be able to send and receive messages, it has to declare the messages in its policy file. If a plugin tries to send a message not declared by its policy an exception will be thrown. This is to prevent plugins from sending messages they are not eligible to send.

There are multiple ways to send and receive messages. For more on receiving messages, have a look at the plugin documentation. There are different types of messages that can be sent by a plugin. Below are some examples of sending messages:

Event Streams:

Topic Streams:

ViewQuery(iOS):

Query:

Command:

Message ID

The Message ID is a unique random UUID that is generated for every instance of a Message sent.

Message ids can be used to filter out events that a plugin is only interested in.

An example of a payload returning a message with a correlationMessage which contains the correlation id in the uuid field

json
  "ExampleEventMessage": {
    "exampleValue": "Lorem Ipsum"
    "correlationMessage": {
      "messageName": "exampleMessage",
      "pluginName": "ExamplePlugin",
      "uuid": "XXXX-XXXX-XXXX"
    }
  }

When a plugin sends a command to another plugin, an event can be broadcast to all other plugins subscribed to that specific event stream. The jsonEventOutput provided by the subscription will contain a correlation message, which must be the same as the command (Message) which triggered the event.

Because a subscription can receive any number of different events and messages at any time, the correlation message can be used to filter out events and check if the data received belongs to the command that was sent by the plugin initially.

Implementation note to have in consideration: Please read here

iOS

JSONEventOutput

Publishers returned when subscribing to Event Streams are of type <JSONEventOutput, Never> The JSONEventOutput contains the payload and a correlationMessage. The correlationMessage is sent by the plugin emitting the value when the Event is sent in response to a previous command by another plugin. The correlationMessagecan optionally be used by a plugin to filter the values it received, for example, using the uniqueid` to identify the response to a particular command it sent.

The correlation message is nothing more than the original Message object.

ModelEventOutput

ModelEventOutput facilitates the use of encodable models internally within a plugin, and to simplify the process to encode them into a JSONObject. ModelEventOutput is a struct conforming to EventOutputProtocol which has a generic model type ModelPayload as the payload of its success result. It's a convenience type that allows you to internally use codable model types instead of JSONObjects as event's payloads.

It has the following initialisers:

  • init(result: Result<ModelPayload, EventOutputError>, correlationMessage: Message? = nil) Where ModelPayload is the model type.

  • init(payload: JSONObject, correlationMessage: Message? = nil, decoder: JSONDecoder? = nil) Where the JSONObject payload is decoded to the decodable generic model type ModelPayload defined in the type being initialised.

To retrieve a JSONEventOutput from an existing ModelEventOutput, you just need to call its method encodedToJSON and it will return its equivalent JSONEventOutput.

Example:

swift
let userJSONObject=  userModel.encodedToJSON()

At the publisher level, for those publishers emitting ModelEventOutputs, there is also the convenience method encodeToJSON, which will map the model to a JSONObject so it can easily be emitted to other plugins.

Example:

swift
private let userPassthroughSubject = PassthroughSubject<ModelEventOutput<UserModel>, Never>()

public func publisher(forEventStream eventStream: Message, payload: JSONObject?) throws -> AnyPublisher<JSONEventOutput, Never> {
    return userPassthroughSubject
        .encodeToJSON(onEncodingError: .ignore)
        .eraseToAnyPublisher()
}

internal func publishUserName(_ userModel: ModelEventOutput<UserModel>) {
    userPassthroughSubject.send(userModel)
}