I open sourced a new way to monetize a mobile app, called PledgeWall.
PledgeWall is a widget that lets users write a message on a public wall, and their messages get styled and placed more prominently based on how much money they pledge. I consider this to be an alternative to traditional methods of app monetization.
I built PledgeWall using Parse, because it’s the easiest thing ever. In the past, I’d usually fire up a Django/AppEngine instance, and do any simple server-side stuff there, but Parse is easily 100X as fast from a cold start – and I really didn’t want to spend more time than most of a Saturday on this app release.
PledgeWall in StopWatch+
Let me back up – I built this PledgeWall widget for v1.2 of my timer app, StopWatch+
(source code) (download free from iTunes).
StopWatch+ is my experiment in what happens if I pick a common, easy-to-build app with established market leaders, and try and beat them simply by having the best app in the genre.
v1.0 of StopWatch was:
- biggest possible button is my key differentiator – no button at all
- countdown with multiple modes
- remember all of your times; export, and delete
- Tweet a time (only added this because Apple wouldn’t approve another simple stopwatch app – I got rejected the first time)
In v1.1, I still didn’t add anything server-side, but I did try and monetize it:
- added countdown timer
- added prompt to buy “Bowser Skin” – a red/yellow theme for the app for .99
- iPad universal
Which brings me to v1.2. Unhappy with making $1/day from selling Bowser Skins, yet getting 400 downloads/day, I decided I needed a creative way to monetize the app. I didn’t want to be another schlocky ad-riddled app, which would make $2/day instead. So, v1.2 is some graphical improvements, bug fixes, and PledgeWall.
How to Monetize StopWatch+ and Make it Fun for Users
PledgeWall is inspired by a couple of things. One is the age old concept of published listing of sponsors – like when you go to a play, the program always includes the rich people who donated in the back, with prominent placement for the biggest donors.
I am also an NPR fan, and I love the kind of content that gets produced by their similar model of having people donate. My complaint with NPR has always been that once you pledge, the station still bombards you with the pledge drive. PledgeWall only nags you until you pledge!
Finally, it’s inspired by modern day companies like KickStarter and Indiegogo – these start-ups have given me greater faith in users to participate in voluntary funding of app development.
Going Serverless with Parse
My company has a great many servers, which thankfully my wife/co-founder Anna tends, along with our gracious friends who help us bring back dead Amazon machines when storms strike and corrupt our RAID. This happened to us the same weekend I was working on StopWatch+ v1.2.
So, when I needed to add a server-side component to my app, StopWatch+, I decided it was a perfect opportunity to save myself a lot of now-time and future-time, by using Parse. You can find all of this in the complete source code, but here are the bits where I used the API:
1) Never-ending tableview
Someday, I hope the Pledge Wall has millions of posts, and I wanted to make sure it would handle any number of posts. So, I have a never-ending tableView that lets you “Load More” after each 1000 entries. Here’s a screenshot and the Parse code:
I’m not sure if this is the “idiomatic” way to do this in Parse, or if such a thing even exists, but here’s my implementation:
// if we have never loaded pledges or we need to fresh, load pledges - (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if ([self.pledges count] == 0 || self.pledgeCount == FLAGGED_FOR_REFRESH) { self.pledges = [NSMutableArray array]; [self loadMorePledges]; [[self.view viewWithTag:LOADING_VIEW_TAG]removeFromSuperview]; } } // fetch up to PLEDGES_PER_CHUNK more pledges to show on the server - (void) loadMorePledges { PFQuery *query = [PFQuery queryWithClassName:@"Pledge"]; // great syntax for cascading sorts [query orderByDescending:@"level"]; [query addDescendingOrder:@"createdOnDevice"]; query.limit = PLEDGES_PER_CHUNK; query.skip = [pledges count]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { if (!error) { if ([objects count] < PLEDGES_PER_CHUNK) { self.pledgeCount = NO_PLEDGES_TO_FETCH; } else { self.pledgeCount = PLEDGES_TO_FETCH; } self.pledges = [self.pledges arrayByAddingObjectsFromArray:objects]; [self.tableView reloadData]; } else { NSLog(@"Error: %@ %@", error, [error userInfo]); } }]; }
And here's a look at the UI where you buy the pledges too:
The Parse code for saving a pledge is canonically simple, goodbye server:
- (void) addPledge { PFObject *pledge = [PFObject objectWithClassName:@"Pledge"]; [pledge setObject:N(buyRow) forKey:@"level"]; [pledge setObject:pledgeField.text forKey:@"message"]; [pledge setObject:[NSDate date] forKey:@"createdOnDevice"]; [pledge save]; … }
2) added ability to control events serverside
Right when I was about to ship the app, I decided I wanted to be able to control how often I prompted users to pledge from the server-side. I wanted to launch it with the same frequency StopWatch+ now uses for prompting the user to buy a "bowser color pack," and see if prompting people to pledge at the same rate resulted in different revenue. Then, I want to be able to adjust the prompt frequency and measure the results from that.
I estimate it's 100 times faster to do this with Parse than with AppEngine/Django. Even if you already have an instance set up, it's still more than 10X faster to write the following code, than to write a view, edit urls.py, and deploy an update.
#define BUG_ME_DAYS_DEFAULT 10 // prompt the user to pledge, control time between prompts via Parse dashboard - (void) comeOnPromptMeMaybe { SavingDictionary *settingsDict = SETTINGS_DICT; int launches = [[settingsDict objectForKey:@"LAUNCHES"]intValue]; if (launches <=0) { launches = 1; } else { launches++; } PFQuery *query = [PFQuery queryWithClassName:@"NumberOfLoads"]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { int bugMeDays = BUG_ME_DAYS_DEFAULT; if (!error) { bugMeDays = [[[objects objectAtIndex:0]objectForKey:@"loads"]intValue]; } else { NSLog(@"Error: %@ %@", error, [error userInfo]); } if (launches < bugMeDays) { [settingsDict setObject:N(launches) forKey:@"LAUNCHES"]; } else { [settingsDict setObject:N(0) forKey:@"LAUNCHES"]; [self promptToBuy]; } }]; }
3) use the online spreadsheet view to do moderation
I am a bit worried people will scrawl obscenities all over the PledgeWall, but I really don't want to deal with the issue in a heavy-handed way until it either happens, or people start pledging in droves. So, I was delighted just to have the ability to go edit/delete posts on Parse's online dashboard, so I can do post facto moderation:
4) Serverless in-app purchases
Also, setting up IAP is typically a pain, but since I just used non-subscription purchases and had Parse to store the messages, it was super-simple. The app-building world is starting to be an easier place!
Conclusions
My takeaways from this project so far are:
- Hacking on the weekends fun - according to my Harvest app, I have 23.18 hours logged so far on StopWatch+ - right now, I'm making nickels per hour, but maybe PledgeWall will do the trick - I don't need to make much for this to be both fun and worthwhile!
- Parse is magic - hat tip to my friend Kevin for helping build the machine
- You can beat entrenched apps just by programming, but it takes some persistence. I'm looking at you Tim O' - you're going down!
- Maybe there's a company somewhere in PledgeWall, though it probably needs to grow past my implementation and not just use Apple In-App-Purchases.
Other than that, I don't yet know how well PledgeWall is working, since the release just launched, but I'll report on the numbers when I know. I hope to also conclude from this project that ads suck and PledgeWall is the future! If this works out at all, I have another app coming up where we think it will be much more suitable - a very philanthropic project.
Software License
I am including the full source code for my StopWatch+ app as part of this post, under the following license. If you have any questions about if you can use the code, email stopwatch@gaiagps.com.
You MAY copy/steal/use any one method or class file in the project - you MAY even use many methods or classes. However, you MAY NOT publish this code verbatim as another app, nor may you augment the code and publish it as another app.
Some things we intend you to do:
- use all the PledgeWall code in your own app
- use any view, including the main view, but write all other code yourself
- use this app as a template to make an identical Android app
Just don't copy my StopWatch app and publish it. That wouldn't be nice. I won't even sue you, but I'll make fun of you on the internet, and crush your app by programming better 😛