Tick Event Handlers

Tick events occur as time passes. You can use a handleTick event handler to capture tick events and execute commands periodically based on time.

For example, the following TouchWrapper increments the repeat counter about every 100 milliseconds as long as the component has the pressed state. When the user releases the component and the pressed state ends, the onPress handler resets the counter to zero.

{
  "type": "TouchWrapper",
  "bind": [
    {
      "name": "RepeatCounter",
      "value": 0
    }
  ],
  "handleTick": {
    "when": "${state.pressed}",
    "minimumDelay": 100,
    "commands": {
      "type": "SetValue",
      "property": "RepeatCounter",
      "value": "${RepeatCounter + 1}"
    }
  },
  "onPress": {
    "type": "SetValue",
    "property": "RepeatCounter",
    "value": 0
  },
  "items": {
    "type": "Text",
    "text": "You held this button for ${RepeatCounter} counts"
  }
}

Tick event handlers

You can define tick handlers at the component level or at the document level.

All components have the handleTick property. The APL document also has a top-level handleTick property.

Property Type Default Styled Dynamic Description
handleTick Array of tick handlers [] No No Handlers to execute when tick events occur.

A single tick handler is an object with the following properties.

Property Type Default Description
commands Array of commands REQUIRED Commands to execute when this handler invokes.
description String "" Optional description of this tick handler.
minimumDelay Number 1000 Minimum duration in milliseconds that must pass before this handler is invoked again.
when Boolean true When true, invoke this handler.

The array of tick handlers executes in parallel. Each tick handler has a when clause. A tick handler executes when both of the following conditions are met:

  • The when clause for the handler evaluates to true.
  • At least minimumDelay milliseconds have passed after the last time this tick handler executed.

Individual APL running environments might check and execute tick handlers at indeterminate intervals. An APL running environment should check tick handlers at least 60 times per second, but a resource-constrained environment might check less frequently.

For example, if you set a tick handler with a minimumDelay of 30, APL invokes it at best every 30 milliseconds. It might be invoked at less frequent intervals. Don't use a tick counter to measure the passage of time. instead use the elapsedTime or utcTime properties.

commands

An array of commands to execute when this tick handler is executed.

The event generated has the following form:

"event": {
  "source": {
    "type": "COMPONENT_TYPE",   // The component type or "Document"
    "handler": "Tick",
    "id": ID,          // ID of the component or null for the Document
    "uid": UID,        // Runtime-generated unique ID of the component or null for the Document
    "value": NUMBER    // Value for the component or null for the document
  }
}

Tick commands always run in fast mode.

minimumDelay

The minimum amount of time in milliseconds that must pass between invocations of this tick handler.

when

When true, this handler executes when the system detects a tick event.

Tick handler examples

Animation example

You can use tick handlers to create state machines that fire commands periodically. The following toggles the lights in a traffic light at random intervals (but approximately once per four seconds).

{
  "type": "Container",
  "bind": [
    {
      "name": "CurrentIndex",
      "value": 0
    }
  ],
  "handleTick": [
    {
      "minimumDelay": 1000,
      "description": "Cycle through the traffic light at random intervals.  The cycle order is green-yellow-red-green-...",
      "commands": [
        {
          "when": "${Math.random() < 0.25}",
          "type": "SetValue",
          "property": "CurrentIndex",
          "value": "${(CurrentIndex + 2) % 3}"
        }
      ]
    },
    {
      "minimumDelay": 15000,
      "description": "Every 15 seconds just go back to red to annoy folks",
      "commands": [
        {
          "type": "SetValue",
          "property": "CurrentIndex",
          "value": 0
        }
      ]
    }
  ],
  "items": {
    "type": "Frame",
    "width": 100,
    "height": 100,
    "borderRadius": 100,
    "backgroundColor": "${index == CurrentIndex ? data : 'grey'}"
  },
  "data": [
    "red",
    "yellow",
    "green"
  ]
}

Rate limiters

You can use tick handlers to create rate limiters. For example, assume you needed to periodically pass information the user scrolling the screen to your skill.

Without tick handlers, you could assign a SendEvent command to the onScroll handler of the ScrollView. This approach causes problems, as the command floods the system with SendEvent messages each time the scroll view moves.

{
  "type": "ScrollView",
  "onScroll": {
    "type": "SendEvent",
    "sequencer": "SendEventSequencer",
    "arguments": [
      "now at position ${event.source.position}"
    ]
  }
}

To improve this solution, throttle the messages with a handleTick handler. In this example, the tick handler limits the rate of sending updates to one time per second and sends the updated scroll position if the last reported position has changed.

{
  "type": "ScrollView",
  "bind": [
    {
      "name": "ScrollPosition",
      "value": 0
    },
    {
      "name": "LastScrollPosition",
      "value": 0
    }
  ],
  "handleTick": {
    "minimumDelay": 1000,
    "when": "${ScrollPosition != LastScrollPosition}",
    "commands": [
      {
        "type": "SetValue",
        "property": "LastScrollPosition",
        "value": "${ScrollPosition}"
      },
      {
        "type": "SendEvent",
        "sequencer": "SendEventSequencer",
        "arguments": [
          "now at position ${ScrollPosition}"
        ]
      }
    ],
    "onScroll": {
      "type": "SetValue",
      "property": "ScrollPosition",
      "value": "{event.source.position}"
    }
  }
}