Apps to make life simpler.

Chris Hulbert is a iOS developer / freelance contractor in Sydney, Australia. Splinter Software is my alter ego on the App store.
Here is my blog, and the apps with which I hope to make the world a simpler place.

See Apps »


So I've written in the past about how nice WebP is. I'm currently working on a personal project that'll incorporate maybe ~260 full-screen retina images on the iPad - lots of pictures! I want them to be WebP to keep download sizes sensible. So i tried a cocoapod (iOS-WebP) that purported to decode WebP for me, but it didn't work. So here's my solution which is simpler, and works:

Header file:

//
//  UIImage+WebP.h
//
//  Created by Chris Hulbert on 9/02/2014.
//  Copyright (c) 2014 Chris Hulbert. All rights reserved.
//
//  This gives you methods for sync and async webp image loading.

#import <UIKit/UIKit.h>

typedef void(^WebpImageCompletionBlock)(UIImage *image);

@interface UIImage (WebP)

/// Synchronously loads a webp image. This can be called from any thread.
+ (instancetype)webpImageNamed:(NSString *)name;

/// Async decoding of a webp image on a background thread. Callback is on main thread.
+ (void)webpDecodeImageNamed:(NSString *)name completion:(WebpImageCompletionBlock)completion;

@end

Implementation file:

//
//  UIImage+WebP.m
//
//  Created by Chris Hulbert on 9/02/2014.
//  Copyright (c) 2014 Chris Hulbert. All rights reserved.
//
//  This gives you methods for sync and async webp image loading.

#import "UIImage+WebP.h"

#import <WebP/decode.h>

@implementation UIImage (WebP)

+ (instancetype)webpImageNamed:(NSString *)name {
    // Load the source data.
    NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"webp"];
    NSData *data = [NSData dataWithContentsOfFile:path];

    // WebP-decode it.
    int width=0, height=0;
    void *webpDecoded = WebPDecodeARGB(data.bytes, data.length, &width, &height);

    // Free the source data.
    data = nil;

    // Use the webpDecoded data to make a CG context.
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(webpDecoded, width, height, 8, width*4, space, (CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
    CGColorSpaceRelease(space);

    // Convert it to a CGImage.
    CGImageRef cgImage = CGBitmapContextCreateImage(context);
    // Free the CG context and decoded data.
    CGContextRelease(context);
    free(webpDecoded);

    // Convert the CGImage to a UIImage.
    UIImage *uiImage = [UIImage imageWithCGImage:cgImage scale:2 orientation:UIImageOrientationUp];
    CGImageRelease(cgImage);

    return uiImage;
}

+ (void)webpDecodeImageNamed:(NSString *)name completion:(WebpImageCompletionBlock)completion {
    // Make the queue.
    static dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("com.splinter.webpdecoder", DISPATCH_QUEUE_CONCURRENT);
    });

    // Decode it in the background.
    dispatch_async(queue, ^{
        UIImage *image = [self webpImageNamed:name];

        // Callback in the main thread.
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(image);
        });
    });
}

@end

You'll need to include Google's 'libwebp' cocoapod for this to work.

Hi everyone, Chris Hulbert here - the iOS guy who seems to always be blogging about my travails building a house, instead of, well, iOS.

So, what's new? My current contract is finishing in a week, and i'm currently looking for new work. If you know anyone in Sydney who needs a great app made, please get in touch.

How's the house? Well, we're still struggling to get it certified. The latest update is that we got three bushfire assessments made (as we back onto a bushy creek), 2 years ago. Two assessments said it's minimal danger. One assessment said it's maximum danger, strangely (this assessment later was retracted by the issuing company).

Well, guess what i discovered recently? The maximum danger assessment is the one that council and the certifier thinks is the official one, even though we've got an email trail proving that our original builder sent them the other (minimum-danger) assessment before they issued the DA. (A DA is an australian development approval. Redtape, basically.)

So, we now have to fight to get the correct bushfire assessment recognised. Priceless! We'll get there, eventually.

Anyway, i just bought a surfboard, which is great fun riding with my girls :)

Keep in touch, friends!

TL;DR: If you overcompress your images, you can get a typical 1.5M JPEG file down to 245kb using WebP and it'll look fine on retina, at 1/6th the size!

Retina laptops and iPads are common now, and we need to re-think our image compression - JPEG's at default quality (70) are massive overkill these days, and are slowing down our apps/websites. Screens are improving faster than our mobile internet infrastructure is, that's for sure!

So, I grabbed an image, sized it to a full-screen iPad retina images, and ran a bunch of tests, compressing and over-compressing the image on JPEG and WebP, to see what I could come up with as a reasonable tradeoff between file-size and quality. Here's the original image:

http://interfacelift.com/wallpaper/details/3359/goat_at_mount_pilatus.html

