Chris's Blog http://splinter.com.au Most recent posts at Chris's Blog posterous.com Mon, 20 Feb 2012 22:00:00 -0800 Using Launchd to run a script every 5 mins on a Mac http://splinter.com.au/using-launchd-to-run-a-script-every-5-mins-on http://splinter.com.au/using-launchd-to-run-a-script-every-5-mins-on

Say you’ve got a script somewhere that you want to run every 5 minutes on OSX. Something like this:

#!/bin/bash
date >> /Users/Chris/Dashboard/dates.txt

Or perhaps your script is a bit more imaginative. Whatever. Make sure that it’s marked as executable:

chmod a+x mytask.sh

Now we need to make a plist to instruct launchd what to do with it. First, come up with a reverse-domain-style name for your task. I recommend something like com.mycompanyname.mydepartment.mytaskname. You’ll then name this file com.mycompanyname.mydepartment.mytaskname.plist. Here’s what the file needs to look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.mycompanyname.mydepartment.mytaskname</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/myuser/Dashboard/mytask.sh</string>
    </array>
    <key>StartInterval</key>
    <integer>300</integer>
</dict>
</plist>

Note that you’ll want to change the task reverse-domain name and the path to your task in the above file. Also the interval, if you want it to run other than every 5 mins.

Next we need to install the task with launchd. First, copy it into your LaunchDaemons folder (or LaunchAgents, if you want it to only run when you’re logged in):

cp com.mycompanyname.mydepartment.mytaskname.plist /Library/LaunchDaemons

Finally, so that launchd will pick it up without needing a reboot, we do the following:

launchctl load -w /Library/LaunchDaemons/com.mycompanyname.mydepartment.mytaskname.plist

To check it’s all installed, do launchctl list and check that your task is in the list.

Disabling

When the time comes that you need to disable the task, do the following:

launchctl unload -w /Library/LaunchDaemons/com.mycompanyname.mydepartment.mytaskname.plist
rm /Library/LaunchDaemons/com.mycompanyname.mydepartment.mytaskname.plist

Further reading

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Mon, 20 Feb 2012 20:51:22 -0800 Generating AES256 keys from a password/passphrase in ObjC http://splinter.com.au/generating-aes256-keys-from-a-passwordpassphr http://splinter.com.au/generating-aes256-keys-from-a-passwordpassphr

From an upcoming app that needs to encrypt your data using a passphrase, using industry standard methods:

#import <CommonCrypto/CommonKeyDerivation.h>

...

// Makes a random 256-bit 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];
}

...

// Make keys!
NSString* myPass = @"MyPassword1234";
NSData* myPassData = [myPass dataUsingEncoding:NSUTF8StringEncoding];
NSData* salt = [self generateSalt256];

// How many rounds to use so that it takes 0.1s ?
int rounds = CCCalibratePBKDF(kCCPBKDF2, myPassData.length,
    salt.length, kCCPRFHmacAlgSHA256, 32, 100);

// Open CommonKeyDerivation.h for help
unsigned char key[32];
CCKeyDerivationPBKDF(kCCPBKDF2, myPassData.bytes, myPassData.length,
    salt.bytes, salt.length, kCCPRFHmacAlgSHA256, rounds, key, 32);
NSData* keyData = [NSData dataWithBytes:key length:32];

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Sun, 19 Feb 2012 02:26:00 -0800 Indie iPhone app marketing, part 2 http://splinter.com.au/indie-iphone-app-marketing-part-2 http://splinter.com.au/indie-iphone-app-marketing-part-2

This post is a follow-on from part 1 of my series on app marketing. If you’ve read that, you’ll know I tried to create a fancy-looking landing page which would hopefully funnel people to the app store, and use Facebook ads to send people to my landing page. Long story short, it was a failure.

Anyway, my next plan is to do a bit of cross-marketing. I’ve been in the habit of including a ‘more apps by this developer’ button or tab in my apps for quite a while, which has linked to a website with a rather plain-looking list of my 11 apps. So, on a whim, I decided to plug some Google Analytics lovin' into the html, and lo and behold: I get ~300 uniques daily! Which is a LOT more than i was able to pull in with Facebook ads on a reasonable budget…

So: my plan this time is to soup up that site a bit, make it pretty, make it promote one particular app, and make all my other apps free for a week so that lots of people are downloading and hopefully tapping on ‘more apps’, and see how my sales go for my particular promoted app.

To make this work, i used the same landing page from part 1, and tweaked to look good at 640x700ish pixels. Also fiddled the html so that on an iPhone, it will fit neatly on a retina screen (320point / 640 retina pixels wide). Thus it should look neat for the user, and hopefully mean sales.

In a week’s time, i’ll post again with iTunes sales results and analytics hit counts, to let you know how it goes. I’m really hoping that it’ll work. I mean, if i’ve already got someone’s attention enough that they’ve downloaded one of my other free apps (and remember, attention is the most valuable thing on the internet attention economy), then chances are that they’ll be more willing to try out one of my other apps than would a random Facebook user who clicked on one of my lame ads.

So: It’s currently Sun 19th Feb (2:30am US iTunes time). I’ve already made the new ‘more apps’ page live. I’m about to make all my other apps free. Stay tuned!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Wed, 15 Feb 2012 15:30:56 -0800 My App Manifesto: Syncing + Dropbox + YAML = Awesome http://splinter.com.au/my-app-manifesto-syncing-dropbox-yaml-awesome http://splinter.com.au/my-app-manifesto-syncing-dropbox-yaml-awesome

I couldn’t decide what to title this post, so i’m giving you choices:

  • Apps that sync
  • Apps, Dropbox, YAMLKit, and power users
  • Dropbox + YAML + Syncing apps = Happy power users
  • Please use Dropbox + YAML and make your App sync
  • My Syncing Apps Manifesto: Dropbox + YAML = Awesome

Anyway, here’s my point: Too many iPhone apps have reviews with a common complaint: It’d be great, if only it would sync.

You see, there are a lot of people these days who have more than one iDevice. Here’s a common situation: Jim has an iPad which he uses at home, an iPhone which he uses on the train to work, and a Mac/PC at work. He’d love to use the same app on all of those, with his data synced elegantly.

Dropbox vs iCloud

Day one gets it right with their diary app: everything is synced to iCloud or Dropbox, there are different versions of the app for iPhone, iPad, and the Mac, and they all work to the strengths of each platform. Eg you can type on the Mac/iPhone, and read on the iPhone, or even take short notes on the iPhone. And using Dropbox is great – I can see the files on Dropbox, they’re not hidden away from me, i feel like i’m in control. I can back them up as I see fit, I can read them (kind-of), or really do anything I feel like. Plus, as of the v1.0+ Dropbox api, you get access to only tidy folder inside Dropbox/Apps to put the files for your app, which will put your users at ease. One last opinion: I think Dropbox is the best ‘cloud-y’ way to bridge the data divide between desktop (explorable file system) and mobile (no visible file system).

iCloud is a bit less awesome (to me) in this respect: the files are tucked away ‘in the cloud’ where you can’t see or touch them easily outside the app. I guess this is ideal for a consumer grade app, but for a power user this is a bit disconcerting. We all like to feel like we’re in control, which this approach (for me, at least) lacks. Plus it locks you to the Apple ecosystem AFAIK, and what if you wanted to make a PC version of your app?

My ideal syncing app

Here’s my ideal solution: the files to your app are all tucked away in a certain folder (eg: /Apps/MyApp) in Dropbox, and all the apps do a 2-way automatic sync between this and their local storage at startup/shutdown/change events. These files are all in some human readable format: YAML, JSON, or (shudder) XML. I personally prefer YAML because it is very human-readable for powerusers who like to open these files in a text editor. Also, because it is full of awesome (IMHO). However, i’ll understand if you prefer JSON or Plists or XML – that’s just my preference. In case you haven’t seen JSON, here’s how it looks:

id: 201327 name: Some Name hue: 300 icon: 248-sign some_array: – First – Second – Last

I’ve tried to follow this approach with my Service History app, and will use it in all my future apps where this kind of data sharing is appropriate. I also open-sourced the 2-way-syncing code, which is fairly simple and easy to integrate into your app, please check it out: CHDropboxSync.

