Posterous theme by Cory Watilo

Finding your location on an iPhone

Here's a simple wrapper for CoreLocation to make it easier to find out your rough (~3km accuracy) location in Objective-C on the iPhone. It wraps the delegates up for you so you only need to deal with blocks, which as we all know, are simply awesome.

To use, include the CoreLocation framework, include the two files in these github gists, and use as below:

    [LocationHelper getRoughLocation:^(CLLocation *location) {
        
        if (location) {
            // Found the location! Use it!
        } else {
            NSLog(@"Couldn't find location for some reason");
        }
        
    }];

Here's the gists:

A generous career

You should live generously. It’s important – we’ve all got something different to give to the world, and if you hide it away, never giving, you’ll shrivel. Whereas if you live generously, you’ll live a big life.

That desire that we all have to be significant, important, and successful – if you twist it into ugly ‘me-first’ ambition, you’ve got it backwards. You’re actually meant to be giving of yourself, and in doing so you will become the ‘bigshot’ you deeply desire to be.

Now – I understand this all sounds like airy-fairy philosophical nonsense, right? Like the kind of thing yoda would say. So let me spell it out to you how it works. I just had the realisation the other night.

Go to local meetups in your area of expertise. Or if there is none, start one. I know it’ll take a lot of time and energy, but you can be generous with your time and energy, i’m sure. When you’re there – don’t try to talk to just your friends, or the ‘important’ people there. Say hi to all the new people! Be welcoming and generous with your words.

If you’re shy, and it’s a struggle to be friendly and welcoming to people you’ve never met – make the sacrifice to be friendly. It’s a generous move, and it’s worth doing. The ‘bounce rate’ at meetups in my experience is massive, roughly a third of the people there have never been before, and they probably won’t come again if nobody says hi to them. So be the person who says hi to them.

Just make sure you’re genuine. Be friendly for generosity’s sake, not just because you’re trying to make a name for yourself. Here’s the difference: if you’re being genuinely friendly, you’ll ask about what they’re working on. If you’re trying to make a name for yourself, you’ll never stop talking about what you’re working on – and people will be repulsed.

Give presentations at meetups! This is a huge thing. You’ve got something to share, i’m sure, and people need to hear it. Meetup organisers in general have a hard time finding people to present. And rightfully so: it takes a lot of time and effort to prepare, and a lot of guts to stand up in front of everyone – but do it anyway, be generous and make the effort.

And here’s how it all comes together: In being generous like the above, you’ll feel deeply satisfied in yourself. You’ll make a stack of new friends. You’ll build the community. You’ll become the person that everyone knows and comes to for advice. You’ll be highly regarded because of your contributions and presentations. Opportunities will come your way, inevitably. It’s easy to see how your generosity will come back to you, multiplied, eventually. So there it is – that’s the nuts and bolts of how being generous actually will make you the success you want to be.

But please, don’t be generous only because of what you can get out of it. Do it because (even though you may not realise) it’s who you really are meant to be.

Skeleton Key Cocoaheads presentation

Here’s the contents of the presentation i gave last night at cocoaheads sydney, for anyone who’s interested. Exclamation marks are slide separators, to suit my presentation app, Impromptu (impromptuapp.com).

Also the source can be found here: https://github.com/chrishulbert/CHBgDropboxSync

Skeleton Key
====

* Dropbox
* Cryptography
* Marketing

!

Dropbox
=======

* Background sync
* Fully automatic
* Suppresses errors
* One folder, yaml files

!

Sync strategy
----

* Compare folders
* Both, same: nothing
* Both, different date: newer wins
* Different: depends on last sync state

!

Different
-------

If missing locally:

Present at last sync = it's been deleted locally, so delete remotely

Missing at last sync = it's been added remotely, so download.

!

Different 2
---

If missing remotely:

Present at last sync = it's been deleted remotely, so delete locally

Missing at last sync = it's been added locally, so upload.

!

Edge cases
----

Since the last sync data is only ever used to justify a
deletion, safe to clear it any time to avoid edge cases.

Eg clearing dropbox, linking, unlinking.

!

Cryptography
============

* Master password is validated using bCrypt
* All passwords are encrypted by AES
* The AES key is derived from the master pass using PBKDF2

!

bCrypt
------

Only used to verify the master pass, nothing else. One way
slow hash designed to not be GPU-able.

Uses JFBCrypt:
http://www.jayfuerstenberg.com/blog/bcrypt-in-objective-c

5 rounds takes ~0.1s on iPhone 4

!