I used this image because it's a nice combo of smooth sky areas and detailed rocky areas, and the contrast of the detailed horns against the smooth grass.

It's worth noting there are a few other options too: Jpeg2000, JPEG XR, and HEVC-MSP. JPEG 2000 is great, but is really slow to decode so not really ideal for mobile (fine for desktop though). JPEG XR is good too, especially on the high-end of the quality scale. But we're interested in the low-end of the quality scale in this article, what with over-compressing our images for retina and all, so i'll leave it. And HEVC-MSP is great according to the Mozilla study linked later on, but it's not finished yet AFAIK so i'll revisit it when it's released and there's a good standard library for it.

So, my findings:

JPEG

I'm using Acorn's JPEG functionality for these tests.

70 - This seems to be the typical JPEG quality setting. Looks great, but it's 1.5MB. You better hope your poor user isn't downloading this bad-boy over 3G while they're sweating it out on the train.

20 - goat horns look fine, sky starts looking noticeably not great. I think you could get away with this, in retina. 500kb.

15 - Even in retina, the sky looks bad. Still, horns and mountains look fine. Avoid, for images with smooth parts.

10 - Same as 15: Detailed areas - horns and mountains - look fine. 401k.

5 and 0 - Both fine in the detailed areas. File isn't much smaller anyway (356 vs 401k) so it's not worth it unless you're desperate.

So: My recommendation, for retina: You can generally get down to 20. Or 10 if you really don't care all that much, or if there are no 'smooth' areas (eg sky) in your photo.

WebP

I'm using the 'libwebp-0.3.1-mac-10.8' command line tools for these tests.

80 - This is the default setting. Looks great, and it's 916kb. So right off the bat, it's killing JPEG.

60,50,40,30 - All good. Getting down to 414kb.

20 - Starting to be noticeably dropping quality if you look close. I doubt you'd notice on retina unless you're an eagle-eye. 334KB.

15 - Same comments as 20. 301k

10 - This is as low as i'd go personally. Kindof the opposite of JPEG: smooth areas (sky) look great, detailed areas (mountain) not so great. Horns still look great though. 245kb.

5 - I'm on the fence about whether or not this is acceptable, to be honest. Wow the file size is temptingly small, though: 192kb.

0 - Yuck. Avoid. 87kb.

WebP has the big advantage that its artefacts are much nicer to the eye, even when they become visible. So I think you can push it further into 'visible artefacts' territory than you can with JPEG.

If i were you, i'd go with WebP at quality 10, at 245kb. Compared to the smallest nice-looking JPEG (Q20, 500kb) - it's half the size!

Summary

So, if you're anything like me, you're probably currently naively using quality 70 JPEG, which is 1.5MB for a typical full-screen iPad retina image.

With some over-compression that wouldn't really be noticeable on retina, you can get that down to 500k and stick with JPEG (certainly worth doing if this whole WebP thing is a bit too much effort for you). That's a third the size already!

And if you switch to WebP, you can arguably get that down to 245kb - that's 1/6th the original size! That's a massive performance win for anyone trying to download your assets over 3G.

Keep in mind that most people use apps on the train or bus, where the cell towers are being inundated by stacks of users. I'm sure they'd rather get a 'looks almost perfect' image in, say, 10 seconds; versus an image that looks a tiny bit better, but takes a minute to download.

Note: This is a very subjective article! Take it with a grain of salt. This is all my opinion, comparing what 'looks good' to my eyes. Oh, and i didn't include any screenshots, because you're probably reading this on a non-retina desktop screen like I am, so I don't think it worth the effort.

Relevant studies:

Worth a read if you're super keen. But I still think it's all subjective, you should play with quality settings and choose what you think looks best. Here you go:

http://people.mozilla.org/~josh/lossy_compressed_image_study_october_2013/

https://developers.google.com/speed/webp/docs/webp_study

Let's get the embarrassing thing out of the way first: I did an MBA. Well, half an MBA. But I was simply misguided at the time, so I'll let you in on a little secret:

Going into management isn't the only way to get ahead if you're a good developer.

Sounds obvious in hindsight, really. But, for the benefit of the guy on work experience at work (whom this post is dedicated to), and for anyone else in the early stages of their career, you can read on...

There are more options than management

You'll find, as a developer, that you can change jobs a few times early on in your career, getting payrises each time until you hit a pay plateau. This is the point at which you're earning 'market rate'. If you live in sydney, like I do, you'll be scratching your head thinking 'this isn't enough pay to support a family - i need to do something'. At this stage you may think, as I did, that the next step is management.

But you may have looked around at management types, and thought they don't all look like they're enjoying it, shall we say? Running around from meeting to meeting, it doesn't really look like the dream most of us have for our futures.

Many writers will give you their opinion on the path you should take. But your path will likely look very different to mine. We're all unique after all. So, in that spirit, I'll do my best to simply give you a few different options you can consider:

Contracting

Contracting through a recruiter is a very simple affair: It's basically having a series of 3 or 6 month jobs. You get paid a significant amount more (email me if you're interested in gory details!), but you don't get sick days, you're expected to be very talented, and there's obviously a bit of risk involved, with the possibility of idle time between contracts. Certainly worth looking into if you're talented, and don't have a wife or kids to support (or have some savings).

If you try this, I'll give you bonus points for keeping in touch with all the cool people you'll meet through your series of contracts. Contracting for a few years can do wonders for your network. Relationships matter for many more important reasons than your career, so make the time to catch up for lunch with ex-colleagues every now and again!

Doing a startup

I've never done this, so won't pretend to have much advice on this. But, if you haven't got a wife/kids/mortgage you have to at least consider this. Even in Sydney there are lots of startup incubators around, such as pollenizer or startmate. Or simply self-fund / bootstrap and build a B2B SAAS app, Amy Hoy-style. I'm personally considering doing this when my current contract expires.

Intrapreneur

No, that's not a mis-spelling. This is where you take on the mindset of an entrepreneur, but apply it internally at a big corporate. This is by no means second rate to starting your own business! The world has plenty of big companies that are overrun by disinterested people, and they really need passionate 'intrapreneur' types to shake them up for the better.

You may have to work at a few different places before you find one where you can flourish as this kind of person. You'll be sticking your neck out - some places you might just get it chopped off. Stick your neck out anyway.

Here's a good tip: If your company has a person in the address book with the title 'innovation manager' - shout them lunch and tell them all your ideas that could improve the company. Or simply, as I do, email some suitably high-up manager your suggestions for ways they could better use you time.

This is my favoured path for the majority of people.

Freelancing

If you've got the time, you can take on freelance work in the evenings to supplement your income. We all know that living in Sydney costs a fortune, so this can really help. If you do this, be careful to start off with smaller achievable projects.

I've found the best way to find clients is through networking, friends, and having a good blog. You may even find work through a freelancer website. When doing freelance work, be sure to split your work up into small milestones (say $1-2000 each) so they don't run out on your bill, and be sure to send the client updates every few days on your progress.

Working part time

If you've got kids/mortgage/whatever, and are chained with 'golden handcuffs' to a boring job that pays the bills - firstly, I feel for you, I totally understand. Your options are limited. But a really good option is to convince your boss to let you work 4 days a week - I've seen a few people do this. You could use the remaining day to work on a startup or side business, or on building a set of freelance clients that one day could support you full-time. For the sake of your sanity, try to get out of that rut.

Networking

Go to meetups! Find a couple that are related to your chosen language and go every time they're on and make some friends. For instance, in Sydney there is Cocoaheads, SydJS, RORO, and others.

Also, keep in touch with ex-colleagues. It's worth the effort to do lunch every now and again. Get out of your shell and be social!

If you're still deciding what language you want to specialise in, make sure you choose one that has a good local community. It just makes lots of things easier in the long term.

Mentors

Most of your mentors will be people you never meet face to face. They'll be people like Seth Godin, Ramit Sethi, Patrick Mackenzie, Hugh MacLeod, Amy Hoy, and so on. Read their books, their blogs, their podcasts - soak it up. These guys (and gal) in particular are great. They'll change your mindset for the better.

All this is predicated on you being good at what you do, but if you truly love it, that won't be a problem, will it?

Go, choose your own adventure. Your career and workplace will mould your personality in ways you can only imagine. So don't go into management unless you really want to - there are plenty of alternatives to try. Best of luck, Matt!

Hi everyone! Firstly, in case you're wondering, you previously subscribed to by blog at splinter.com.au. I blog about iOS development and my roundabout journey on the way to entrepreneurship. I think a little brutal honesty in the form of my less-than-stellar story can help people feel like they're not the only one on the roundabout, hoping to get off it one day.

Well, what's new? My iOS apps are down to single-digits daily income. It's a pretty common story for indie developers, really. Not much to speak of there. Our house is almost finished being built, we're being held up by the government department for hooking up gas. Contracting at News Corp currently - the project finishes in 6 weeks but my contract finishes in a few months, which is interesting. I've started a mailing list for iOS devs in Sydney looking for work too - i get a lot of companies contact me directly through this blog when they're looking for people, so i figured why not make a mailing list for it: splinter.com.au/grapevine

Year of positivity

As i've written about before, this year i've been trying a few different approaches to becoming a more positive guy. I hope someone out there finds this useful. I'm sure there are a lot of you out there battling the daily grind of starting a business on the side, and can do with a dose of positivity :)

