Rollout.io - Documentation

Creating Feature Flags

Feature flags are at the core of Rollout.io. Using feature flags, you can easily enable and disable features in your application, giving you full control over how users are able to interact with your application.

There are two kinds of feature flags in Rollout:

  1. True / False flags
  2. Multi-Variant flags

Adding a Feature Flag to the code

Creating and using a new flag in Rollout is simple.

Creating a container class

In order to create your first flag, you need two things:

  • A container class for your flags
  • A defined flag inside the container class

The flag name is derived from the flag variable name.
In the below example we defined a true/false flag with the name videoChat.
This flag is false by default.

import Foundation
import ROX
public class Flags: RoxContainer{

  public let videoChat = RoxFlag();
}
// Flags.h file
#import <ROXCore/ROXCore.h>

@interface Flags : ROXBaseContainer

@property (nonatomic) ROXFlag* videoChat;

@end
  
  
// Flags.m file
#import "Flags.h"

@implementation Flags

- (instancetype)init {
  self = [super init];
  if (self) {
    self.videoChat = [[ROXFlag alloc] init];
  }
  return self;
}

@end
public class Flags implements RoxContainer{
  public RoxFlag videoChat = new RoxFlag();
}
const appSettingsContainer = {
  videoChat: new Rox.Flag()
};
const appSettingsContainer = {
  videoChat: new Rox.Flag()
};
const appSettingsContainer = {
  videoChat: new Rox.Flag()
};
public class Flags implements RoxContainer{
  public RoxFlag videoChat = new RoxFlag();
}
public class Flags : IRoxContainer
{
  public RoxFlag videoChat = new RoxFlag(); 
}
from rox.server.flags.rox_flag import RoxFlag

class MyContainer:
     def __init__(self):
        self.video_chat = RoxFlag()
// Default value must be specified with go, see the next example
require 'rox/server/flags/rox_flag'

class Container
	attr_accessor :video_chat

  def initialize
  	@video_chat = Rox::Server::RoxFlag.new
  end
end

Setting flag default value

Rollout true/false flag is false by default. To override the value, just pass the desired value to the flag.
The default value is the value the flag will have if no other value was set to override it.

import Foundation
import ROX
public class Flags: RoxContainer{
  
  public let videoChat = RoxFlag(withDefault: true);
}
#import "Flags.h"

@implementation Flags

- (instancetype)init {
  self = [super init];
  if (self) {
    self.videoChat = [[ROXFlag alloc] initWithDefaultValue:YES];
  }
  
  return self;
}

@end
public class Flags implements RoxContainer{
  public RoxFlag videoChat = new RoxFlag(true);
}
const flags = {
  videoChat: new Rox.Flag(true);
};
const flags = {
  videoChat: new Rox.Flag(true);
};
const flags = {
  videoChat: new Rox.Flag(true);
};
public class Flags implements RoxContainer{
  public RoxFlag videoChat = new RoxFlag(true);
}
public class Flags : IRoxContainer
{
  public RoxFlag videoChat = new RoxFlag(true);
}
from rox.server.flags.rox_flag import RoxFlag

class MyContainer:
     def __init__(self):
        self.video_chat = RoxFlag(True)
import (
        "github.com/rollout/rox-go/server"
)

type Container struct {
  VideoChat server.RoxFlag
}

var container = &Container {
  VideoChat: server.NewRoxFlag(true),
}
require 'rox/server/flags/rox_flag'

class Container
	attr_accessor :video_chat

  def initialize
  	@video_chat = Rox::Server::RoxFlag.new(true)
  end
end

Registering the container class

Once you have the container class, you need to register its instance to the Rollout SDK. This is done with the register SDK function.

The register function should be called before the call to Rox.setup()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

//register Flags instance
  ROX.register("<container_name>", container: Flags())
  ROX.setup(withKey: ROLLOUT_KEY)
  return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [ROX register:[[Flags alloc] init]];
    [ROX setupWithKey:@<ROLLOUT_KEY>];
    
    return YES;
}
public class App extends Application {
  
  @Override
  public void onCreate() {
    super.onCreate();
    
    Flags flags = new Flags();
    Rox.register("<container_name>", flags);
    
    Rox.setup(this);
    
    // This can also be done in the MainActivity if your app doesn't extends the Application class
  }
}
const roxContainer = {
  // define Rox entities here
}
Rox.register('<containerName>', roxContainer);
Rox.setup('<ROLLOUT_KEY>');
const roxContainer = {
  // define Rox entities here
}
Rox.register('<containerName>', roxContainer);
Rox.setup('<ROLLOUT_KEY>');
const roxContainer = {
  // define Rox entities here
}
Rox.register('<containerName>', roxContainer);
await Rox.setup('<ROLLOUT_KEY>');
Flags flags = new Flags();
Rox.register("<container_name>", flags);
Rox.setup(ROLLOUT_KEY);
Container flags = new Container();
Rox.Register("flags", flags);
await Rox.Setup(<ROLLOUT_KEY>);
from rox.server.rox_server import Rox