One pitfall i’ve had with this solution was a user who contacted me, saying that when syncing one time the app deleted their data off their iPhone, rather than pushing it up to Dropbox. Now i can’t say for sure what happened in this situation, it’s impossible to know, but I think a nice solution would be that whenever the sync detects a remote delete, it should move the local file in a ‘Trash’ folder, and provide a UI for the user to restore files on a one-by-one basis if they want, just in case some bugs slipped through the radar. I’m planning on doing this with CHDropboxSync one day. Maybe for my current app-in-progress (a password syncer).

YAML

I’m a bit fan of YAML. I think it’s the only serialisation format that is optimised for readability. JSON is pretty close, but let’s be real here: it’s only readable to us devs, your average Joe is going to run away scared. YAML is so good, i’ll bet you could use it to serialise your monthly sales data, print it out verbatim, and give it to your CEO and he’d happily read it. Sometimes we devs need to get out of the ivory tower, I think. Use YAML, it’s friendly, people will love it.

One fair criticism is that it’s a bit difficult to find a good YAML library for ObjC. I recommend YAMLKit + libYaml. I tried YAML.framework, but it serialised in UTF16 which ruled it out for me. YAMLKit is a winner – it’s as simple as follows:

Read a yaml file into an NSDictionary:

NSDictionary *d = [YAMLKit loadFromFile:@“/Some/Path/To/My/File.yaml”];

Write an NSDictionary to disk in yaml format: [YAMLKit dumpObject:myDictionary toFile:@“/Some/Path/To/My/File.yaml”];

I’d really love a free YAML reader for mac to magically appear and make it’s way onto the app store, with a file association to the YAML file type. Imagine the feeling of ‘Oh sweet, I’m peeking behind the curtain, I can see my data, it’s readable and accessible’ when you user is poking through their Dropbox, finds the files to your app, double clicks one and it opens in textedit or a nice YAML visualisation tool. It’d be so empowering. I know I’d love it.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Tue, 07 Feb 2012 02:16:00 -0800 Indie iPhone App Marketing part 1 http://splinter.com.au/indie-iphone-app-marketing-part-1 http://splinter.com.au/indie-iphone-app-marketing-part-1

Hi – I’m Chris. I do a bit of indie iPhone app development in my spare time. So… i’ve got 10 apps on the app store now – impressive, right? Well, not so much: sales are dismal! In the range of less than 10 per day.

Update: Marketing part 2 is here.

So here we are: I’ve written as much Obj-C as a man could possibly handle – it’s time to try something else. Marketing! I’m going to write a series of blog posts as a ‘tell-all’ as I try a few things to promote a few of my apps. Hopefully the results will give people in a similar situation a hint of what to try, and maybe (probably?) what not to try.

Experiment 1: Landing page + Facebook ads

My first experiment is this: make a website to promote my app, and use Facebook ads to drive people to my site for a week. So here’s what I did:

Designing the site

I planned on making a single-page website which links straight to the App Store. After a bit of reading on hacker news, I came to the conclusion that my landing page needed to include the following:

  • A nice design
  • Social proof (eg reviews)
  • A 30second video
  • Screenshots
  • A nice big call to action
  • Key features under the fold
  • Trustmarks

After looking at lots of apps' websites, I found a couple of designs I liked, and proceeded to imitate my favourite elements from each one until the following site was born: www.servicehistoryapp.com. If you want to use my html/css as a basis to make your own site, you’re welcome! A link back would be appreciated, is all.

Some details: I’m hosting the site through S3’s website hosting, so that my costs are nothing if nobody visits. All I had to pay is $9 for the domain. I’ve put google analytics on the site, so i can track visitors / traffic sources etc.

I skipped making a video for the site – this seemed like too much like hard work. Maybe i’ll do a video later, if the promotion shows promise. Also, I couldn’t for the life of me figure out what ‘trustmarks’ are, so skipped those also. And since the app had no reviews of its own, I put some reviews from other of my apps (labelled as such, of course), to give people a feel for the quality of my work.

Facebook ads

What can I say: Facebook ads are awesome. It took me about 5 minutes to set up ads on Facebook, and you can target them by age group, sex, and even what interests people have. It puts google ads to shame, in my opinion.

So, I created an ad like the following: Male only, age 18-35, for people who are interested in ‘cars’ or ‘motorcycles’. It is very targeted, and i’ve got high hopes for a good click through rate.

I set the budget to $4 a day, and since i’m going for a week’s trial, i’ve set the spend limit to $30. The only tricky part of this was setting the spend limit: To do this, go into the ‘manage ads’ part of facebook, click on ‘Billing’ in the left, and click the ‘edit’ button next to ‘Account Spend’.

Results