I've been trying to brainwash myself. I bet that sounds terrible, so let me explain: Essentially the idea is to come up with some positive phrase and repeat it in your mind several times a day with an alarm on your phone, trying to get it 'stuck' in your mind. In my case, the phrase was something to the effect of 'I'm not a victim, our builder almost sent us broke, but it drove me to contracting and now we're doing OK.' This sentence might make more sense if you've been reading my blog a while and have followed the saga my family's been through building a house.

But what i've found is that no matter how many times i repeat this phrase in my conscious mind, i get more and more frustrated. You know what i'm pretty sure is happening? Deep down, i just can't accept this sentence - my unconscious mind knows it's a lie! The truth is, i was already a contractor before our builder went broke. If they hadn't gone broke, i would still be earning contractor rates, but wouldn't be paying off a monstrous debt. There could have been a nice new car or boat sitting in the driveway, or some money saved that i could have used to fund time off to attempt a startup. Instead, well, there's just an empty driveway. But at least we're solvent :)

So trying to brainwash yourself into positivity by simply repeating a positive phrase doesn't work in my experience, if you unconsciously reject the phrase.

Friends

Recently, I spent a couple of days at work chasing down a bug. In the end, i couldn't find it, and one of my colleagues found it in his spare time. Very embarrassing - although less so than Apple's iMessage woes in iOS7!

Anyway, got me thinking - we're not meant to operate in isolation. Working closely with encouraging friends will take you a lot further than you can get on your own. So that's going to be my next idea on the path to positivity - see if i can make some regular time with close friends to encourage each other. It'd be easier if so many of my mates hadn't left sydney due to the unaffordable housing market!

Keep in touch, everyone.

Have you ever chambered a round in a rifle? The best part is the satisfying 'shick-chunk' as you push the bolt forwards, then lock it down. Ok, maybe you haven't: but have you ever driven a car with a good, notchy manual gearbox? Or how about hitting a baseball? There's something very satisfying about these. That's what i'd like to talk about now.

What's so much fun about these actions? I think, in a nutshell, they're very tactile experiences. As you force the round into the chamber, you're in complete control of that bit of copper and lead. You feel the vibration in your hand as the firing pin clicks into position. You feel the resistance as you lock the bolt down. When changing gear in a nice car, you feel the friction point as the clutch travels, you feel the gearbox snick into gear through the gearknob, and you feel it in your butt as you plant the accelerator once it's in gear. Or when you swing for the ball, you feel it hit the sweet spot of the bat, you hear the satisfying crack, the almost-painful vibrations in the bat as the ball takes flight.

And on the other hand, if you're loading a rifle and the bullet jams, or it pings off into the grass as it somehow doesn't go in the barrel right, or if you miss a gear shift and crunch your gearbox, or if you swing for the ball but it just hits the edge of the bat - it's just so frustrating, isn't it? Even writing this paragraph is putting my teeth on edge.

Personally, I think it's all about control and feedback. You feel in control of the rifle, of your car, of that bat. You're getting feedback that says 'you got it right'. I think that's the essence of why activities like these are so good for stress relief: after a week feeling frustrated and powerless at work, you get to be in complete control of that gearbox. I really think that axis of powerlessness vs control is a powerful force when it comes to what makes us human, and tactile activities that make us feel in control are huge stress relievers, especially activites using our hands.

Which brings me to apps. How satisfying is it when there's a good-sized button in an iPhone app that you can easily touch, and you get instant feedback that you touched it by the button turning a darker colour (iOS7 notwithstanding)? Conversely, how frustrating is it when a button is too small, and it takes 3 tries to tap it successfully? And how frustrating are web apps, where (because it's all a scrollable area), button taps are delayed by half a second, so the button only goes dark grey sometime after you tap it? It just isn't a tactile experience, and to me it feels cheap and nasty.

Or the other day, when i saw a co-worker using Powerpoint on windows 8: it has the same swipe to change pages as you'd expect, but you get no feedback on your swipe until you lift your finger. They obviously thought it'd be easier to wait for the finger gesture to finish instead of showing the pages scrolling with your finger. And if you swipe left, it won't go back a page either. It's just not tactile, not satisfying to use, and really frustrating.

So, when you make your apps, please make them responsive, tactile, and make the user feel like they're in control. Don't put buttons in a UIScrollView if possible. If you can, set delaysContentTouches to NO for your UIScrollViews. Make tap areas at least 44pt. And if your designer has his heart set on a design with sub-44pt controls, see if you can make the tappable zone stretch beyond the actual visual size of thutton. Make sure your UIButton's highlighted state looks sufficiently different to the normal state (although with iOS7 coming, i'm not sure what that'll affect). In your UIGestureRecognizers, make sure you update the UIGestureRecognizerStateChanged state and update the visuals to suit, not just waiting for the gesture completion. And so on and so forth… i hope you get the idea.

Thanks for reading :)

You can read more of my blog here in my blog archive.