Flurry Remote Config for iOS

Modes of Operation

When using Flurry Config, there are two modes of operation. In the first mode, referred to as the Simple Mode, the developer needs only to make a minimal number of calls and she will have the full config system at her disposal. The caveat, though, is that any new configs will only be loaded on the next app cold start.

The Advanced Mode gives the developer much finer grain control, allowing for greedy activation of a newly fetched config based on subscribing to observers which notify of fetch. Choosing between the Simple Mode and the Advanced Mode with its greedy activation is a choice to be considered. While the Simple Mode of fetch and activation on the next cold start allows for the utmost consistency in the UI, a greedy activation of a newly fetched config makes the app more responsive to the developers wishes. Example of when greedy activation may make sense: notify users that service is down or prompt user to update to latest version.

Checking Config Flags

All config values are stored as strings but for convenience three getters are provided to allow the developer to interact with the config system in an easier manner.

- (NSString *)getStringForKey:(NSString *)key

withDefault:(NSString *)def;

- (BOOL)getBoolForKey:(NSString *)key

withDefault:(BOOL)def;

- (NSNumber *)getNumberForKey:(NSString *)key

withDefault:(NSNumber *)def;

Upon calling a method other than getStringForKey:withDefault: the SDK will attempt to make some kind of meaningful conversion. In the case of getBoolForKey:withDefault: any value other than “0” will be interpreted as YES.

Why Defaults?

With each getXForKey:withDefault: call there is an (optional) default value that may be passed in. In some circumstances it is possible that a config will not be available. Providing a default allows the config system to have a hard-coded fallback. It is also useful for testing.

Simple Mode

There are only two calls necessary in simple mode:

(FConfig *)sharedInstance;

(void)fetchConfig;

It is not strictly necessary but is a best practice to call [FConfig sharedInstance] in application:DidFinishLaunchingWithOptions:. This method acts as an initiator for the config system and will also cause the loading of any previously fetched configs on a cold start.

The simplest form of activation which would also fetch configs for the next cold start would look like this:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

[Flurry startSession:@"MY_API_KEY"];

[[FConfig sharedInstance] fetchConfig];

return YES;

}

From here the developer can make getter calls as normal. This will ensure that the latest config will be fetched and loaded for the next cold start. In the meantime, the previously loaded config can be relied on.

Advanced Mode

It might be the case that a greedier application of the config is desired or necessary. Flurry Remote Config provides the tools necessary for this use case.

The Observer and Activation

In the simple mode a config is “activated” on the next cold start after the config is fetched. However, there is a method that allows the developer to directly activate a config (preferably once it’s been fetched).

- (BOOL)activateConfig;

This will take a newly fetched config and apply it greedily.

However, there is very little point in calling activate if a new config has not been fetched. Hence, the SDK provides a protocol to which the developer can register a class as an observer. The observer will be called back when a config has been fetched. The developer may implement this protocol and the following methods in the observing class:

@protocol FConfigObserver

- (void) fetchComplete;

- (void) fetchCompleteNoChange;

- (void) fetchFail;

- (void) activationComplete;

@end

To register your class, a dispatch execution queue should be provided.

- (void)registerObserver:(id)observer withExecutionQueue:(dispatch_queue_t)queue;

The dispatch queue, while perhaps seemingly an odd addition, is important. Flurry Config operates in a completely thread safe manner. When an observer is called all execution will take place on the dispatch queue provided. This has the effect of making sure that the developer controls the thread safety of any UI or app modifications.

Any class may be registered as an observer. As a result, the app can update only the elements which need updating and defer other elements for later (on the cold start). A great example of this would be to register a ViewController as an observer and have it update the UI on fetch completion:

- (void)viewDidLoad {

    [super viewDidLoad];

    // create the dispatch queue

    self.queue = dispatch_queue_create("dispatch_queue_#2", 0);

    // Register as an observer

    [[FConfig sharedInstance] registerObserver:self withExecutionQueue:self.queue];

   ...

}

- (void)fetchComplete {

    [[FConfig sharedInstance] activateConfig];

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

    // Do some UI work here

    ...

  }];

}

In order to ensure that the application does not get caught in a transitional state by some unanticipated caller on a successful activation all observers will be notified of the activation. An observer may then update its state with the latest config if that is desired, much in the way that an observer can update itself on fetch completion.