First day (26th Jan)

  • Ad impressions: 407
  • Click-through-rate: 0
  • Spend: $0
  • Site traffic: 8 visitors
  • Sales: 1
  • Conversion rate: 0% :(

I looked at the facebook campaign and noted that I wasn’t spending anywhere near the $4/day that i had budgeted. Oops! So I opted to open up the ads to a wider audience, by removing the age group and male-only filters. Hopefully this’ll make a difference tomorrow.

Second day (27th Jan)

  • Money spent: $2.31
  • Impressions: 17851 (wow, facebook is cheap)
  • Ad clicks: 4
  • Click rate: .022% (maybe i need to re-word my ad, seems very low?)
  • Site traffic: 6 visitors (awful)
  • Average time on site: 2m29s (awesome!)
  • Sales: 1

I’m blown away that people are spending average 2 and a half minutes reading my sales drivel on the servicehistoryapp.com site. Lets hope that means i can get a good conversion rate (once i can figure out how to get people to visit).

It also looks like I should re-write the ad copy so that i get more people’s interest. Only 1 click for ~5000 impressions seems pretty lousy to me. So i’ve rewritten it to be the hopefully-catchier: “Are you OCD about your car’s services? Keep organised with this iPhone app, with photos and notes of all your services.”

Third day (28th Jan)

  • Impressions: 1157 (why has this dropped?
  • Ad clicks: 0
  • Click rate: 0
  • Site traffic: 0
  • Sales: 1

I changed my copy yesterday, and it looks like the ad didn’t run this day whilst it was in review.

Another thing I noticed with today’s results is that i’m getting ~250 visitors to my apps.splinter.com.au site. This is the site that appears when people tap the ‘more apps’ tab in any of my apps. It’s a cross-promotional tool that i’ve largely forgotten about, and only in the last couple days have I bothered to install a google analytics tracking code on it. This is quite a good result, and leads me to believe that i probably should put more effort into sprucing that site up.

Day 4 (29th Jan)

  • Money spent: $4.00
  • Impressions: 12870
  • Ad clicks: 7
  • Click rate: .054% (looks like the re-wording doubled this!)
  • Site traffic: 8 visitors
  • Sales: 2

Well, the new copy certainly worked. My click-through-rate has gone from roughly 0.02% to 0.05%. So, for every thousand times the ad shows up on a page, twice as many people are clicking on it. Copywriting for the win! So, as a net result of this, I got 7 clicks today. But only 2 sales – and it’s impossible to know if these sales came through my site or via people browsing the app store.

Day 5 (30th Jan)

  • Money spent: $4.00
  • Impressions: 25304
  • Ad clicks: 7
  • Click rate: .028% (went right back down again, oddly)
  • Site traffic: 6 visitors
  • Sales: 0

Somehow, today the ad showed twice as much, but people only clicked half as frequently. No idea why. Maybe because it was a monday? Nobody clicks ads when they’re grumpy at work?

Day 6 (31st Jan)

  • Money spent: $2.00
  • Impressions: 29432
  • Ad clicks: 3
  • Click rate: .010% (halved again!)
  • Site traffic: 3
  • Sales: 1

At this point, my only guess is that everyone in my target market who would be interested in clicking my ad, has clicked the ad already. That’s my best guess as to why my CTR has halved for two days in a row.

Day 7 (1st Feb)

  • Money spent: $0.00
  • Impressions: 3328
  • Ad clicks: 0
  • Click rate: 0
  • Site traffic: 2
  • Sales: 0

I’m guessing that since my click rate has dropped right off that facebook isn’t bothering to show my ads any more. If i was continuing this experiment, i’d thus delete this ad and re-word another one. But since I only wanted to run this for a week, this seems like a funny way to finish: my ads are doing so badly, that facebook isn’t even showing them.

I made a small change to the landing page today: I changed the background photo to a nicer photo I found earlier, but had been awaiting permission to use it from the photographer. Thanks, Chris Farmlett!

Summary for the week

  • Impressions: 90k
  • Clicks: 21
  • Click rate (CTR): 0.023%
  • Facebook spend: $12
  • Cost to display the ad to 1000 people (CPM): $0.14
  • Cost per click: $0.59
  • Sales: 6

If everyone who clicked on the ad bought a copy of the app, i’d be making 4 cents each time ($.59 to get a customer, $.63 revenue per sale). But I know that’d be more like 1-10%. Well, looks like this was a total failure. At least i’ve got a sweet looking landing page which’ll be useful for other marketing efforts.

Timezone

Just a quick note about time zones: The facebook and adsense reports are in the sydney timezone, however the app store sales reports are odd: sales reports for any given date consist of american sales made between 12am-11:59pm LA time, and australian sales made between 12am-11:59 perth time. So, given that I anticipate most sales to be in america, I’ll go with: e.g. the 31st in the australian timezone will be the 30th in the US timezone.

Experiment 2: Next steps

So where to from here? In short: cross-marketing.

Since i’ve got ~250 visitors on my ‘other apps by me’ site, i think i’ll spruce it up and use it to promote my a different app each week, and see how i go. I also plan on setting a different one of my apps to be free each week, as all those ‘apps that are now free’ sites and apps will promote my apps for me. Hope you found this interesting.

Update: Marketing part 2 is here.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Thu, 02 Feb 2012 15:24:56 -0800 Perspectives http://splinter.com.au/perspectives http://splinter.com.au/perspectives

Here’s an example of two very different perspectives on an identical situation:

Its raining – lousy weather, i’m stuck on an awful train, i’m going to be late to work because i slept in, i can’t think of anything to write because i made a goal to write a blog every day this week but just can’t keep it up, just want the day off.

Or:

Nice cool weather today, it’s good to be on a train but not sweating like a pig. I slept through my alarm clock – whoops – but I was around for when my daughter woke up, which was sweet. I’ve written 4/5 days this week so far – doing well!

Both are perspectives on the exact same situation. But if you let yourself think like in the first example, you’ll be such a downer, such a grump, that nobody will want to be around you – you’ll even hate your own company. So why not take the second perspective?

You’re going to have to choose one way or the other. You might as well choose to think positively. I know – it’s harder, it goes against the grain, it takes conscious effort, it may simply not be in your personality. But nothing good came of no effort. So make the effort.

Just don’t be fooled into thinking that your current way of thinking is the only option available to you. It is definitely possible to change your perspective. Once you know that you can change, well you’ll just have to live with the knowledge that if you’re not improving, it’s nobody’s fault but your own.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Wed, 01 Feb 2012 14:55:53 -0800 Accountability and Free Will http://splinter.com.au/accountability-and-free-will http://splinter.com.au/accountability-and-free-will

I had a thought the other day which i’d like to expand on (and it doesn’t matter either way, as basically nobody reads this blog!). It was: If you give someone a responsibility, but don’t give them the authority or power to fulfil that responsibility, you can’t really hold it against them when that responsibility inevitably goes unfulfilled. It’s a common principle in leadership and in business.

And it got me thinking about the derelict family up the street. The one that, at midnight once a week runs outside their house screaming at each other, waking everyone up in the street. Now my first instinct is to think ‘Trash! Grow up.’ – as would most people. But after considering my own struggles to deal with smaller issues, it’s hard to not feel hypocritical.

It’s as though we’re responsible for living well (eg: not screaming at your partner out on the street at 1am in my neighbours case, or being more fun to be around in my case), but aren’t given the authority/power to actually live well. And if not, well, who could blame you for being a shouty bogan?

Or is this just justifying our lack of effort at dealing with this stuff? Does ‘free will’ come into play somewhere? Do we actually already have the power to deal with this stuff: is our free will that power? I’m not so sure, because there seems to be an awful lot of people out there living crappy lives. And i’d like to know exactly: what is it that’s different about those random people you occasionally meet, those people who seem to have unlimited reserves of willpower to improve themselves, and seem to be doing really well? What’s their secret?

Or is this concept of responsibility and blame completely irrelevant anyway? If a criminal is who they are because they never were given the power to improve themselves (eg were born to a lousy family, grew up in a lousy school) – you have to forgive them, surely? Of course you’d have to lock them up anyway for the sake of public safety. But it wouldn’t be for punishment’s sake any more: it’d be simply for the purposes of rehabilitation (Ha! Like our prison systems rehabilitate anybody).

But this all strikes me as a little cold. There’s no room for the magic that is the human spirit in all this – and if life isn’t malleable with our free will, then it’s a pretty grim existence. We all love the stories of someone who pulled themselves by their bootstraps out of a crappy upbringing and sitation, the classic underdog tale, because I think it speaks to a deeper truth embedded in our psyche: we do have what it takes within us to win, if only we’d try hard enough.

I really would like to know how it works! I often see people who i’d love to help, love to see them pull themselves out of a hold, but i just can’t figure out how to do it. I can’t make my mind up at all about these issues. But i’m still leaning towards thinking: We can’t really blame people for failing to improve themselves, but we can certainly blame them for failing to try.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Tue, 31 Jan 2012 15:59:40 -0800 Badassery http://splinter.com.au/badassery http://splinter.com.au/badassery

There are two types of badasses in this world: idiots and heroes. We all know an idiot or two: the friend that, especially when drunk, is willing to do crazy things with nary a second thought to their safety. Their bravado is simple: fear is simply not part of the equation. But there’s a second type, too: the heroes. These are the average Joes whose life is punctuated by real danger and fear, but who fulfils their true duties despite the presence of fear.

Idiot badasses have no fear at all. It is as though they were born with a part of their brain missing. Or maybe they simply give no heed to the consequences of their actions, as though thinking further than 10 minutes into the future is beyond their capabilities. Or, most insidiously, a bigger fear overshadows every other fear: the fear of being exposed as a scared wuss. The idiot will chase cheap thrills (see: trainsurfing) without a trace of fear, but will never attempt the big challenges of life.

The idiot badass has a big disadvantage over the rest of us: with no fear, he has no compass that points towards what he should be truly doing. When you’ve got a set of goals in front of you, and you’re wondering what to do next, the easiest way to prioritise is to consider which one scares you the most. That is your internal compass pointing you towards which course of action is what you truly should be doing with your life. However, the idiot badass cannot rely upon this, because they have trashed their compass.

True badasses look like you and me. An average life, full of the normal big challenges we all face. These big challenges carry real danger: What if i can’t find a job? I’ll lose the house. These dangers gives rise to fear. This sprinkling of fear is what prevents most people from rising to the challenges they face, and thus they check out, and settle for a second rate existence. But a true badass sees these fears for what they are: hollow bullies that, once confronted, will crumple like a paper tiger. And thus the life of a true badass consists of moving from challenge to challenge, facing and feeling fear the whole time, but fighting forwards with the knowledge that ‘I have what it takes to beat this’.

It is a truly satisfying life, beating increasingly larger challenges and fears, free from the need to chase the cheap thrills that an idiot badass will do. Who needs to go train surfing to prove their bravado, when day in and day out you’re kicking ass when it comes to what is truly important? Personal demons, financial troubles, marriage woes, problems with the kids – all these are the challenges that you were put on this earth to beat. Your life is a hollywood blockbuster. These problems are the stereotypical eurotrash bad-guy, wreaking havoc on the people you love. You’re mission is clear – face your fears, overcome these challenges, do it scared, and kick his ass. Do that, and you’re a true badass in my book.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Mon, 30 Jan 2012 15:36:00 -0800 Sacrifice http://splinter.com.au/sacrifice http://splinter.com.au/sacrifice

I think I finally understand the meaning of sacrifice in leadership now. For many years, I assumed sacrifice was all about taking yourself down a peg. It’s as though being born in a first-world country was somehow an unearned privilege, and that you have to make a sacrifice in some way to avoid being a bourgeois pig.

Now that always struck me as a guilt trip, and I hate those. So I relegated the notion of ‘sacrifice’ to the dustbin, and went on with my mission to improve my lot in life. And after my umpteenth frustrating time trying to get work done on my side business whilst packed into a noisy sweaty train, I realised: I’m sacrificing.

It flipped my realisation: sacrifice isn’t a guilt trip, some way of paying back some of what you owe, as though your position of privilege has endeared you with some kind of karmic debt. No – it’s just the nuts and bolts of what is required if you are going to do something with your life.

I could be riding my motorbike to work this morning. The riders at work think i’m crazy not to. But instead, i’m on the train, working on my laptop, gradually making progress on iPhone apps that one day will increase my family’s income. I’m certainly not paying off a guilt debt, i’m working hard towards improving my situation. It’s the polar opposite of my original thought of what sacrifice was about, but i think it’s the more accurate of what sacrifice is about.

All good in theory, but how does this affect my life, though? Well, your perspective on sacrifice affects the type of sacrifice you’ll take. Let’s say you have some unsavoury personality trait that you aren’t particularly proud of. For me, i get too impatient and lose my cool. Well, if I believed that sacrifice was about ‘paying back’, every time i lose my cool I’d go off on a guilt trip as my sacrifice. However if I believe in sacrifice as ‘paying forwards’ towards a better tomorrow, instead of wasting time on a guilt trip I’d be engaging with the painful work necessary to deal with my issues – hopefully with the outcome that i will eventually beat that flaw of mine. Now think – which of those two approaches is more worthwhile? Hands down the latter, in my opinion. Guilt trips will get you nowhere.

Sacrifice isn’t about the past. It’s about the future. It’s the struggle of getting through the tough times necessary to build a better future.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Sun, 29 Jan 2012 15:43:51 -0800 Lead Yourself First http://splinter.com.au/lead-yourself-first http://splinter.com.au/lead-yourself-first

Don’t even bother trying to lead others if you haven’t mastered the art of leading yourself first. Seriously, you’re wasting your time.

It works like this: you’re at the doctor, and he tells you to stop smoking. Even recommends a program. But you can’t help but notice that he smells oddly like cigarettes himself, has telltale yellow teeth, and looks a lot older than the 40 years that you know he is. You think about the program he’s recommending. Well, it worked so miserably for him – as if you’d waste your time.

Or you’re watching some random TV host recommend their latest results-guaranteed exercise program. The same host whose weight has gone up and down like a yo-yo over the years. That’s a recommendation that’ll probably be best ignored.

Here’s the thing: nobody’s looking for wisdom. Find me 10 people who don’t know that the best way to look after your body is exercise + eating properly – good luck! No, we all already know what we should be doing. We just want an example to follow.

Nobody wants to be told what to do. Everyone wants to be shown what to do. Show, don’t tell.

I think this is because we want to see that it’s possible before we try anything ourselves. We need to believe that a certain course of action has paid off for someone else, and that they’re willing to hold our hands, to show us how to do it. Only then will a person follow your advice, because it worked for you, and you’re willing to show them how.

This is also why you’ll end up like your friends: Their examples show you what is possible, and how to get it.

If you can’t kick your own ass into gear, someone whom you are intimately familiar with, how do you expect to be able to kick anyone else’s?

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Thu, 26 Jan 2012 21:40:00 -0800 How to ping a server in Objective-C / iPhone http://splinter.com.au/how-to-ping-a-server-in-objective-c-iphone http://splinter.com.au/how-to-ping-a-server-in-objective-c-iphone

I recently had to make an iPhone app ping a server to check for it, and couldn’t find much helpful code out there. So I cobbled together a small helper which works with the SimplePing old code from apple’s site. Seems to work fine for me. Here’s how you’d use it:

- (void)tapPing {
    [SimplePingHelper ping:@"www.google.com"
        target:self sel:@selector(pingResult:)];
}

- (void)pingResult:(NSNumber*)success {
    if (success.boolValue) {
        [self log:@"SUCCESS"];
    } else {
        [self log:@"FAILURE"];
    }
}

Pretty simple to use – that’s all there is to it. The concept is that you use this as a very simple way of testing if networking will allow you to reach a server.

Keep in mind, that if you’re using this to poll a server to test for network connectivity, it’s probably worth using Reachability to check if they’re not connected to wifi, and if not then don’t ping too often for the sake of the poor old battery!

If it receives a response from the host, it calls pingResult immediately. If it doesn’t get a response, it times out after 1second and calls pingResult with failure.

It takes care of all the memory management for you, so you don’t have to worry about that. It retains target for 1 second, until it cleans up, so there’s no danger of it calling pingResult on a class that has been freed.

Source code can be found on github: https://github.com/chrishulbert/SimplePingHelper

To use it, grab the source and copy the 4 SimplePing* files into your project. Hope it helps someone!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Mon, 16 Jan 2012 14:54:00 -0800 iOS Automated Builds with Xcode4 http://splinter.com.au/ios-automated-builds-with-xcode4 http://splinter.com.au/ios-automated-builds-with-xcode4

iOS Automated Builds with Xcode4

I just spent the last (frustrating) week or more at work setting up our build server for some iPhone / iPad projects. It’s so complicated, and there’s so much misinformation out there, that it’s worth sharing some tips here which will hopefully be useful for us to read back on. And maybe this’ll save someone some hassles out there too.

In this article, i won’t give you a complete build script, as everyone’s environments are subtly different, but instead i hope to impart the knowledge and tips and pitfalls that’ll make it reasonably straightforwards for you to create your own script without all the horrible hassles I had.

Aims

Our final goal was to have two files arrive in our Dropbox each morning: zip files containing a dev branch build and a stage branch build. These zip files are to contain Xcode archives (for later app-store submission) and the ipa files (for TestFlight).

Our setup and requirements can be described thus:

  • A ruby script is used to control the build process.
  • Two Git branches: dev and stage. The idea is that all new features get added to the dev branch. Any bug fixes get added to the stage branch. Every two weeks (end of the sprint), with the testers' blessing, we merge all the new features from dev into stage, and then merge all the bug fixes from stage into dev.
  • We use TestFlight to provide the builds to the testers. For this, we need signed IPA files to submit to test flight.
  • We want to produce an Xcode archive (.xcarchive) – this is used for submission to the app store, and for sending to clients. With this file, we can double click it on any mac, which will import that exact build into Xcode, and we can then re-sign it with the App-store distribution profile and submit to the app store (so long as the Bundle Seed ID is the same – more on this later). This is so that we can ensure that what we submit to the app store is exactly the same build that was tested.
  • We’re using Xcode 4, and don’t need to support pre-iOS 4, so that simplifies things a lot (e.g. no need for CODE_SIGN_ENTITLEMENTS plists or Ad-hoc build configurations).

Provisioning

It’s worth going over provisioning: certificates, keys, profiles, etc. I’ll try to describe how it affects you, but you should really also read Technote 2250.

In short, it works like this:

Your private key signs a certificate, which validates provisioning profiles.

Keys and Certificates

Private keys are generated using the Keychain on your mac. Using this key, you request a certificate. So certificates only work if you have the matching private key installed – be aware of this, you cannot simply download the certificate from the iOS provisioning portal if you don’t have the private key. To copy a key to another computer, e.g. your build machine, right click > Export the key from within the Keychain, and make sure you export it as a .p12 file.

If, in the Keychain, you can’t see a little triangle next to your certificate, then it means your private key isn’t installed. Delete the certificate (it’s useless without the key), export the key from the original mac it was generated on, and import it on the other machine before you try importing the certificate.

You may see ‘Code sign identity’ mentioned in the project settings later on. This is a synonym for your certificate.

You’ll only need one distribution certificate, it’ll work for all your provisioning profiles (both adhoc and appstore).

Keep in mind that everything i’m discussing is to do with distribution, not development. Always ensure you’re in the ‘distribution’ tab in the iOS provisioning portal.)

Provisioning Profiles

You need two profiles: ad-hoc (TestFlight) and app-store.

When you generate a profile, it will be linked to your certificate, and will only work on a mac that has the cert (and key) installed. When you generate new profiles, however, you don’t need to re-generate the certificate – it isn’t necessary.

Profiles only work for a specific app id (discussed later), or they can work for a wildcard id. For this article, i’ll only be discussing those with specific app id’s.

When profiles are generated, they are locked to a Bundle Seed ID and a Bundle ID. When you compile an app, you must use a profile with a matching bundle id, or the profile won’t work. Your app’s bundle ID can be found in your Info.plist file.

Once an app has been signed with one profile (e.g. your ad-hoc TestFlight profile), you can re-sign it with another profile (e.g. your app-store profile) – IF the Bundle Seed ID’s match.

Provisioning profiles have a unique UUID to identify themselves, eg: 44A1166C-C096-4112-B3E0-9081520CBABC. This UUID can be extracted in the build script, as it changes each time you add a new device to your profile, and you don’t want to have to hunt down the UUID each time you re-generate the profile. It’s far easier to simply check the *..mobileprovision file into your repo, and let the build script look up the UDID, install the profile, and update the build settings accordingly.

App ID’s

An App ID = the Bundle Seed ID + ‘.’ + Bundle ID.

The Bundle Seed ID is also known as the App ID prefix, or the Team ID. When you generate a profile, it will be given your Team ID as it’s Bundle Seed ID. The Bundle Seed ID looks like: 5M3M5W7ABC.

Nowhere in your app’s configuration do you need to specify a bundle seed id, and the only time it matters is for app store submission time: when you want to submit the exact same build that the testers tested. When you go to re-sign the archive with your app store distribution profile, it’ll need to have the same Bundle Seed ID as in the ad-hoc profile you originally signed it with for testing.

The Bundle ID looks like: ‘com.splinter.myawesomeapp’. This is specified in your app’s Info.plist file. When you do a release/archive compile, you’ll need to sign against a provisioning profile that matches this bundle id. Eg, if you have two apps, with the following bundle id’s:

  • com.splinter.world-domination-app
  • com.splinter.servitude-app

In that case, you’ll need different profiles, with bundle id’s to match. (Or you can use wildcard profiles, but i won’t go into them here). The important thing to remember is: the bundle id in the profile must match your app’s bundle id, or it won’t compile.

Server setup

The server I use is a mac mini, with Bamboo. Personally, i don’t really recommend Bamboo as it over-complicates the git repository setup which makes it difficult to push commits up to your git server – which we use to make version number commits. You can use cron jobs, or Hudson, or any other CI setup you wish.

TestFlight

TestFlight has a brilliantly simply upload API that uses Curl from the command line to submit newer versions of your app. The only complication is if you wish to have both dev and stage branches available to your testers: since TestFlight groups builds by bundle name, only the most recent build will be available.

So, to get around this, I use ‘PlistBuddy’ to modify the app name and bundle ID prior to building the dev branch version. Please note that if you do this, you’ll also need to use a provisioning profile that matches the new bundle id.

Build Steps

The steps I follow for making a build are as follows:

  • Increment the version numbers in the Info.plist file
  • Check into git the updated Info.plist
  • Override the bundle id and name, if it is the dev branch
  • Install the provisioning profile
  • Use zerg_xcode to update the project file, to use the desired certificate and provisioning profile
  • Build the .xcarchive
  • Create the .ipa file
  • Upload to TestFlight

Incrementing versions

Our app’s versioning (for internal development and testing) follows a common major.minor.buildnumber strategy. Eg: 1.4.123. Each time the build runs, it increments the build number, updates the Info.plist, and checks it into git. The git commit uses the version number as the commit message so that later on we can see the exact code that was used to produce a given build.

To do this, PlistBuddy is used to extract the old version number, which is then incremented and saved to the Info.plist. In the ruby script, it looks like this:

oldVersion = `/usr/libexec/Plistbuddy -c "print :CFBundleVersion" Info.plist`.strip

This returns a string, such as ‘1.2.3’. We then need to increment this, which the following does:

components = oldVersion.split('.')
newBuild = components.pop.to_i + 1
components.push(newBuild).join('.')

We then need to save this new version number back to the Info.plist, which is a simple matter of using Plistbuddy again. So the complete function to read, increment, and save the file is as follows:

def incrementBundleVersion
    oldVersion = `/usr/libexec/Plistbuddy -c "print :CFBundleVersion" Info.plist`.strip
    components = oldVersion.split('.')
    newBuild = components.pop.to_i + 1
    version = components.push(newBuild).join('.')
    system("/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion #{version}\" Info.plist")
    system("/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString #{version}\" Info.plist")
end

Don’t forget to add the code to check this back into Git after doing this.

Overriding bundle id and names

At this step, you can override the bundle id and bundle names. This is useful if you want to have different branches of your code available to your testers on TestFlight, such as dev and stage builds. Since TestFlight groups builds with the same bundle id/name, you’ll have to change it to do this.

PlistBuddy is used for this:

def overridePlistBundleName(infoPlist, name)
    system("/usr/libexec/PlistBuddy -c \"Set :CFBundleDisplayName #{name}\" \"#{infoPlist}\"")
    system("/usr/libexec/PlistBuddy -c \"Set :CFBundleName #{name}\" \"#{infoPlist}\"")
end

def overridePlistBundleId(infoPlist, id)
    system("/usr/libexec/PlistBuddy -c \"Set :CFBundleIdentifier #{id}\" \"#{infoPlist}\"")
end

Keep in mind of course that if you change the bundle id, you’ll need to use a different provisioning profile that matches the new bundle id.

Installing profiles

Your *.mobileprovision files should be checked into your repo, so that whenever you need to add a new device to your profile it is a simple enough job to update the profile on the iOS Provisioning Portal, download the updated profile, and check it in.

However, your profile will need to be installed. To do this, you first need to get the UUID out of the file, with code such as follows:

# Gets the uuid from a provisioning profile
def getProfileId(provisioningProfile)
    File.open(provisioningProfile,"r") {|f|
        raw = f.read
        nice = raw.gsub("\n","").gsub("\r","").
            gsub("\t","").gsub(" ","")
        matches = nice.scan(/UUID<\/key>(.*)<\/string>/)
        return matches.first.first
    }
    abort("Couldn't find UUID in: " + provisioningProfile)
end

Once you have the profile’s UUID, you can install it automatically, so that nobody has to log into the build server to install new profiles every time that someone updates the profiles. The code to install a profile:

# Installs a profile so we can compile against it and returns it's uuid
def installProfile(provisioningProfile)
    uuid = getProfileId(provisioningProfile)
    dest = File.expand_path("~/Library/MobileDevice/Provisioning Profiles/#{uuid}.mobileprovision")
    system("cp \"#{provisioningProfile}\" \"#{dest}\"")
    uuid
end

After installing the profile, you’ll need to store it’s UUID somewhere for later when it comes to updating the project file so that this profile is explicitly selected.

Zerg

Zerg_xcode is a ruby gem that is used to modify the Xcode project file so that we can explicitly set the provisioning profile and code signing identity (aka certificate).

It is unfortunate that we cannot set these as command line arguments and have to resort to updating the project files, but xcodebuild ignores build settings when archiving.

It is important to manually override the project file, as it gives us much more control over selecting the correct profile and certificate. This is necessary when we need to use different profiles for the different branches, because the bundle id’s are different for TestFlight’s sake (also, it prevents us from submitting a dev build to the app store!).

Here is the code I use to update the project file to manually set the certificate name (aka code sign identity) and profile’s UUID. Note that the certificate name is something like ‘iPhone Distribution: MY COMPANY’, which you can find in your keychain.

require 'rubygems'
require 'zerg_xcode' # https://github.com/ddribin/zerg_xcode

# Update the project to set the profile etc because
# xcodebuild only pays lip service to command line args
def doctorProject(target, identity, profileUuid)
    project = ZergXcode.load("MyProject.xcodeproj")

    configuration = 'Release'

    build_configurations = project["buildConfigurationList"]
        ["buildConfigurations"]
    configuration_object = build_configurations.select {|item|
        item['name'] == configuration }[0]
    configuration_object["buildSettings"]
        ["PROVISIONING_PROFILE"] = profileUuid
    configuration_object["buildSettings"]
        ["PROVISIONING_PROFILE[sdk=iphoneos*]"] = profileUuid
    configuration_object["buildSettings"]
        ["CODE_SIGN_IDENTITY"] = identity
    configuration_object["buildSettings"]
        ["CODE_SIGN_IDENTITY[sdk=iphoneos*]"] = identity

    target = project["targets"].select {|item|
        item['name'] == target }[0]
    build_configurations = target["buildConfigurationList"]
        ["buildConfigurations"]
    configuration_object = build_configurations.select {|item|
        item['name'] == configuration }[0]
    configuration_object["buildSettings"]
        ["PROVISIONING_PROFILE[sdk=iphoneos*]"] = profileUuid
    configuration_object["buildSettings"]
        ["CODE_SIGN_IDENTITY[sdk=iphoneos*]"] = identity

    project.save!
end

Build the .xcarchive

This is where the rubber hits the road: building the archive. Thankfully, with all the prerequisites taken care of by now, it’s a simple matter of the following:

xcodebuild -target "My Target" -scheme "My Scheme" -configuration Release clean archive

Note that we’re doing an ‘archive’, not a ‘build. It’s important to note the difference here. An archive builds a ’.xcarchive', whereas a build produces a .app and a .dsym. However, the .app and .dsym are fairly limited on their own – there’s not much you can do with them. An xcarchive is much more useful: you can import it into Xcode on another mac, re-sign it, and submit to the app store.

It is worth noting than an xcarchive is simply a package folder which contains the .app, .dsym, and a plist descriptor. So if you need the .dsym to symbolicate crashes later on, you can get it out of the xcarchive.

Xcodebuild will put the .xcarchive in a folder such as:

~/Library/Developer/Xcode/Archives/yyyy-mm-dd/MyArchive.xcarchive

To make it easier to find the just-generated archive, in my build script, before the xcodebuild step, I rename the Archives folder. Then after running the build, there will only be one xcarchive file inside the archives folder, which i grab, and then rename the Archives folder back as it was before building. This means that you can only run one build at a time on your build server, but it’s the best outcome given that you can’t control where the file is output.

The .xcarchive is really a folder, which contains the .app folder, among others. It contains symlink(s), which you have to be careful to preserve when copying, moving, and zipping it. If you break the symlink, you will get confusing code signing issues later on when you try to use it.

To preserve the symlinks, I make sure to use ‘mv’ to move the .xcarchive to my desired folder, as I could not get ‘cp’ to maintain relative symlinks. And when zipping it up, ensure that you use the -y option to preserve the symlinks, e.g.:

zip -rqy "MyOutput.zip" "/Folder/With/My/ArchiveAndIpa")
# -r means to recurse through folders, -q quiets the output,
# -y to preserve symlinks so as to not break codesigning

