Skip to content

AppPlugin

PlatformHub facilitates the communication between plugins. This is done via Messages (Events, Queries, Commands). Messages can only be sent and received by plugins. This presents two problems:

  1. Getting the first view on screen. As iOS works off a window->rootViewController, how do we get the first ViewController and make it root of the Window?
  2. Moving information that comes from the system. e.g. Notifications, App lifecycle events, camera to and fro.

These can be achieved by implementing the concept of the AppPlugin.

This is a plugin internal to the Host App, implemented in the host app target; it's generally not a separate library, nor gets released to Nexus and doesn't go through the accreditation process as it doesn't get shared between apps, so it doesn't become part of the Platform. It does however still need a Policy file correctly declaring the messages that it sends and receives.

In its simplest form, the AppPlugin is a class in your App bundle implementing the PluginProtocol.

There are a few things you need to make sure happen when implementing your AppPlugin.

  • It implements the PluginProtocol and needs a Policy file.
  • It needs to be injected with PlatformHub's MessageSender instance, so the AppPlugin needs to be passed via the PlatformHub.start() method; this way the AppPlugin will go through the initialization process like the other plugins and be injected with the MessageSender instance.
swift
/// ... AppDelegate code
platformHub = try PlatformHub.start(AppPlugin.self) { appPlugin in
    window.rootViewController = try appPlugin.fetchRootViewController()
}
/// ... AppDelegate code
  • You might want to inject configuration files into the AppPlugin; the standard and recommended way is to do this via the PlatformHubPluginsConfig, but it is not a technical requirement and config can be passed manually.
  • For a detailed view of how to implement the above points please have a look at the Sample App's App Delegate.

The first and most important task is to retrieve the View from the relevant Journey and make it the root of the App. This is achieved by defining a func like fetchViewController() -> UIViewController and sending the correct ViewQuery to the correct Journey

swift
func fetchRootViewController() throws -> UIViewController {
    try messageSender.send(
        viewQuery: try Message(Send.ViewQuery.RootNavigationControllerPlugin.rootNavigation),
        sender: self
    )
}

This is then called from the AppDelegate and set as root

swift
window?.rootViewController = try appPlugin.fetchRootViewController()

As you can see this bridges the API paradigm of a mobile app (which we can't control) and the Events based system of PlatformHub. This way the AppPlugin can translate all API based system calls into Messages to and from other plugins.

Like any other plugins, the AppPlugin can subscribe to Events and receive updates from other plugins in order to manage App wide states, e.g. connection status (via a Reachability plugin)

As it is a Plugin for all intents and purposes, it can implement a Redux paradigm like all other Journey plugins in order to provide the root View and App wide state. The sample code below deals with a connectionStatus changed

swift
public func subscribeOnStartup(toPublisher publisher: AnyPublisher<JSONEventOutput, Never>, forEventStream eventStream: Message) throws {
    switch try Outgoing.ReachabilityPlugin.EventStream(message: eventStream, plugin: self) {
    case .connectionStatusChanged:
        publisher
            .compactMap { jsonEventOutput -> ConnectionStatus? in
                do {
                    print("🚦 REACHABILITY UPDATED: \(jsonEventOutput)")
                    let payload = try jsonEventOutput.result.get()
                    return try ConnectionStatus(payload: payload)
                } catch {
                    assertionFailure("❌ SUBSCRIPTION ERROR: could not parse message: \(error)")
                    return nil
                }
            }
            .sink { [weak self] connectionStatus in
                self?.store.dispatch(.connectionStatusChanged(isConnected: connectionStatus.isConnected))
            }
            .store(in: &cancellables)
    }
}

See here for the full implementation. For Android implementation, see here.

Look at the AppPlugin in the PlatformHub Sample App to see how it is achieved with UIKit. Look at the AppPlugin implementation in the Reactive Framework POC to see how it is achieved with SwiftUI.