my_container = MyContainer()

Rox.register(<container_name>, myContainer)
cancel_event = Rox.setup('<ROLLOUT_KEY>').result()

import "github.com/rollout/rox-go/server"

type Container struct {}

var flags = &Container {}
var rox *server.Rox

func initRox() {
  options := server.NewRoxOptions(server.RoxOptionsBuilder {})

  rox = server.NewRox()
  rox.Register("flags", flags)
	<-rox.Setup("<ROLLOUT_KEY>", options)
}
require 'rox/server/rox_server'

class Container
end

container = Container.new

Rox::Server::RoxServer.register(<container_name>, container)
Rox::Server::RoxServer.setup(<ROLLOUT_KEY>).join

Using the flag

The last two things you need to do in the SDK is to use the flag. You can use the flag by checking the value of the flag.

Here's the code:

flags.videoChat.enabled { 
  print("VideoChat Feature is Enabled")
}
[myContainer.videoChat enabled:^{
  NSLog(@"VideoChat Feature is Enabled");
} disabled:^{
  // skip the tutorial
}];
if (flags.videoChat.isEnabled()) {
  Log.i(tag, "VideoChat Feature is Enabled");
}

if (flags.videoChat.isEnabled()) {
  console.log('videoChat is enabled');
}
if (flags.videoChat.isEnabled()) {
  console.log('videoChat is enabled');
}
if (flags.videoChat.isEnabled()) {
  console.log('videoChat is enabled');
}
if (flags.videoChat.isEnabled()) {
  Logger.info(tag, "VideoChat Feature is Enabled");
}

if (flags.videoChat.isEnabled() == true)
{
  // videoChat is enabled
}
if my_container.video_chat.is_enabled():
    print('video_chat is enabled')
if (flags.VideoChat.IsEnabled(nil)) {
  // start the tutorial
}
if container.video_chat.enabled?
  puts "video_chat is enabled"
end

Creating multivariant feature flags

Setting a multivariant flag

Multivariant flags are flags that can have multiple values and not just true or false.
When you create a multivariant flag, you provide the default value and all the other values this flag can have.
Like simple true/false flags, the flag name is derived from the flag variable name.

import ROX
import ROXCore

class Flags : RoxContainer {
  let titleColors = ROXVariant(withDefault: "White", options: ["White", "Blue", "Green", "Yellow"])
  
  // The following is the same thing but with Enum instead of String array
  let titleColorsEnum = RoxEnumVariant(Colors.White)
}

enum Colors : Int {
  case White
  case Blue
  case Green
  case Yellow
}
// Flags.h file
#import <ROXCore/ROXCore.h>

@interface Flags : ROXBaseContainer

@property (nonatomic) ROXVariant* titleColors;

@end
  
  
// Flags.m file
#import "Flags.h"

@implementation Flags

- (instancetype)init {
  self = [super init];
  if (self) {
    self.titleColors = [[ROXVariant alloc] initWithDefault:"White" options:@[@"Blue", @"Green", @"Yellow"]];
  }
  return self;
}

@end
public class Flags implements RoxContainer{
  public RoxVariant titleColors = new RoxVariant("White", new String[] {"White", "Blue", "Green", "Yellow"});
  
  public enum Color {
    WHITE, BLUE, GREEN, YELLOW
  }
  // The following is the same thing but with Enum instead of String array
  public RoxEnumVariant<Color> titleColorsEnum = new RoxEnumVariant<>(Color.WHITE);
const flags = {
  titleColors: new Rox.Variant('White', ['White', 'Blue', 'Green', 'Yellow']),
};
const flags = {
  titleColors: new Rox.Variant('White', ['White', 'Blue', 'Green', 'Yellow']),
};
const flags = {
  titleColors: new Rox.Variant('White', ['White', 'Blue', 'Green', 'Yellow']),
};
public class Flags implements RoxContainer{
  public RoxVariant titleColors = new RoxVariant("White", new String[] {"White", "Blue", "Green", "Yellow"});
  
  public enum Color {
    WHITE, BLUE, GREEN, YELLOW
  }
  // The following is the same thing but with Enum instead of String array
  public RoxEnumVariant<Color> titleColorsEnum = new RoxEnumVariant<>(Color.WHITE);
from roxserver.flags.rox_flag import RoxFlag
from roxserver.flags.rox_variant import RoxVariant

class MyContainer:
     def __init__(self):
        self.title_colors = RoxVariant('White', ['White', 'Blue', 'Green', 'Yellow'])
import (
        "github.com/rollout/rox-go/server"
)

type Container struct {
        titleColors server.RoxVariant
}

var flags = &Container {
        titleColors: server.NewRoxVariant("Green", []string{"White", "Blue", "Green", "Yellow"})}
require 'rox/server/rox_server'
require 'rox/server/flags/rox_variant'

class Container
	attr_accessor :title_colors
	
