Remote configuration and A/B Testing framework for iOS. Documentation available online.
One of the big challenges when making iOS apps is a consequence of the fact that pushing an app update takes a long time. Sometimes we want to be able to change something in one of our apps remotely as quickly as possible.
const BOOL isThisFeatureEnabled = [activeConfig[@"AppFeatures"] boolForKey:@"SomeFeatureEnabled"];
with this one-liner, MSActiveConfig
would tell us if that particular feature has been enabled by us, but we can also react to changes on the configuration in real time:
[activeConfig registerListener:self
forSectionName:@"AppFeatures"];
- (void)activeConfig:(MSActiveConfig *)activeConfig
didReceiveConfigSection:(MSActiveConfigSection *)configSection
forSectionName:(NSString *)sectionName
{
[self changeFeatureEnabledStatus:configSection[@"SomeFeatureEnabled"]];
}
- Enabling and disabling features. This allows you to test features with only a subset of users before you roll it out to everyone, or to control the load that feature creates on on your backend, for example.
- Delaying making decisions after submitting the app. E.g.: how often should this request happen?, How many times should this be retried? With Active Config you no longer need to know the answer to those questions before you send your app to Apple, since you can change those values later easily.
- A/B Testing! If you can serve a configuration to your users, you can serve different configurations to different users.
The A/B Testing frameworks out there give most of the responsibility to the app: they assume the app is going to know all the possible values that you are going to want to test for a specific feature. This is incredibly restrictive, since it will force you to update your app to try new values. Active Config encourages you to leave the knowledge on the backend, giving your more control.
For example, if you were to A/B test some text on some part of your app, traditional A/B test frameworks would tell the app to choose option A, or option B. This way, the app must know before hand what those strings are. With Active Config, you would put the string inside the configuration, so you can change them at any point.
Of course there's a lot more to A/B testing than what MSActiveConfig
does, but it provides the foundation for you to implement it in your apps with a lot of flexibility. More specific tools for A/B testing are coming in future releases.
Using CocoaPods:
- Add
pod 'MSActiveConfig', '~> 1.0.1'
to yourPodfile
. - You're done!
git submodule add git@github.com:mindsnacks/MSActiveConfig.git <path/to/your/submodule>
There are two ways you can integrate MSActiveConfig
into your project:
-
If you already have a workspace or don't mind creating one:
- Add the
MSActiveConfig.xcodeproj
file into your project by dragging and dropping it. - Select your project on Xcode, and then your target and navigate to "Build Phases".
- Tap on the "+" button on "Target Dependencies" and add
MSActiveConfig
. - Tap on the "+" button on "Link Binary with Libraries" and select
libMSActiveConfig.a
.
- Add the
-
By simply adding the source files inside
MSActiveConfig/Classes
into your project and compiling them with the rest of your source.
Start by importing MSActiveConfig.h
.
A typical app would have one MSActiveConfig
object that you create like this:
id<MSActiveConfigDownloader> configDownloader = ...
id<MSActiveConfigStore> configStore = ...
MSActiveConfig *activeConfig = [[MSActiveConfig alloc] initWithConfigDownloader:configDownloader
configStore:configStore];
For a complete code snippet on how to instantiate the MSActiveConfig
object, check the wiki.
A class must conform to this protocol to allow MSActiveConfig
to retrieve updates from the network. The given class needs to implement this method.
- (NSDictionary *)requestActiveConfigForUserWithID:(NSString *)userID
error:(NSError **)error;
For most applications, the provided MSJSONURLRequestActiveConfigDownloader
class will suffice, it allows you to create a downloader object like this:
downloader = [[MSJSONURLRequestActiveConfigDownloader alloc] initWithCreateRequestBlock:^NSURLRequest *(NSString *userID) {
return [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myserver.com/activeconfig/user/%@", userID]]];
}];
Note: MSActiveConfig
will never start a download on its own, you must use the public method -downloadNewConfig
to tell it to download.
This protocol defines a series of methods that allows MSActiveConfig
to persist downloaded configuration to be able to retrieve it on subsequent app launches in order to always use the most up to date configuration.
- (MSActiveConfigConfigurationState *)lastKnownActiveConfigurationForUserID:(NSString *)userID;
- (void)persistConfiguration:(MSActiveConfigConfigurationState *)configuration forUserID:(NSString *)userID;
MSActiveConfig
provides one implementation of this protocol that uses NSUserDefault
as its backing store: MSUserDefaultsActiveConfigStore
. This class also allows you to provide an initial or bootstrapped configuration that will be use until the app successfully downloads a more recent configuration from the server.
- (id)initWithInitialSharedConfiguration:(MSActiveConfigConfigurationState *)initialSharedConfiguration;
This class represents a given configuration set that Active Config can use to provide setting values. It can be instantiated from an NSDictionary
for example to create the initial configuration for MSUserDefaultsActiveConfigStore
by parsing a JSON file in the app bundle.
This is the type of object that you get when you ask MSActiveConfig
for configuration. It's a wrapper around all the setting keys and values in the specified ConfigSection (See Configuration Exchange Format below). It has methods to retrieve a value expecting a specific type, and returns a form of nil in case the setting key isn't present or it has a different type.
MSActiveConfig
is built to be extremely flexible. The only rules that you need to follow are imposed by the configuration exchange format that MSActiveConfig
understands. The configuration is represented in a dictionary object that must look like this:
{
"meta":
{
"format_version_string": "1.0",
"creation_time": "2012-08-20T19:36Z",
"other_meta_key": "arbitrary objects with other relevant information"
},
"config_sections":
{
"ConfigSection1":
{
"settings":
{
"SettingKey1":
{
"value": "This can be a a string, number, boolean, array or object"
}
}
},
}
}
When you ask MSActiveConfig
to download an update of the configuration, it will expect a dictionary with this format. If the provided dictionary can't be parsed, the problem will be logged to the console and the configuration will be ignored.
The meta section of the configuration must contain the format version (for future use) and the creation time (for debug purposes). All other added values are optional but can be used to give context to the configuration.
For example, if you're using MSActiveConfig
to do some kind of A/B Testing, you can add some information to the meta dictionary to later on identify what group of the test that user belongs to when sending events to your analytics service. You can access the meta dictionary in two ways:
- When a new configuration finishes downloading,
MSActiveConfig
posts anNSNotification
(MSActiveConfigDownloadUpdateFinishedNotification
) that contains the meta dictionary in one of theuserInfo
keys (MSActiveConfigDownloadUpdateFinishedNotificationMetaKey
). - At any time, by calling the
-currentConfigurationMetaDictionary
method onMSActiveConfig
.
This is the top level group of settings. A section groups a series of settings that are relevant to a specific component of your app. When using the -registerListener:forSectionName:
API, you can be notified when any of the settings of a section changes. They're represented with the MSActiveConfigSection
class.
This is a particular setting contained within a section. These are the ones that you'll ask MSActiveConfigSection
for.
MSActiveConfig
provides APIs to allow you to have different configurations for different users on your app. This is designed for apps that allow you to log-out and log-in with a different user. If this is not your case, all these APIs allow you to simply use nil
as a userID.
Run the target MSActiveConfig-SampleApp
on the Xcode project for a sample implementation of MSActiveConfig
on an app.
This sample app uses this backend to get its configuration.
MSActiveConfig
is a solid framework that has been shipping in a handful of apps with millions of users for > 6 months. Future work is captured in the issues page.
MSActiveConfig
requires iOS6 or higher, but it would be easy to make it support iOS5 if you really need it for your project.
MSActiveConfig
is available under the MIT license. See the LICENSE file for more info.