Create the .ipa file

Now that we have the .xcarchive, we need to produce the .ipa which we send to TestFlight for our testers to use. Use a utility called PackageApplication for this. But first you’ll need to find the path to the .app file within the .xcarchive first. I use the following in my ruby build script to do that:

app = Dir[outputFolder+'/*.xcarchive/Products/Applications/*.app'].first

And to generate the IPA filename that’ll go in my output folder:

ipa = outputFolder + '/' + File.basename(app).gsub('.app', '.ipa')

Once you have found those, perform something like the following:

/usr/bin/xcrun -sdk iphoneos PackageApplication
  "/Path/To/MyApp.app" -o "/Path/To/MyOutput.ipa"

One interesting point that took a while to realise, is that a lot of examples you’ll see on the internet for how to use PackageApplication are misleading: they show command line arguments for signing with a certificate and profile, but this is simply unnecessary, as your code was already signed as part of the archive process.

Also, be aware that PackageApplication only works if you pass it full, expanded paths for the .app and .ipa. So relative paths, or any path with ~ in it, will fail with no error message, confusingly. So be careful of that pitfall.

Upload to TestFlight

Once you have your ipa file, it is a simple matter of uploading it to TestFlight using their convenient curl api:

system("curl -F file=@\"#{ipa}\" -F api_token='#{token}'
  -F team_token='#{team}' -F notes=\"#{notes}\" -F notify=True
  -F distribution_lists='#{list}'
  \"http://testflightapp.com/api/builds.json\"")

