Uh oh! We couldn’t find any match.

Please try other search keywords.

Bixby Developer Center

Guides

Conditionals and Control Flows

Conditionals use logic to guide the runtime execution along a path appropriate for the current context. Here are the different control flow statements that are available:

if-else

if, else-if, else These keywords allow chaining choices in this order:

  • Start with a required if(%expression%)
  • Follow with any number of optional else-if(%expression%)
  • End with an optional else.

For example:

if(%expression%) {
  ...
} else-if(%expression%) {
  ...
} 
else {
  ...
}

The chain will be evaluated from top to bottom and execute the first qualifying code block:

  • if(%expression%) and else-if(%expression%) qualify if their %expression% evaluates to true. You can use Expression Language to write powerful %expression% choices.
  • else always qualifies when it is reached.

Example

The recipe.FindRecipes action uses relaxation to progressively drop the less meaningful inputs. As long as the action returns no results, this chain of conditionals will be evaluated to determine which input to drop next.

output (Recipe) {
on-empty {
if (exists(maxCalories)) {
drop (maxCalories)
} else-if (exists(servings)) {
drop (servings)
} else-if (exists(collection) && !relaxed(collection)) {
drop (collection)
} else-if (exists(flavorProfile)) {
drop (flavorProfile)
} else-if (exists(maxPreparationPeriod)) {
drop (maxPreparationPeriod)
} else-if (exists(theme)) {
drop (theme)
} else-if ("exists(course) && !contains(course,'Dessert')") {
drop (course)
} else-if (exists(cuisineAttribute) || exists(cuisine)) {
ordered-effects {
drop (cuisineAttribute)
drop (cuisine)
}
} else-if (exists(meal) && !relaxed(meal) && !relaxed(collection)) {
drop (meal)
} else-if (exists(ingredientToInclude)) {
drop (ingredientToInclude)
}
}
}

This is a strategy that conditions the time of day to the different meals.

instantiation-strategy {
id (recipes-default-meal)
match {
viv.food.MealName (meal) {
to-input: viv.recipe.FindRecipes(action)
}
}
if ("now().time.hour < 8") {
strategy {
intent {
goal: viv.food.MealName("Breakfast")
}
}
}
if ("8 <= now().time.hour && now().time.hour < 11") {
strategy {
intent {
goal: viv.food.MealName("Brunch")
}
}
}
if ("11 <= now().time.hour && now().time.hour < 14") {
strategy {
intent {
goal: viv.food.MealName("Lunch")
}
}
}
if ("14 <= now().time.hour && now().time.hour < 21") {
strategy {
intent {
goal: viv.food.MealName("Dinner")
}
}
}
if ("21 <= now().time.hour") {
strategy {
intent {
goal: viv.food.MealName("Snack")
}
}
}
}

switch

To define a switch, start with switch(%expression%){}. Then add one or more case(%value%) and a final optional default block.

switch (%expression%) {
case (%value%) {
...
}
case (%value%) {
...
}
default {
...
}
}

The switch will be evaluated from top to bottom by comparing the %expression% against each case. It will execute the code block for the first case that matches (otherwise it will fallback to the default block, if present).

Example

A very common use case is to adapt dialog to the quantity:

dialog (Concept) {
match {
viv.recipe.RecipeServings (this)
}
switch (plural(this)) {
case (One) {
template (serving)
}
default {
template (servings)
}
}
}

choose

By default, Bixby will select the first occurrence of whatever it is looking for (such as a 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%) {
...
}

Here are the possible values for %order%:

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

Example

This makes the Result Dialog for recipes more variable.

dialog (Result) {
match {
viv.recipe.Recipe (this)
}
switch (plural(this)) {
case (One) {
template ("Here is a #{concept(this)}")
}
default {
choose(Random) {
template ("Here are some #{concept(this)}")
template ("I found some #{concept(this)}")
template ("I found some yummy #{concept(this)}")
template ("Here are some tasty #{concept(this)}")
}
}
}
}

for-each

Loops through an array of structures, letting you render each element within the array in a separate component. The for-each key specifies an as key which sets the variable name of the individual element in each loop iteration. For example, if you specify as (item), you can then refer to each item defined in the loop with item: text ("#{item.line}").

Example

This creates a partitioned area in your layout, with separated rows that iterate through a list of ingredients. Each hbox contains a line item and the quantity of that item in separate vbox containers.

partitioned {
content {
for-each (this.ingredients) {
as (ingredient) {
hbox {
content {
vbox {
halign (Start)
content {
text {
value ("#{ingredient.line}")
style (Title_XS)
}
}
}
vbox {
halign (End)
content {
text {
value ("#{ingredient.quantity}")
style (Title_XS)
}
}
}
}
}
}
}
}
}

You can optionally use an index-var key within the as block to bind a variable to the loop index. This is a counter that starts at 0 and is incremented 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.

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