Storing a password
------

    salt = [JFBCrypt generateSaltWithNumberOfRounds:5];
    hash = [JFBCrypt hashPassword:clearPassword withSalt:salt];

Store both the above strings

!

Validating
----------

    test = [JFBCrypt hashPassword:clearPassword
        withSalt:salt];
    valid = [test isEqualToString:hash];

!

PBKDF2
------

An industry-standard (eg OpenSSL) way to derive a key
from a passphrase/word.

Using Security framework, and CommonCrypto/CommonKeyDerivation.h

We're making a 32byte / 256bit key.

!

Calibrating
-------

So that we know how many rounds to make it take ~0.1s:

    int rounds = CCCalibratePBKDF(kCCPBKDF2,
       clearPassword.length, keySalt.length,
       kCCPRFHmacAlgSHA256, 32, 100);

!

Salt
----

    - (NSData*)generateSalt256 {
        unsigned char salt[32];
        for (int i=0; i<32; i++) {
            salt[i] = (unsigned char)arc4random();
        }
        return [NSData dataWithBytes:salt length:32];
    }

!

Deriving
-----

    NSData* myPassData = [clearPassword
        dataUsingEncoding:NSUTF8StringEncoding];

    unsigned char rawKey[32];
    CCKeyDerivationPBKDF(kCCPBKDF2, myPassData.bytes,
        myPassData.length, keySalt.bytes, keySalt.length,
        kCCPRFHmacAlgSHA256, rounds, rawKey, 32);
    return [NSMutableData dataWithBytes:rawKey length:32];

!

AES
---

Also using Apple's Security framework's CommonCrypto

Using NSData+CommonCrypto helpers:
https://github.com/AlanQuatermain/aqtoolkit

!

Encrypt
---

    - (NSString*)encrypt:(NSString*)clear {    
        NSData* clearData = [clear
            dataUsingEncoding:NSUTF8StringEncoding];

        NSData* crypto = [clearData
            AES256EncryptedDataUsingKey:key error:nil];

        return [crypto base64EncodedString];
    }

!

Decrypt
----

    - (NSString*)decrypt:(NSString*)crypto {
        NSData* cryptoData = [NSData
            dataFromBase64String:crypto];

        NSData* clearData = [cryptoData
            decryptedAES256DataUsingKey:key
            error:nil];

        return [[NSString alloc] initWithData:clearData
            encoding:NSUTF8StringEncoding]; 
    }

!

Marketing
----

* Nobody responds to requests for reviews.
* Ads driving people to your promo-site costs more than income.
* Promo site with videos, screenshots, and features.
* Sells best when free!

!

So I got some advice..
---

Patio11 (HN)

!

Next time
----

* Find popular forum
* Working with them, make app
* Make placeholder page, collect emails, have shareables
* Determine if worth proceeding
* Attract via forums and ads
* Emails with updates
* Done
* Post launch, still interact, and email updates
* Emails link to forum for discussion

!

Please visit
-----

App: SkeletonKeyApp.com

Presentation: splinter.com.au

CHBgDropboxSync - Dropbox auto-sync for your iOS apps

Just open sourced a new project, you can grab the code and read the full docs here: https://github.com/chrishulbert/CHBgDropboxSync

CHBgDropboxSync

Obj-C / iPhone / iOS library for background syncing the contents of a folder to Dropbox, just like the ‘magical’ way that the desktop version automatically keeps everything in sync, by Chris Hulbert – chris.hulbert@gmail.com

CHBgDropboxSync

Performs a 2-way sync process designed to either download or upload changes as necessary. Uses the NSUserDefaults to keep track of local files to help it decide whether to delete or transfer if a file is present one one side but not on the other.

To use this, add the Dropbox API to your app as you normally would, creating an interface to link (and unlink) as normal. Use this class to sync a local folder (typically your Documents folder in an iOS app) to your app’s folder on Dropbox in the background without user intervention.

Github

Again, all the code’s on github: https://github.com/chrishulbert/CHBgDropboxSync

That book about that Steve Jobs guy

In the spirit of my book club which has dwindled in membership down to... well, just me, here's a book review of what I just read: The Steve Jobs Biography.

I just finished reading it (takes quite a while!) and the feeling i can't escape is that the author squandered his access to Jobs. The world has just lost one of its visionary leaders, and the book basically documents his home life, his youth, his sickness, and what he did, but never really answered my core question: how and why? What was his 'secret sauce'? I'm really dying to know - how, in a world of mediocre products (cough google cough) and passionless companies (cough microsoft cough), did he manage to build Apple?