This api is very simple and better documented on TestFlight’s site anyway, so i won’t get into it much here. It’s just worth reiterating that if you want dev and stage builds to be separately available on TestFlight for your testers, you’ll have to change the bundle id and bundle name as described in the section ‘Overriding bundle id and names’.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Sun, 15 Jan 2012 19:11:00 -0800 Xcode 4 - Command line builds of iPhone apps http://splinter.com.au/xcode-4-command-line-builds-of-iphone-apps http://splinter.com.au/xcode-4-command-line-builds-of-iphone-apps

Here’s the gist of how you can build your iPhone app from the command line. Change into the folder that contains your *.xcodeproj, and run the following:

xcodebuild -target "My Target" -scheme "My Scheme"
  -configuration Release clean archive

This will generate an xcode archive (*.xcarchive) in ~/Library/Developer/Xcode/Archives/DATE/… somewhere.

Now you’ve got your xcarchive, and assuming that your project was set up to sign using an ad-hoc provisioning profile, how to generate the IPA file that you can submit to TestFlight for your testers?

/usr/bin/xcrun -sdk iphoneos PackageApplication
  "/absolute/path/to/MyApp.xcarchive/Products/Applications/MyApp.app"
  -o "/absolute/path/to/MyApp.ipa"

Note that PackageApplication somehow only works if you use absolute paths. So ~ and relative paths don’t work – beware of that.

