Saturday, October 4, 2014

How I developed SMS-like hookup app on WhatsApp

Recently, I have been working with Omar and Kweku on a new social app called Beep. It's a hookup app that allows you to hook up with your friend's friends. You can read more about the app here: http://getonbeep.com/

Users sign up for the service using their phone numbers. We wanted to find a way to authenticate users without incurring SMS costs. From experience with developing Saya, SMS authentication cost increases fast as you get more users.

Our attempt at solving the issue was to get a short code that users can text a code to to authenticate their numbers. In case that we can shift the cost to the user even though that will obviously affect our conversion rate.

So last week, I had a discussion with Omar. What if we can send the code to users through WhatsApp? Or How about users can send us the code through WhatsApp? Considering 90% of our target market (mainly college students) are already using WhatsApp, that wasn't a bad idea. We cut our costs and users don't have to pay for SMS.

Luckily there's WhatsApp api library called Yowsup. To test the concept I decided to build a SMS-like hookup service on WhatsApp. I called the service WhookApp.

This is how it works:

To create profile, user sends:

JOIN, name, gender, age, location

The user get's a reply through WhatsApp:
You have successfully created your profile on WhookApp

Behind the scene, the service gets the user's phone from the WhatsApp message (as sender). Then registers the user to the system.

To find hookups, send

FIND, gender, age range (optional), location(optional)

You will receive a WhatsApp message like below. It shows the total number of hookups found and the first hookup

Found : 10

Name: Lolo
Age: 25
Loction: East Legon
Phone: +233241111111
Message: I'm a happy go lucky girl!

To get the next result from the last search, user sends
NEXT

Users can update their profile, set their status messages and other stuff. Also, since it's possible to send location on WhatsApp, you can easily provide users with hookups around them.


PS:
Unfortunately WhatsApp blocked my number. Which is in accordance with at their Terms of Service which does not allow harvesting/soliciting information from users. I tested it with just my number so I didn't solicit any phone number for any user.

I developed using python on top of Yowsup (a python WhatsApp api)  and the code is available on github. I used Redis and Elasticsearch for the backend database.

Below is a screenshot of me testing the service by finding a hookup. The data is dummy data generated using Faker.




I think WhatsApp can become the next platform for developing SMS/Push like services. Just like Facebook Connect. I hope to do a separate post on that. But for now No Games, No Ads, No Gimmicks. And no developer platform.

Saturday, September 14, 2013

Unique Auto Increment Values for NSManagedObjects (Core Data)

Auto Increment Values for NSManagedObjects

Unique Auto Increment Values for NSManagedObjects

Core Data automatically generate auto increment id for each managed object.

The unique auto id is however not exposed through the api. However, there is [NSManagedObject objectID] method that returns the unique path for each object.

Its usually in the form <x-coredata://SOME_ID/Entity/ObjectID> e.g <x-coredata://197823AB-8917-408A-AD72-3BE89F0981F0/Message/p12> for object of Message entity with ID `p12. The numeric part of the ID (last seqment of the path) is the auto increment value for each object.

Here is a method that returns the auto increment integer value for an NSManagedObject

@implementation NSManagedObject(Extra)

-(NSUInteger)autoId{

NSURL *url = [[self objectID] URIRepresentation];
//get full url
NSString *url_string = [url absoluteString];

//split with a /
NSArray *parts = [url_string componentsSeparatedByString:@"/"];

NSString *last_segment=[parts objectAtIndex:[parts count]-1];

//p#
NSString *number_part = [last_segment stringByReplacingOccurrencesOfString:@"p" withString:@""];

NSUInteger auto_id = [number_part integerValue];

number_part =nil;
last_segment=nil;
parts=nil;
url=nil;
url_string=nil;


return auto_id;

}
@end

Side Note

Do not use this method on the fly. When I first used this method in our chat app, it took much time to render the messages. I was using the auto increment value to order the messages.

To solve this, I added a new attribute to the Messages entity called auto_increment_integer. Anytime I create and save a new object, I set its auto_increment_integer value to the return value of autoId.

