Material Motion Exploring solutions that will empower creators with the tools needed to describe and implement rich, interactive motion on any platform. Edit this page · History
MotionObservable _remember startWith
Status Interface level Implementation level Library
Stable as of February 21, 2016 L3: Stream creator L4: Runtime engineering material-motion
platformsrctests
iOS (Swift) View View
JavaScript View View
_remember
upstream (T)
(T) downstream

_remember specification

This is the engineering specification for the MotionObservable operator: _remember.

Overview

_remember caches the most recent upstream value and synchronously broadcasts it to all subscribers.

MVP

Expose _remember API

class MotionObservable<T> {

  public func _remember() -> MotionObservable<T>

Create storage for a set of observers

  public func _remember() -> MotionObservable<T> {
    var observers: [MotionObserver<T>] = []

Create a nullable Subscription variable

Maintain a single upstream subscription so long as there is at least one downstream subscription.

  public func _remember() -> MotionObservable<T> {
    ...

    var subscription: Subscription?

Create a cache of the last-received value

This variable will store the last-received upstream value. This is necessary when providing the latest value to new subscribers.

  public func _remember() -> MotionObservable<T> {
    ...

    var lastValue: T?

Return a MotionObservable of type T

  public func _remember() -> MotionObservable<T> {
    ...

    return MotionObservable<T> {
      // Connection logic
      return {
        // Disconnection logic
      }
    }
  }

On connection: subscribe upstream if necessary

The subscription should cache the received value and emit the value to all subscribed observers.

  public func _remember() -> MotionObservable<T> {
    ...

    return MotionObservable<T> {
      if observers.count == 0 {
        subscription = self.subscribe { value in
          lastValue = value
          for observer in observers {
            observer.next(value)
          }
        }
      }

      ...
    }
  }

On connection: add the observer to the list of observers

This should be done after subscribing upstream.

  public func _remember() -> MotionObservable<T> {
    ...

    return MotionObservable<T> {
      ...

      observers.append(observer)

      ...
    }
  }

On connection: immediately emit the last value to the observer

Only do this if lastValue has been set by the upstream subscription.

  public func _remember() -> MotionObservable<T> {
    ...

    return MotionObservable<T> {
      ...

      if let lastValue = lastValue {
        observer.next(lastValue)
      }

      ...
    }
  }

On disconnection: remove the observer and unsubscribe if necessary

Unsubscribe once there are no more downstream subscribers.

  public func _remember() -> MotionObservable<T> {
    ...

    return MotionObservable<T> {
      ...

      return {
        if let index = observers.index(where: { $0 === observer }) {
          observers.remove(at: index)
          if observers.count == 0 {
            subscription?.unsubscribe()
            subscription = nil
          }
        }
      }
    }
  }