Now, if you’re clever, you can script all this with ruby to be nicely automated. I’ll get into that in another post. This is part one of a series on continuous integration of iOS apps that i’m working on…

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Fri, 13 Jan 2012 15:05:00 -0800 Guest post by Jason McDougall http://splinter.com.au/guest-post-by-jason-mcdougall http://splinter.com.au/guest-post-by-jason-mcdougall

Guest post by Jason McDougall from Ironside Knights

If you had a business idea and someone offered to help you do all the hard work for you, help get your business up and running: what would be your response?

How good would it be if someone gave you proven instructions to help you get out of your current situation, guaranteed the return, and even offered to do the hard work for you. Would you take the leap?

I think sometimes we are scared, not confident in our abilities or just don’t want to see ourselves fail. But what if we could overcome that? When that person says to us: I will hold your hand every step of the way, get you up and running and ensure your success, and all you have to do is believe, what if we were able to take the risk?

Most of us say we would take it. But why is it then that most of us don’t?

Why is it that most of us are afraid: stuck where we are and not willing to step out and take a risk? Or we rationalise, thinking ‘I don’t have that person that will do it for me’. Ok, so I don’t either – but what if you spent an hour a week actually taking concrete steps towards your goals? After all it is only an hour – would you do it?

Take that risk, step out and believe for the impossible. Believe that we can, believe that we aren’t going to be stopped at every corner. Believe that like Benjamin Franklin, with a bit of hard work we too can achieve our destiny.

This week I want to focus on what we believe is the impossible, whether it be changing our attitude, making those sales calls or just teaching our kids something that they will treasure for the rest of their lives. I want to encourage each of us to step up, step out and take a risk. Do it from a place of confidence that we have the potential and we can achieve it.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Tue, 10 Jan 2012 17:02:00 -0800 Scouts, Games and Motivation http://splinter.com.au/scouts-games-and-motivation http://splinter.com.au/scouts-games-and-motivation

I’ve been thinking lately about the concept of ‘starting small’, and now it’s the time of year for new year resolutions I’m hoping to marry the two.

So the idea is to approach my new years goals in a small way. Sneak up on them, as it were. Catch them unawares. And then knock them flat.

Watch the chips fly

I maintain that it’s impossible to remain motivated at anything unless you can see progress, the metaphorical ‘chips flying’. So why do we set goals that will take an entire year to accomplish? Who has the kind of discipline to remain motivated in their goal half-way into the year, let alone for the full year?

So here’s my strategy to keep myself motivated: lots of small goals. Ideally, a week each. That way, as i can tick them off rapidly, and see my progress, it’ll be easier to remain motivated.

Games and Scouts

This idea of lots of little goals came to me, of all places, while playing jetpack joyride on my phone. This game has a very common ‘game mechanic’ these days, which is aimed at keeping you engaged with the game. Because the longer they can keep you engaged with the game, the more likely it is that you’ll spend money on upgrades etc.

The game mechanic is to give you lots of small accomplishments as you play. At any time, there are only three goals that you can achieve. The small number of goals avoids too-many-options paralysis and helps keep you focused. And the fact that the goals are simple to achieve keeps you motivated because you can see your progress.

There’s nothing more demotivating than trying to do something incredibly difficult and making absolutely zero progress, is there? You just feel like you’re wasting your time. Which is the feeling this ‘game mechanic’ aims to avoid.

And this mechanic isn’t even a new idea – the Boy Scouts thought of it a very long time ago! Their badge system is the original game-like engagement strategy: lots of small, achievable goals that you can try. And as you achieve each one, you get a nice pat on the back: a little badge to sew onto your uniform. Sometimes i wonder if the game companies deliberately copied this system. It’s genius.