Like this:

    NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];

    Message *m = [Message MR_createInContext:localContext];
    //Set attributes here
    //....
    //save object
    [localContext MR_saveToPersistentStoreAndWait];

    //set the auto increment value
    m.auto_increment_id=[NSNumber numberWithInt:[m autoId]];
    //save again
    [localContext MR_saveToPersistentStoreAndWait];

Hope this helps.

Saturday, August 31, 2013

Custom Back Button UINavigationBar

Custom Back Button - UINavigationBar

Custom Back Button - UINavigationBar

IOS comes with useful controls for developers which makes app development and UI navigation pretty easy.

One of such controls is the UINavigationController.

UINavigationController allows you to push ViewControllers onto a stack. It comes with a UINavigationBar which automatically contains back button to go to the previous View from the current view.

Using UINavigationController is common is most ios apps.

The back button which is a UIBarButtonItem usually comes with the defaut chrome for the IOS app.

However, if you have a different design for your app, you may as well want to have a custom back button.

There are two ways to do this:

1) Change the background Image of the back button

2) Create a custom UIBarButtonItem as the back button (and hide the original back button)

This post will focus on the second method. The first method works in situations where you dont want to change the text on the back button, just the background image or the theme.

UINavigationBar

The UINavigationBar is the top bar that's is automatically placed at the top of UINavigationController to allow for navigating back and forth.

The UINavigationBar of a UINavigationController can be accessed (from a UIViewController)

self.navigationItem

So to get our custom back button we are basically doing this;

  1. Hide the default back button
  2. Create a custom UIBarButtonItem
  3. Add it to the UINavigationBar

Another thing we need to do is to ensure we do steps 2 and 3 only when we have are on a view other than the first on the stack.

  1. Create a new single view application from Xcode
  2. Add UIViewController call it BaseUIViewContoller

In BaseUIViewController.m, make the following changes to the default viewDidLoad method

    -(void)viewDidLoad
    {
        [super viewDidLoad];
        //hide the default back button 
        [self.navigationItem setHidesBackButton:YES];
    }
  1. We are going to add a method that will create a custom UIBarButtonItem using our custom back button image

In BaseUIViewContoller.h add this

    -(UIBarButtonItem*)barButtonWithImage:(UIImage*)image 
    target:(id)target 
    action:(SEL)action;

In BaseUIViewController.m add this for the implementation of the method

    -(UIBarButtonItem*)barButtonWithImage:(UIImage*)image 
    target:(id)target 
    action:(SEL)action
    {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

    [button setBackgroundImage: [image stretchableImageWithLeftCapWidth:7.0 topCapHeight:0.0] forState:UIControlStateNormal];

    [button setBackgroundImage:image forState:UIControlStateHighlighted];

    button.frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);

    [button addTarget:self action:action  forControlEvents:UIControlEventTouchUpInside];

    UIView *v=[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, image.size.width, image.size.height) ];

    [v addSubview:button];

    UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:v];

    return barButton;
    }

Finally lets add another method that will show the back button only when we are not on the first ViewController in a NavigationViewController

Remember we are hidding the back button. However, the navigationItem property of the UIViewController has leftButtonItems which is array of buttons that will appear left side of the UINavigationBar. So we ca add our custom UIBarButtonItem to this array and have it act as our back button!

In BaseUIViewController.h add this

-(void)setupBackButton;
-(void)goBack;

In BaseUIViewController.m add this for the implementation of the method