He seemed to embody obsession to detail, passion to make things great, and managed to scale that out to his team, but how? The book only goes into all his outbursts. We all know that he was a prick, sure. But it labors the point. And misses the thing i really want to know - which is: how did he transfer that 'secret sauce', whatever it was, to his team? Because i'm dying to know - i want to do the same one day. And i'm sure that his fits and rants weren't what did it. But, after reading the book, that's all i learnt - he was a grump. Well, so's the chair-throwing Ballmer, and it hasn't exactly produced anything special.

I just really want to know - how did Jobs *do* it? What was his secret sauce? Maybe jony should write a book that explains. I'd buy it.

Another app marketing idea

One more idea I gleaned from tim ferris' blog, I really should keep track of these:

* Make a freemium app, with an IAP to upgrade
* Freemium means it's easy to get people 'in the door' so to speak
* Once they're 'in the door', pop up a nag alert every now and again (eg every 5th launch) to suggest why they should upgrade to the full version
* Each nag alert, recommend a different benefit of the full version, so gradually it'll convince them to purchase.

Sounds reasonable to me. It all seems to be about having a 'smooth funnel' - eg on your app's promo site, don't try to convince them to buy it, make it a smaller step: ask for their email instead. Make the app free, so it's a small step from finding it to installing it. And once they're in the app, gradually convince them into purchasing the full version with 'nag' alerts.

Of course, nag politely, don't put people off ;)

Sweet grouped tables on the iPhone

Sweet grouped tables on the iPhone

I’m a big fan of using grouped table views whenever I need to display a bit of simple information in an app. I’ve used this in at least one of my apps off the top of my head, plus another that’s in the works. You can have a look at the screenshots attached to this post to see what i’m talking about. If you like the style and want to reproduce it in one of your apps, here’s how it’s done:

Concept

Here’s the gist of it: a textured background, with black ‘embossed’ group headers, dark transparent cells with black borders and white text. There’s probably cooler designs out there, but for a designed-by-the-programmer app it looks just fine :)

(download)

Helper

I’ve got a ‘helpers’ class that I use for styling these grouped table views (and other stuff too), here’s the code you’ll need from it:

Using it

In your viewDidLoad, apply the style to the table view:

[Helpers styleTableView:vl.tableView];

The above removes the etched borders from the cells, makes the cell borders solid black, and sets the textured background using ‘backgroundView’ - it took me ages to realise that I had to use that instead of backgroundColor, so take note.

You’ll need to override the heightForHeaderInSection function:

-(CGFloat)tableView:(UITableView *)tableView

    heightForHeaderInSection:(NSInteger)section {

    return [Helpers styledTableHeaderHeight];

}

Before the cells are displayed, you must style them to override the default colours:

- (void)tableView:(UITableView *)tableView

    willDisplayCell:(UITableViewCell *)cell

    forRowAtIndexPath:(NSIndexPath *)indexPath {

    [Helpers styleWillDisplayCell:cell];

}

To set the group headings, you must use the viewForHeaderInSection instead of titleForHeaderInSection, and use the helper to position a label inside a view:

-(UIView *)tableView:(UITableView *)tableView

    viewForHeaderInSection:(NSInteger)section {

    if (section==0)

        return [Helpers styledTableHeader:@"Details"];

    if (section==1)

        return [Helpers styledTableHeader:@"Content"];

    if (section==2)

        return [Helpers styledTableHeader:@"Tweet"];

    return nil;

}

And that’s it - you’ll have a nice, simple styled grouped table view that’ll hopefully make your app look a little less plain vanilla.

 

Another app marketing idea...

One more idea to add to the mix:

* Before even *thinking* about starting development, make a landing page to collect email addresses for people who'd be interested
* Send people to these landing pages via google/facebook/forum threads
* Make a few of these landing pages
* The landing page that gets the best conversion rate (visitors vs number of emails collected) is the app that gets built!

Quickly check for any missing retina graphics in your project

Quick bit of code to throw in your app delegate as a test for any missing retina assets. Useful when checking if you’re all set for the retina iPad:

// Figure out which images don't have an @2x
NSArray* files = [[NSBundle mainBundle]
    pathsForResourcesOfType:@"png" inDirectory:nil];
for (NSString* file in files) {
    if ([file rangeOfString:@"@2x"].length==0) {
        NSString* retina = [file
            stringByReplacingOccurrencesOfString:@".png"
            withString:@"@2x.png"];
        if (![files containsObject:retina]) {
            NSLog(@"Missing retina: %@", [file lastPathComponent]);
        }
    }
}