Observables in a remote interface are reactive streams of Event instances in Swift that, when connected to a Web Frame, can push data to the web layer. They are implemented with Combine Framework, the reactive programming library that powers SwiftUI, which means they are instances of Publishers.

Examples

Observables can be declared any number of ways. The following examples are for a remote observable that sends events every time the network connection changes.

Declared as a @Published property

Use the @Published attribute to create an underlying publisher for the annotated property. Every time the property is assigned a new value, the underlying publisher will emit the new value to any subscribers.

The underlying publisher can be accessed with the $ operator ($connection in the following example).

class Network {
    @Published var connection: Event?
}

Declared as a CurrentValueSubject

A CurrentValueSubject can be used to declare a publisher outside of a class.

let networkConnection = CurrentValueSubject<Event?, Never>(nil)

Web Frame Configuration

For the following example, we’ll be using an observable declared like the following:

let networkConnection = CurrentValueSubject<Event?, Never>(nil)

The observable reference can be passed directly to the Web Frame configuration.

Notice we must reset the type of the publisher to AnyPublisher by using .eraseToAnyPublisher() to conform to the RemoteObservable type expected by Peregrine. This is known as type erasure in Swift.

let configuration = WebFrame.Configuration(
    baseURL: ...
    functions: ...
    observables: [
        "networkConnection$": networkConnection.eraseToAnyPublisher()
    ]
)

Observables must end in a dollar sign ($) to denote a reactive property, as popularized by Cycle.js. This isn’t merely convention—the dollar sign is how ProxyClient knows it’s an observable, not a function.

Swift API

Event

Remote observables must emit Event objects, which represent a single value sent to the web layer.

Initializers

init?<T: Encodable>(_ json: T)

Create an Event with any encodable object.

Structs (or classes) must implement the Encodable protocol from Swift:

struct Connection: Encodable {
    let type: String
}

Then, objects can be passed to this constructor directly:

let event = Event(Connection(type: "wifi"))

This initializer will return nil if encoding fails (highly unlikely).

Not all Events must be wrapped in objects. In Swift, many data types implement the Encodable protocol: strings, booleans, ints, doubles, arrays, dictionaries, etc.