Bixby Developer Center

Guides

Conditionals and Control Flows

Conditional statements let you control your capsule's execution by testing conditions, such as the values of concepts or expressions, and taking different actions based on the result of the test. An example of a conditional is "if the time is before 8:00 a.m., then set the default meal choice to 'breakfast'." Bixby provides several conditionals for use in your code, as well as a looping construct to repeat the same set of actions with each element in an array.

if-else

result-view {
match {
Shoe (this)
}

render {
if ("size(this) > 1") {
list-of (this) {
where-each (shoe) {
macro (shoe-summary-macro) {
param (shoe) {
expression (shoe)
}
}
}
}
} else-if ("size(this) == 1") {
layout {
macro (shoe-details-macro) {
param (shoe) {
expression (this)
}
}
}
}
}
}

View master on GitHub

The chain is evaluated from top to bottom and executes the first code block whose conditional expression evaluates to true:

  • if (expression) and else-if (expression) execute their blocks if their expression evaluates to true. There can be multiple else-if blocks, but only the first true if or else-if block will be executed.
  • else always executes if it is reached (that is, none of the if or else-if blocks evaluate to true).

Conditionals can use Expression Language to perform a wide range of tests. In the example above, size() tests how many results have been returned. Any value that can be accessed in EL can be tested in a conditional:

  • The value of a node. Depending on the cardinality of that node, this could be a single value (such as the number of dice being thrown in the Quick Start Guide's dice-rolling capsule) or an array of values (such as a list of results in a result view). These values could be primitive or structure concepts.
  • A property of a structure concept, such as the sum property on the RollResult in the dice-rolling capsule. The value of a property could be another concept (primitive or structure), or could be a symbol of an enum.
  • An input on an action.

Here's another example of an if/else-if/else construction from a transactional capsule:

activity-support {
match {
Receipt (this)
}
time (statusRefreshTime)
states {
if (orderState == 'Ordered') {
state (Scheduled) {
expires(statusRefreshTime)
summary-view {
message {
template ("#{value(orderState)} #{value(order)}")
}
title {
template ("#{value(order)}")
}
details {
template ("#{value(orderState)}")
}
}
detail-view {
render {
layout-match (this) {
mode (Details)
}
}
}
}
} else-if (orderState == 'Shipped') {
state (Relevant) {
expires(statusRefreshTime)
summary-view {
message {
template ("#{value(orderState)} #{value(order)}")
}
title {
template ("#{value(order)}")
}
details {
template ("#{value(orderState)}")
}
}
detail-view {
render {
layout-match (this) {
mode (Details)
}
}
}
}
} else { //orderState == 'Delivered' || orderState == 'Cancelled'
state (Recent) {
summary-view {
message {
template ("#{value(orderState)} #{value(order)}")
}
title {
template ("#{value(order)}")
}
details {
template ("#{value(orderState)}")
}
}
detail-view {
render {
layout-match (this) {
mode (Details)
}
}
}
}
}
}
}

View master on GitHub

switch

A switch block is similar to an if/else-if block, but only performs a single conditional test at the start of the block, providing case blocks that handle cases for the conditional expression's returned value.

dialog (Value) {
match: ProductType(this)
switch (this) {
case (FlowerArrangement) {
template("Flower Arrangement")
}
case (Bouquet) {
template("Bouquet")
}
case (Plantable) {
template("Plantable")
}
}
}

View 078b450 on GitHub

The expression in switch (expression) defines the test (in this case, simply the value of this). Then, case statements test the value of the expression. The first case that matches the result of the switch expression will be executed.

Just like the if/else-if block has an optional else block to be executed if no tests match, a switch block has an optional default block that will be executed if no case values match:

dialog (Concept) {
match {
Shoe (this)
}
switch (plural(this)) {
case (One) {
template (shoe)
}
default {
template (shoes)
}
}
}

View master on GitHub

choose

By default, Bixby will select the first occurrence of whatever it is looking for (such as a selection strategy or a dialog template), while respecting the control flows. You can modify this behavior for a code block by wrapping it with a choose.

choose (%order%) {
...
}

The %order% can be either:

  • First: Select the first valid occurrence.
  • Random: Find all valid occurrences and randomly select one.

This control flow is particularly useful when adding dialog variations, like in the Dialog Affirmations Sample Capsule:

// Pick a random affirmation from a bag of words
macro-def (AFFIRMATION) {
content {
choose (Random) {
template (Okay)
template (Alright)
}
}
}

View f61843b on GitHub

Note

If you are testing your capsule in the Simulator, disable the Deterministic Mode checkbox to make sure that choose (Random) behaves as expected.

for-each

A for-each block loops through an array of values, such as a list of results in a result view, and executes its code block on each individual element within the array. In Bixby, this is used most often for rendering elements within their own view components.

compound-card {
content {
for-each (items) {
as (item) {
// do something with the the item
}
}
}
}

This creates a partitioned area in your layout, with separated rows that iterate through a list of hotel amenities, rendering them in single-line containers.

result-view {
match: Hotel (hotel) {
from-output: ShowHotelPartitioned
}

message {
template ("Here are partitioned hotel amenities:")
}

render {
layout {
section {
content {
partitioned {
content {
for-each (hotel.amenities) {
as (amenity) {
single-line {
text {
value ("#{value(amenity)}")
style (Detail_L)
}
}
}
}
}
}
}
}
}
}
}

View master on GitHub

The as block also provides a loop index, a counter that starts at 0 and increments with each loop iteration. For the first item, the index will be 0; for the second, it will be 1; and so on. You can use this for conditional logic within your loop. By default, the loop index counter is bound to the i variable. You can only have a single as block per for-each loop.

for-each (this.instructions) {
as (instruction) {
divider
single-line {
text {
style (Detail_M_Soft)
value ("Step #{i + 1}") // i is the default index variable and starts at 0
}
}
paragraph {
value ("#{raw(instruction)}")
style (Detail_M)
}
divider
}
}

If you want to use another variable name, you can use the index-var key to specify another variable to bind to the loop counter.

for-each (this.items) {
as (item) {
index-var (j)
if (j == 0) {
// first item
} else {
// second through last item
}
}
}