  def initialize
 		@title_colors = Rox::Server::RoxVariant.new('red', ['red', 'blue', 'green'])
 	end
end

In this example, the title color can be any one of black, blue, green and yellow. By default, it will be white.

Using multivariant flag

switch roxContainer.titleColors!.value()! {
  case "White":
    print("Title color is White")
  case "Blue":
    print("Title color is Blue")
  case "Green":
    print("Title color is Green")
  case "Yellow":
    print("Title color is Yellow")
  default:
    print("Title color is default - White") 
}

// for Enum based multivariant flag
switch roxContainerEnum.titleColors.value {
  case .White:
    print("Title color is White")
  case .Blue:
    print("Title color is Blue")
  case .Green:
    print("Title color is Green")
  case .Yellow:
    print("Title color is Yellow")
}
if ([roxContainer.titleColors.value isEqualToString:@"Blue"]) {
	NSLog(@"Title color is Blue");
}
else if ([roxContainer.titleColors.value isEqualToString:@"White"]) {
	NSLog(@"Title color is White");
}
else if ([roxContainer.titleColors.value isEqualToString:@"Green"]) {
	NSLog(@"Title color is Green");
}
else if ([roxContainer.titleColors.value isEqualToString:@"Yellow"]) {
	NSLog(@"Title color is Yellow");
}
if (roxContainer.titleColors.value() == "Blue") {
  Log.i(tag, "Title color is blue");
} else if (roxContainer.titleColors.value() == "Green") {
  Log.i(tag, "Title color is green");
} else if (roxContainer.titleColors.value() == "Yellow") {
  Log.i(tag, "Title color is yellow");
} else if (roxContainer.titleColors.value() == "White") {
  Log.i(tag, "Title color is white");
}

// for Enum based multivariant flag
switch (roxContainer.titleColorsEnum.getValue()) {
  case BLUE:
    Log.i(tag, "Title color is blue");
    break;
  case GREEN:
    Log.i(tag, "Title color is green");
    break;
  case YELLOW:
    Log.i(tag, "Title color is yellow");
    break;
  case WHITE:
    Log.i(tag, "Title color is white");
    break;
}
if (roxContainer.titleColors.getValue() === 'Blue') {
  console.log('Title color is blue');
} else if (roxContainer.titleColors.getValue() === 'Green') {
  console.log('Title color is green');
} else if (roxContainer.titleColors.getValue() === 'Yellow') {
  console.log('Title color is yellow');
} else if (roxContainer.titleColors.getValue() === 'White') {
  console.log('Title color is white');
}
if (roxContainer.titleColors.getValue() === 'Blue') {
  console.log('Title color is blue');
} else if (roxContainer.titleColors.getValue() === 'Green') {
  console.log('Title color is green');
} else if (roxContainer.titleColors.getValue() === 'Yellow') {
  console.log('Title color is yellow');
} else if (roxContainer.titleColors.getValue() === 'White') {
  console.log('Title color is white');
}
if (roxContainer.titleColors.getValue() === 'Blue') {
  console.log('Title color is blue');
} else if (roxContainer.titleColors.getValue() === 'Green') {
  console.log('Title color is green');
} else if (roxContainer.titleColors.getValue() === 'Yellow') {
  console.log('Title color is yellow');
} else if (roxContainer.titleColors.getValue() === 'White') {
  console.log('Title color is white');
}
if (roxContainer.titleColors.value() == "Blue") {
  Log.i(tag, "Title color is blue");
} else if (roxContainer.titleColors.value() == "Green") {
  Log.i(tag, "Title color is green");
} else if (roxContainer.titleColors.value() == "Yellow") {
  Log.i(tag, "Title color is yellow");
} else if (roxContainer.titleColors.value() == "White") {
  Log.i(tag, "Title color is white");
}

// for Enum based multivariant flag
switch (roxContainer.titleColorsEnum.getValue()) {
  case BLUE:
    Log.i(tag, "Title color is blue");
    break;
  case GREEN:
    Log.i(tag, "Title color is green");
    break;
  case YELLOW:
    Log.i(tag, "Title color is yellow");
    break;
  case WHITE:
    Log.i(tag, "Title color is white");
    break;
}
print('color is %s' % my_container.title_colors.get_value())
  switch flags.titleColors.GetValue(nil) {
        case "Green" :
                fmt.Println("green");
        case "White" :
                fmt.Println("white");
        case "Blue" :
                fmt.Println("blue");
  }
puts "color is #{container.title_colors.value}"