本文へ移動

State、event、navigation ​

Reactive store ​

Apsuleでは概念上3つのscopeを使います。

ScopeLifetime主な用途
stateView/component runtimeUI state、loading flag、現在のlist data。
global実行中Apsule Projectrouteをまたぐsession/account/shared state。
storagePersistentprocess/project再起動後も残る設定やデータ。魔法のような同期disk writeではなくHost API経由でアクセスする。

stateとglobalの変更はreactiveです。

javascript
state.loading = true;
global.currentUser = user;

bindされたSwiftUI nodeは自動更新されます。

Action ​

JSONは名前付きactionを参照し、任意のJavaScriptを直接含めません。

json
{
  "id": "refresh",
  "type": "Button",
  "props": { "text": "Refresh" },
  "events": {
    "tap": { "action": "refreshMatches" }
  }
}
javascript
actions.refreshMatches = async () => {
  state.loading = true;
  try {
    state.matches = await api.getMatches();
  } finally {
    state.loading = false;
  }
};

Actionは任意のevent payloadを最初のJavaScript引数として受け取れます。通常のstatic controlは現在、domain dataなしでactionを呼び出します。ForEach内で描画されたcontrolは現在のiteration scopeを受け取るため、各rowごとに別のstatic actionを作らなくても、actionからevent.match.idやevent.item.contentなどを参照できます。

entry viewにはappear actionを定義できます。Hostは実行中Projectのrootが表示されたとき、通常のPromise-aware action pathからこれを呼び出します。そのため、非同期cache loadや、stateをSwiftUIへ反映する必要がある初期同期を行う場所として適しています。

Event vocabulary ​

実装済みのevent vocabularyには次が含まれます。

  • appear
  • disappear
  • foreground
  • inactive
  • background
  • tap
  • doubleTap
  • longPress
  • change
  • submit
  • refresh
  • drag
  • drop
  • swipe
  • focus
  • blur
  • confirm
  • reorder
  • context
  • navigate
  • error
  • message
  • regionChange
  • mapTap
  • 外部lifecycle event向けのtop-level action openURL、notification、push

Event payloadはJSON-serializableです。gesture/map/web event固有のfieldはevent配下に置かれ、ForEachでは現在のiteration scopeも保持されます。

viewの各所へfilesystem pathを埋め込むより、route nameを利用します。

json
{
  "routes": {
    "home": "views/home.json",
    "player": "views/player.json"
  }
}

JavaScript API:

javascript
router.push("player", { id: player.id });
router.pop();
router.replace("home");
router.sheet("settings");
router.dismiss();

上記JavaScript routerは実装済みです。Route parameterはstate/globalへコピーしない限り、route.params scopeにあるread-only inputとして扱われます。宣言的なNavigationLinkもprops.paramsを受け取れます。

Hostはさらにapsule:// deep-link schemeを処理します。実行中Projectは通常の外部URLをactions.openURL(payload)で受け取ります。scene transitionではtop-levelのactions.foreground、actions.inactive、actions.backgroundを呼び出します。Project push notificationはProjectがpush permissionを宣言し承認されている場合にのみactions.push(payload)を呼び出し、local notificationのtapはactions.notification(payload)を呼び出します。

このroute modelは、将来Apsule Builderにvisual navigation graphを追加する場合にも適しています。

Subscription ​

長時間動作するHost API operationはcancel可能なsubscriptionを返します。

javascript
const sub = host.location.watch(position => {
  global.position = position;
});

// Later
sub.cancel();

runtimeは、明示的にapp scopeへ昇格された場合を除き、所有viewが破棄された時点でview-scoped subscriptionを自動解放する必要があります。