One more thing: as I’ve written about before, it’s impossible to really stick with something if you don’t really believe that you stand a chance at achieving it. So by keeping these goals within your reach, by making them believable, you stand a much better chance of sticking with it.

Example goals

So, when coming up with my goals, i’m keeping a few things in mind so as to emulate the game/scouts system: Keep them simple, short, achievable, and part of a bigger goal.

A bigger goal i have this year is to be less tired and irritable. So my first few mini-goals are:

  • Get to bed by 10
  • Not drinking any coke/coffee late in the day
  • Go cycling every day

These goals will hopefully help me sleep better, by making me more regular in my sleep patterns, with less caffeine in my system and a bit more tired. Better sleep = less tired = less irritable. Brilliant!

How

So how to put these goals into place? In my case, i’m taking one at a time. Each goal will last for a week, and i’ll make a little checklist on a post-it note for each day. I’ll keep this in my wallet and check it off each day, and at the end of the week i’ll hopefully have done well. Once a week, i’ll use Trello to keep track of 3 lists: upcoming goals, my current goal, and accomplished goals. The accomplished goals is important: looking at this list rapidly grow is the key to keeping motivation and momentum up.

The key here is that everything is so simple and achievable. I’m not shooting for the stars or anything crazy. All i’m doing is getting to bed early for a week. Anyone can do that. Then i’ll cut out some caffeine (still have it in the morning though) – who can’t keep that up for a single week? As for cycling, well i’ll do the best i can.

Anyway I think it’s a more realistic system than massive goals like ‘Learn another language’. I mean, goals like that will get you nowhere. It’s like going indoor rock-climbing and going straight to the difficult wall. You’re not going to get anywhere. Much better to start at the shorter walls with lots of easy-to-hold rocks. Which is, in a nutshell, what i’m doing.

And, if i could be so presumptuous, you should too.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Sun, 08 Jan 2012 15:16:00 -0800 2011 Re-cap http://splinter.com.au/2011-re-cap http://splinter.com.au/2011-re-cap

So, it’s curtains for 2011, and i’m old now, having just had another birthday. Time to grow up, I guess. Well – in that vein, here’s a bunch of things i learned and grew in over the last year.

Be active, not passive

I spent 4 years in a dead-end job, waiting for something great to happen. Needless to say, it didn’t. Eventually, the revelation dawned on me that I was meant to make that ‘something great’ happen. I should have been active, taking initiative, rather than passive. We’re not meant to sit there and take life as it comes, we’re made to get out there make a ruckus.

This led me to wonder, ok so i’m meant to do something, but do exactly what? Options in life are basically infinite, so you need some boundaries when choosing. Which leads to my next two lessons.

Use what’s in your hand

So – what to do with my newfound zeal for action? Well, the first key is to look at what’s in your hand. Do what you can with what you have. In my case, that is programming skills with making iPhone apps. So I started making lots of apps in my spare time and on the train to work, and putting them on the app store. I contacted a couple of charities and offered to make apps for them.

Try a bunch of things

The next thought i had was to use the concept of ‘failing forwards’ when it came to trying to figure out what i should be doing. Basically, try a bunch of ideas, and see what fails and what works. The failures – well, give those up as a bad idea; as for the successes, keep at it with those.

So for me, working with the charities ended up being a failure. Working on my ‘comet server’ project was another failure. But working on iPhone apps in my spare time was a resounding success.

Start a book club with friends

This year I started a book club with a few friends. We mainly read inspirational / leadership themed books, and meet up every few weeks to discuss what we got out of them. It’s been a great source of inspiration and growth to us all.

Some practical things that have been important: Kindles, not paper books, are the only way to go in Australia – as it simply takes too long to ship books from the states for it to be workable. Also, we’ve decided it all works better if we’re reading the same book, so that we can all discuss it together.

It turns out that Benjamin Franklin did exactly this – set up a book club with friends. In the day of kindles and cheap ebooks, it’s a really easy way to grow.

Start small

One recurring theme in my life this year has been to ‘start small’. In my apps, i try to keep them small and manageable so that i’m more likely to finish them. Same with other projects in life: my friend one day wants to become an ambassador for fatherhood. So we’re working on getting to start small, as a blogger. Once he’s got 100 posts, he’ll write an ebook. Later on, possibly a printed book, and then we’ll see what doors that opens.

The key is to start small, with something that is within reach and currently achievable. Starting small is the key to achieving big things.

If you’ve got no platform, you can still work on yourself

It’s all about building a platform. Someone like Bono has a massive opportunity to make a difference, because of his influence. His fame and influence with world leaders are his platform. And he can act from that platform and achieve far more than most people can.

I’ve found that living an influential life is about building that platform. We start with no platform, then we may have a small platform when we have a family to influence, then a larger platform if we became a business leader, and so on. As before, it is all about starting small, and doing what we can with the platform we’ve got. As we do what we can with our current sized platform, it’ll naturally grow – and then we can do more.

But all this left my friends and I with the question – what do we do, as guys with no platform or influence? Well, we realised that if you’re not in leadership over anyone else, you’re in leadership over yourself. So your own self is your platform. So do what you can with that – build yourself, learn to lead yourself and grow yourself.

I’ve got a plan now

This year’s growth started with the thought: I’m getting older, probably should grow up and do something with my life. But no further plan was forthcoming until much later on. It’s funny how doing the best with what you’ve got brings ideas to the fore about what you can do next.

Anyway my plan (described in another blog post) is to keep working for a few years, meanwhile working on apps in my spare time. And when the time comes that the income from apps reliably eclipses my day job, i would leave work and start a business creating iPad applications for small businesses, with the dream being to emulate the success of Dr Chrono (an iPad medical management app) in Australian hospitals.

It’s surprising what you can achieve with a bit of work

There’s been a lot of hard work this year. Between me and a friend, we’ve written a heck of a lot of apps and blog posts. But it’s been amazing what has come out of it. In his case, his quality of writing has gone through the roof. His first book is looking more achievable by the day. As for me, 10 apps later things are looking bright.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Wed, 04 Jan 2012 16:00:00 -0800 Ruby script to increment a build number http://splinter.com.au/ruby-script-to-increment-a-build-number http://splinter.com.au/ruby-script-to-increment-a-build-number

This is used to increment a build number in a tracking file for use in a build / CI script:

#!/usr/bin/ruby

def increment(filename)
    # Read it
    file_str = ''
    if File.exists?(filename)
        file_str = File.open(filename, 'r') {|file| file.read }
    end

    old_version = file_str.to_i
    new_version = old_version+1

    # Write
    File.open(filename, 'w') {|file| file.write(new_version) }

    puts "Incrementing version number in #{filename} to #{new_version}" 
end

if ARGV.length < 1
    puts 'Usage: VersionIncrement '
else
    increment(ARGV.first)
end

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Sun, 18 Dec 2011 16:30:28 -0800 Turning 30? All ideas, no execution? http://splinter.com.au/turning-30-all-ideas-no-execution http://splinter.com.au/turning-30-all-ideas-no-execution

Inspired by a recent popular post on HN about a guy who’s about to turn 30 and laments the lack of actually doing anything in his life…

Ideas

Some people regret the fact that they can come up with lots of great ideas, but cannot execute them. Others are frustrated that they can execute well, but cannot come up with an idea that is original or noteworthy. I personally find myself in the latter – but either way, people are going to be upset that they find themselves on one side of the balance and lack the ability to do the other.

I used to think that ‘ideas are nothing, execution is everything’. I think this idea has been spread wide and far by people who find themselves on the former side of the aforementioned scale, thinking: ‘I’ve got lots of ideas but haven’t prospered – therefore ideas must be worthless’.

Nowadays, I’ve come to the conclusion that ideas are just as valuable as an execution skillset. I’ve spend the last year executing well – i’ve come up with 10 original iPhone app ideas, and executed and shipped all of them in my spare time (and they’re not just “fart apps” – they are useful utilities). So by any measure, i think i can safely claim to be good at execution.

But here’s the rub – 2 of my apps are the only ones that have done well. And, to me, these were among the most poorly executed of my apps – they certainly didn’t take more than a couple weeks each to develop, with no marketing whatsoever. So, i’ve come to conclude that the ideas behind these apps, in terms of creating something that people actually want to buy, have made them successful.