-(void)setupBackButton{
    if(self.navigationController && 
        [self.navigationController.childViewControllers count]>1){
        UIBarButtonItem *back = 
        [self barButtonWithImage:[UIImage imageNamed:@"Back"]
         target:self action:@selector(goBack)];
        self.navigationItem.leftBarButtonItems = 
        [NSArray arrayWithObjects:back, nil];
}
-(void)goBack{
    [self.navigationController popViewControllerAnimated:YES];
}

Now we will make all our UIViewControllers inherit from our BaseUIViewController.

In any of our UIViewControllers viewDidLoad method we can call the setupBackButton and that will put our back button in the nav bar.

-(void)viewDidLoad{
    [super viewDidLoad];
    [self setupBackButton];
    //do other stuff...
}

This will setup the back button and allows you to navigate back in the UINavigationController views.

Hope this helps.

Thanks for reading

Tuesday, January 15, 2013

Setting up Laravel-4 on Webfaction

Setting up laravel 4 on [**Webfaction**](http://webfaction.com?affiliate=jeff01)

Setting up laravel 4 on Webfaction

  1. Create your domain or subdomain. E.g laraveltest.hostname.com

    Check here on how to create add domains or subdomains on webfaction.

  2. Create your application. Make sure you select Static/CGI/PHP and PHP 5.4 runtime

    Check here on how to create add applications on webfaction

  3. Create your website and add domain and application you've created. E.g laraveltest

    Check webfaction on how to create website on webfaction

Test in browser

http://laraveltest.hostname.com

and see you have Hello World displayed.

Setting up laravel 4

Installing composer

To install composer to your home directory

$cd ~

Run this using php54 runtime

$php54 -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"

This will install composer to your home directory so you can run it like composer.phar

Installing latest laravel-4 version

As of this writing laravel 4 is still in better. To get the lastes go to http://four.laravel.com and get the latest version.

I got my version from https://github.com/laravel/laravel/archive/develop.zip

$ cd ~

$ wget https://github.com/laravel/laravel/archive/develop.zip

$ unzip develop

This will unzip the content of develop to laravel-develop directory in the home directory

Ready to install laravel dependencies

$ cd ~

$ cd laravel-develop

$ php54 ~/composer.phar install

This will install all laravel dependencies into the vendor directory in the laravel-develop directory

Create a directory in your home folder that will take all laravel apps

$ cd ~

$ mkdir laravelapps

$ cd laravelapps

$ mkdir laraveltest

Now copy the content of ~/laravel-develop/public directory into ~/webapps/laraveltest.

You should have these files inside the directory ~/webapps/laraveltest

index.php
.htaccess
favicon.ico

Now copy all files and folders inside ~/laravel-develop except vendor and public to ~/laravelapps/laraveltest/


We are almost there!


Editing index.php to point to the the right files

$ cd ~/webapps/laraveltest

$ nano index.php

Change this line

require __DIR__.'/../vendor/autoload.php';

To point to the new path:

require '/home/<<your-webfactio-username>>/laravel-develop/vendor/autoload.php';

Or

require '~/laravel-develop/vendor/autoload.php';

Then change this line

require_once __DIR__.'/../start.php';

To

require_once '/home/<<your-webfactio-username>>/laravelapps/laraveltest/start.php';

Update:

In order to use artisan commands do the same changes you made above to index.php in this file: ~/laravelapps/laraveltest/artisan


Done!!

Now check

http://laraveltest.hostname.com

You should see Hello World from laravel default '/' route.

Happy coding!!

Cheers!! @badu_bizzle

Special thanks to Taylor Otwell for creating the awesome PHP framework

Saturday, August 18, 2012

Erlang Application with rebar

The basho guys did a great job for the erlang community for creating rebar which makes creating erlang/OTP applications easier. Rebar allows you to create apps, generate releases and upgrades all based on OTP standards.

1. Make sure you have erlang installed. Check out erlang website for instructions

2. Download rebar. I'd recommend the modified version by OJ available here

3. Extract the content to a folder. cd into that folder and run sudo ./bootstrap

4. You should get a build of rebar available in that folder. For easy access, copy the rebar file into your path, basically /usr/bin folder. Now we are ready to create our erlang app

5. Create a folder in your home directory e.g mkdir ~/sampleapp

6. Navigate into that folder cd ~/sampleapp

7. Run sudo rebar create-app appid=sampleapp. This will create the skeleton of our erlang app.

You can run make commands to compile and generate releases for your erlang app and don't forget to edit rebar.config to include dependencies.
You can read more on rebar and the available commands on the internet.

Cheers.

Friday, August 17, 2012

Twisting Erlang with Python

Lately, I have been thinking of developing a server partly in Erlang and Python (using twisted).
Erlang shines at managing processes whiles python is very good at IO stuff compared to Erlang.

This may be interesting. I will post about how it goes.

Wednesday, August 15, 2012

Amazon Load Balancer Setup for Websocket

Open your load balance settings on amazon and set port configuration to TCP forwarding

That should work.

Note that you can't get access to the client's IP address since the load balancer doesn't forward that to your websocket server.

Hope this helps