Why do you need this segmentation at all? Yes, because users are very different. Suppose the application is free. Two users have downloaded it. One has 0 rubles on his account and he will never buy anything, while the other has made purchases in the app for 1,500 rubles. One goes on foot, and the other goes to Bentley. Obviously, the approach to these people should be different.
Why do AB testing? To test your hypotheses on how to optimize sales. Which banner sells best - with a cat or dog? We divide the audience into two equal parts, one shows a cat, the other a dog. We compare sales, draw conclusions, change the behavior of the application without an update.
There are ready-made systems on the market that solve the voiced tasks. For example:
- swrve.com - “In terms of pricing Swrve is between $ 2000 and $ 9000 payable monthly, with an annual subscription. It is a test of a / b tests. ”
- www.localytics.com - “ENTERPRISE Starts at $ 1,790 per month, all apps (volume-based pricing).”
I focus on cost here. I would be happy to use one of them, if it were cheaper. Tell us if you know similar cheap or even free systems.
')
Then I will tell you how to quickly and cheaply build your own solution. It does not need to make a server with a database, which greatly simplifies the task.
Configuration
Let us introduce the concept of “configuration” (“config”) for an application. Here we will store all the settings of the application - what and to whom to show. The config is a static JSON file, the same for all users. Config need to put on some hosting (any site is suitable). The application will download it at each launch (for example, through
NSURLConnection ).
Next, I will talk about the example of banners, although you can apply for any aspect, up to the colors of the buttons. Our configuration looks like this:
{ "banners": [ { "if": "$user_type == 'geek'", "link": "http://habrahabr.ru", "image": "habrahabr.jpg" }, { "if": "$user_type == 'has_money'", "link": "http://buyall.com", "image": "buyall.jpg" } ]}
Here, in addition to the obvious “image” and “link”, the banner also has an “if” field. It is key to user segmentation. So we ask whom to show which banner.
I recommend disabling caching when downloading a config so that there is always the latest version of the file. It also makes sense to encrypt the config so that some tricky users will not customize the application to fit their needs.
User variables
Let's set up in the application “user variables”, which we can use in “if” expressions in banners. This is done simply, like this:
- (void)onProductBought:(NSString*)productId {
Variables are a dictionary of keys and values. What exactly to include there, everyone chooses himself, based on their needs. In variables, you can store the status of purchases, user type (determined on the basis of different data), social network activity, device type (ipad, iphone), application version, etc.
On the first launch of the application, we also create boolean variables for AB testing:
- (void)onAppStarted { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; if (![defaults objectForKey:@"install_date"]) { [defaults setObject:[NSDate date] forKey:@"install_date"]; [defaults setBool:(arc4random() % 2) == 0 forKey:@"test_a"]; [defaults setBool:(arc4random() % 2) == 0 forKey:@"test_b"]; [defaults setBool:(arc4random() % 2) == 0 forKey:@"test_c"]; [defaults synchronize]; } }
Three variables are enough to conduct three independent tests simultaneously. For example, one variable is responsible for the banner background color (blue or red), the second for the banner text color (white or black), and the third for the banner text itself (neutral or outrageous).
Expressions from variables
Based on user variables, we can compose expressions. This is conveniently done using standard iOS predicate tools. Read more about it
here .
As a result, you can make flexible conditions. For example:
- $ user_type == 'geek' && $ test_a == 1 - banner for geeks from test group N2
- $ purchases_count> 2 && $ test_a == 0 - banner for those who made more than 2 purchases from test group N1
Such expressions are calculated very simply:
- (BOOL)evaluatePredicate:(NSString*)predicateStr { NSDictionary* userVars = [self userVars]; @try { NSPredicate* predicate = [NSPredicate predicateWithFormat:predicateStr]; return [predicate evaluateWithObject:nil substitutionVariables:userVars]; } @catch (NSException *exception) { NSLog(@"Exception! %@", exception); return NO; } }
In my case with banners, I consider the expression “if” and if the condition is met, then I download and show the banner.
Analytics system
For AB-testing necessarily need analytics system. For this we can use the free
Flurry . Then instead of the usual:
[Flurry logEvent:@”PRODUCT_BOUGHT” withParameters:@{ @”product_id”: productId } ];
we will use:
[Flurry logEvent:@”PRODUCT_BOUGHT” withParameters:[self paramsWithUserVars:@{ @”product_id”: productId }] ]; - (void)paramsWithUserVars:(NSDictionary*)params { NSMutableDictionary* newParams = [[NSMutableDictionary alloc] initWithDictionary:params]; NSDictionary* userVars = [self userVars]; for (NSString* var in userVars) { newParams[var] = userVars[var]; } return newParams; }
When working with analytics you need to take into account some limitations. For the Flurry event, the number of parameters cannot exceed 10. Therefore, the extra variables from the parameters should be cut off. Flurry also collects information on sessions. Therefore, in order to count the number of users, one should not send some events twice.
Push notifications
An important marketing tool are push notifications. We also want to apply segmentation to them. In order not to annoy push “buy” those who have already bought.
We’ll need a free push-to-mail solution -
Urban Airship (UA). It is free up to 1 million pushes per month, which is quite enough for small projects. UA has a tag and segment mechanism. Tags allow you to mark a user with a label (string). Segments allow you to make expressions from tags and send pushy only to a given segment. For convenience, we will use the same user variables as tags, but in a different form. Instead of {@ “product_id”: 1} there will be @ ”product_id_1”.
NSMutableArray* pushTags = [NSMutableArray new]; NSDictionary* userVars = [self userVars]; for (NSString* var in userVars) { NSString* tag = [NSString stringWithFormat:@"%@_%@", var, userVars[var]]; [pushTags addObject:tag]; } [UAPush shared].tags = pushTags; [[UAPush shared] updateRegistration];
Each change to a variable must be tracked and updated in the UA library.
Further on the UA site in the Audience> Segments section, you can create the necessary segments. For this, there is a web designer. As an additional bonus, UA will calculate the approximate audience of each segment.
You can send pushes to all devices directly on the UA website. Segments can only be sent via
API . Apparently this is done as an additional incentive to go to a paid service. But we will use the curl utility and write a small script:
Before sending a push to a segment, I recommend checking it first on one device:
curl -X POST -u "$APP_AUTH" -H 'Content-Type: application/json' -d '{"device_tokens":["$TEST_DEVICE_TOKEN"],$PUSH_MESSAGE}' https://go.urbanairship.com/api/push/
Conclusion
The described system is not perfect, sometimes inconvenient to use, but nevertheless solves the task for small projects (300.000 users - ok). You can implement it in a day and, if properly used, this investment will pay off very quickly. The maintenance of the system does not require money (except hosting, where the config is located). When your project becomes successful and gains momentum, it makes sense to move from your decision to a commercial one. I wish you all good luck!