For an opposing example, let’s pick on my first app, which allows you to enter your 20 favourite recipes. It will then choose a week’s worth of dinners at random, and compile a combined shopping list for you, and let you tick off the items as you purchase them. Now the market has decided that this idea isn’t worth paying $1 for. Hey, what gives? I executed pretty well – its not an awful app, why won’t people buy it? I can only conclude – the idea sucked.

So, if you’re what we call in Australia an ‘Ideas Man’, don’t be too upset – the world needs your great ideas just as much as it needs people like me who can execute on them.

Growing

And who’s to say you’re at one end of the scale or the other? Who’s to say that i’m good with ideas, therefore i must be awful at execution? Who’s to say that if i’m good at grinding out the work and getting things done, I mustn’t be very creative?

The two skills aren’t mutually exclusive. There’s no reason you can’t be good at both. If you’re great at one, there’s no reason you can’t grow to become good at the other. People can grow and change, if they believe they can. I mean, i’m sure you’re better at a lot of things than you were 10 years ago.

Becoming an Ideas Man

This part is for me: how to get better at ideas. James Altucher talks a lot about your ‘Idea Muscle’ – basically his idea is that the creative part of your brain is plastic, and if you practice enough, it will get better at coming up with ideas.

So his exercise is to spend 10 mins each day coming up with ideas. Just get a post it note, and scribble down 10 ideas each morning, to get the juices flowing. Start with writing down terrible ideas, just so long as you’re coming up with any ideas you’ll be working out your brain. And inevitably the ideas will improve with practice.

It’s worth a try – i’ll see how I go with it. If nothing else, it’ll provide me with, on average, as poor ideas as I already have – but due to the volume, there’ll surely be a couple of gems in there that’ll be worth executing on.

(And ladies, the reason I don’t say ‘Ideas Person’ is because in Australia, ‘Ideas Man’ is a bit of an amusing meme.)

Become an Execution Man

How to get better at ‘getting things done’? Well, for me, i’ve found the key is to start small. Recognise that you have a set ‘attention span’ and work on projects that fit into that span. And each time you ‘ship’, your confidence and thus your ‘attention span’ will bump up a notch.

For instance, my attention span for any project is about a month. If anything takes longer than that, i’ll become demotivated, disinterested, and bored – it simply won’t get done. So the key is – when starting a project, i think ‘can i do this in less than a month?’. If so, i’m golden – i’m confident i can get through it with my motivation intact.

If you bite off more than you can chew, you’ll end up with a string of large projects that you only got ½ way through and abandoned them. Don’t be tempted to take on those large projects – if it’s ego, or ambition making you do it, well swallow your pride and try biting off something a bit more ‘bite sized’.

Babies start with small spoonfulls of smooshy stuff: if you feed them too much they’ll spit it out. You’re like that – start with small smooshy projects, and as you personally grow in your ability to execute, work your way up to a massive steak of a project.

The key is to start small, and take on bigger projects as your capacity grows bigger. Just be sure to take on projects that you can finish – because each time you finish, your confidence will grow. Don’t take on projects that you can’t finish – because when you fail, you’ll shrink. As you stick to achievable projects, your confidence, attention span, and motivation will grow.

Keep growing :)

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Wed, 14 Dec 2011 17:45:00 -0800 CHDropboxSync - simply sync your iOS app's documents to Dropbox http://splinter.com.au/chdropboxsync-simply-sync-your-ios-apps-docum http://splinter.com.au/chdropboxsync-simply-sync-your-ios-apps-docum

This is a small library designed to make it extremely simple for you to sync your iOS app’s ‘Documents’ folder’s files and subfolders to a folder on Dropbox.

Recently Dropbox brought out ‘sandbox’ api keys, which mean that your app only gets access to a single folder such as Dropbox/Apps/MyAppName. The idea is that this library is to be used with that setup.

Unfortunately the normal Dropbox API only gives primitive methods for getting metadata, uploading and downloading specific files – it doesn’t give you the simple ‘sync this folder now please’ functionality that you’re probably used to in the desktop variant. So this library hopes to deal with that.

It’s been extracted from an app I’ve been writing to help keep track of your cars' oil changes. You can see the app on http://apps.splinter.com.au. If you find this library useful, please buy my app, it may also be useful! Unless you live in appsterdam, sorry but I don’t have any apps for keeping track of the oil changes on your bicycle ;)

Where to get it

github.com/chrishulbert/CHDropboxSync

How to use

The idea is that in one view controller of your app, you’ll have a ‘sync’ button. In this controller, you’ll want a retain property to keep the syncer object alive. And this controller will be a delegate of the syncer, so it’ll know when to dealloc the syncer by nilling out the property.

Eg, in your view controller:

#import "CHDropboxSync.h"
@property(retain) CHDropboxSync* syncer;
@synthesize syncer;

// Do the dropbox sync. This takes care of all user alerts
- (void)mySyncButtonWasTapped {
    self.syncer = $new(CHDropboxSync);
    self.syncer.delegate = self;
    [self.syncer doSync];
}

// Delegate callback from the syncer. You can dealloc it now.
- (void)syncComplete {
    self.syncer = nil;
}

- (void)dealloc {
    self.syncer = nil; // Probably a good idea to do this
    ...
    [super dealloc];
}

Note that it is up to you to handle the normal Dropbox linking process.

Sync strategy

You may choose to write your own syncer, eg you may wish it to be a background process instead of modal which i’ve chosen to. Or you may be curious about the two-way sync strategy. It’s very simple algorithm, so I wouldn’t recommend it for life-critical apps, but it does work well for my app:

  • After each synchronisation, make a note of all files/folders in the local documents folder, and their last modified dates, and store it for use in the next sync. This is called the previous sync status.
  • When syncing, it compares files and folders locally vs remotely.
  • If a file exists in both places but the last modified dates are different, it syncs to use the newer one
  • If a file/folder only exists in one place, it uses the previous sync status to determine if the file was deleted or added since the last sync.
  • If the item was deleted in one place since the last sync, it will be deleted in the other place.
  • If the item was added since the last sync, it will be uploaded/downloaded to the other place.
  • The code is intentionally clear (not concise) so you should be able to read it to get how this works.

Dependencies

  • ConciseKit
  • DropboxSDK v1.0+

Other options

  • There is also jessegrosjean’s DropboxSync, however it does not work with the latest Dropbox API. You should check it out if you’re not happy with CHDropboxSync.

License

MIT license – no warranties!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert
Sat, 10 Dec 2011 17:14:00 -0800 Deep-enumerating a directory on the iphone, getting file attributes as you go http://splinter.com.au/deep-enumerating-a-directory-on-the-iphone-ge http://splinter.com.au/deep-enumerating-a-directory-on-the-iphone-ge

I found out the hard way, after lots of time wasted then a bit of googling, that NSFileManager’s enumeratorAtURL crashes! Don’t use it.

So if you need to recursively search a folder in objective-c (on the iPhone, in my case), grabbing all the attributes of the files (such as last-modified date), here’s an alternative way to do it. Please note this example uses ConciseKit:

NSMutableSet* pathsToSearch = $mset($.documentPath);
while (pathsToSearch.count) {
    // Pop a path to search
    NSString* pathToSearch = [pathsToSearch anyObject];
    [pathsToSearch removeObject:pathToSearch];

    // Scan it
    NSArray* contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pathToSearch error:nil];
    for (NSString* item in contents) {
        // Item is a file or a folder
        NSString* itemPath = [pathToSearch stringByAppendingPathComponent:item]; // Get the full path for it

        // Get the attributes
        NSDictionary* attribs = [[NSFileManager defaultManager] attributesOfItemAtPath:itemPath error:nil];
        BOOL isDirectory = $eql([attribs $for:NSFileType], NSFileTypeDirectory);
        BOOL isFile = $eql([attribs $for:NSFileType], NSFileTypeRegular);
        NSDate* modified = [attribs $for:NSFileModificationDate];

        // Recurse if its a folder
        if (isDirectory) {
            [pathsToSearch addObject:itemPath];
        }

        // Do something with it
        NSLog(@"%@; %@", itemPath, modified);
    }
}

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1109544/Jeans.jpg http://posterous.com/users/heOedYyuwnXS2 Chris Hulbert chrisfromsydney Chris Hulbert