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.
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.auJust open sourced a new project, you can grab the code and read the full docs here: https://github.com/chrishulbert/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
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.
Again, all the code’s on github: https://github.com/chrishulbert/CHBgDropboxSync
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 :)
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.
Just finished the website to promote my latest app, Skeleton Key Password Manager with Dropbox!
Busy uploading a couple of videos at the moment, and then I’ll try hit up some of the popular app review blogs and see how my luck holds up.
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]);
}
}
}