Matt Connolly's Blog

my brain dumps here…

Tag Archives: iphone

Xcode testing AFNetwork Operation callback blocks

Just recently, I was writing some tests in Xcode for some HTTP requests using the AFNetwork library. Previously I’ve used the ASIHTTPRequest library, but in this particular project, I’ve chosen to use AFNetworking for its JSON support.

Since the requests run asynchronously we need a way to wait for the operation to complete. This is easy:

- (void)testRequest
{
    MyHTTPClient* api = [MyHTTPClient sharedInstance]; // subclass of AFHTTPClient
    NSDictionary* parameters = [NSDictionary dictionary]; // add query parameters to this dict.
    __block int status = 0;
    AFJSONRequestOperation* request = [api getPath:@"path/to/test"
                                        parameters:parameters
                                           success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                               // success code
                                               status = 1;
                                               NSLog(@"succeeded");
                                           } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                               // failure
                                               status = 2;
                                               NSLog(@"failed");
                                           }];
    [api enqueueHTTPRequestOperation:request];
    [api.operationQueue waitUntilAllOperationsAreFinished];

    STAssertTrue([request isFinished], @"request finished");
    STAssertEquals(request.response.statusCode, 200, @"request returned 200 OK");
    STAssertEquals(status, 1, @"success block was executed");
}

This is great for testing that the request completes, and verifying its status. But if we need to test anything in the success or failure callbacks, the last test will fail with `status == 0`.

This is because AFNetwork processes its response in a background thread, and the final success or failure block callback is dispatched asynchronously from there to a specific queue, which unless provided is the main queue. This means that the block won’t get called until *AFTER* the test code has completed.

Putting in some kind of a lock causes a deadlock, since the test is running on the main thread, and the block callback never gets an opportunity to run. The solution is to manually run the main threads runloop until the callbacks have been processed.

Here’s my solution:

- (void)testRequest
{
    MyHTTPClient* api = [MyHTTPClient sharedInstance]; // subclass of AFHTTPClient
    NSDictionary* parameters = [NSDictionary dictionary]; // add query parameters to this dict.
    __block int status = 0;
    AFJSONRequestOperation* request = [api getPath:@"path/to/test"
                                        parameters:parameters
                                           success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                               // success code
                                               status = 1;
                                               NSLog(@"succeeded");
                                           } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                               // failure
                                               status = 2;
                                               NSLog(@"failed");
                                           }];
    [api enqueueHTTPRequestOperation:request];
    [api.operationQueue waitUntilAllOperationsAreFinished];

    while (status == 0)
    {
        // run runloop so that async dispatch can be handled on main thread AFTER the operation has 
        // been marked as finished (even though the call backs haven't finished yet).
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:[NSDate date]];
    }

    STAssertTrue([request isFinished], @"request finished");
    STAssertEquals(request.response.statusCode, 200, @"request returned 200 OK");
    STAssertEquals(status, 1, @"success block was executed");
}

This addition will continually pump that run loop which allows AFNetwork’s async dispatch of the block to the main queue to execute, and hey presto! We now have a test that can also verify code in the success (or failure) completion blocks of an AFNetwork request operation.

Advertisements

Finally I can hide Gmail’s All Mail folder in IMAP

I don’t know how long this Google Labs feature has been around, but I’m thrilled that now I can hide the “All Mail” folder from Gmail’s IMAP so Mac Mail doesn’t download messages twice!

Some people may not like that you cannot access archived messages from Mac Mail, but I’m happy to go online to do that. Messages I want to keep I move into folders anyway.

Once you enable the Advanced IMAP labs feature, you see this in the Labels settings page:

The Show in Imap checkbox appears when you enable the Google Labs Advanced IMAP feature.

The Show in Imap checkbox appears when you enable the Google Labs Advanced IMAP feature.

It also hides it on my iPhone = yay!

iPhone Accessories, iPhone Cases, iPod Accessories & Cases: XtremeMac

So, my search for a decent iPhone case is over. I wanted one that was:

  • small
  • not sticky (a la rubber / silicone)
  • nice looking
  • cheap

And this is what I found: The Xtrememac Microshield.

It may not be the cheapest, but it’s certainly not the most expensive for what it is…

Try this link (might not work because of stupid Flash based website): iPhone Accessories, iPhone Cases, iPod Accessories & Cases: XtremeMac.

iPhone broke, went to Apple Store, fixed

On Christmas day, my iPhone stopped working. The battery went flat after using it to play music on a speaker dock and it would not start again.

I followed the instructions on Apple’s site for support and was pretty convinced that it was dead.

After a bit of a run around going to the phone shop where I bought it and then ringing some Apple resellers near me, I rang Apple directly for support, which I should have done in the first place.

Made a reservation with the guy on the phone, soonest available was at Chermside 2 days after the call. They had my phone serial on file as still being under warranty, so I didn’t even need to bring the original packaging or proof of purchase.

I turned up, waited a little for the appointment because they were about 10 minutes behind schedule, then explained the situation. The guy had a quick look and whipped out a replacement unit. AWESOME!

I had to sign a disclaimer that they would not be reponsible for any data loss, but since everything is backed up on the computer by iTunes and all my email/addressbook/calendar is synced with MobileMe I knew I’d lose nothing anyway.

I setup my mobile me account in the phone and it started downloading all my contacts and calendars using Apple’s free Wifi connection in the store.

And when I got home, I plugged it in to my Mac. iTunes recognised the same SIM card was in a new phone and asked if the phone should be setup as a new phone, or restored from my old one. I hit restore and after about 20 minutes or so, everything was back the way it was. Everything. Even old text messages.

So, thank you Apple for having such great support. It really was a painless process to go through. 10 out of 10!

iPhone tethering: My fridge, my beer.

Up until now, when you bought a fridge, you could put anything you like in it. And when you bought beer and put it in your fridge, you could do whatever you like with the beer.

Now there’s this new fridge out that’s got a special deal with all the breweries  (like the deal between Apple and Optus) where you have to pay an extra fee if you give the cold beer to a friend.

What’s that? I bought the fridge. I bought the beer. I can put the beer in the fridge and drink it. But I have to pay an extra fee to pass the beer onto my friend? Just because it’s a new fridge? RUBBISH!

And so, Apple’s tethering deal with Optus is clearly a money grab for the convenience of something we’ve always been able to do with other phones. (At least every Nokia phone I’ve ever had with bluetooth could tether data from any access point).

Does anyone know if there’s a petition against this?

What about some regulation that this practice is unfair for consumers? ACCC? TIO? Anyone?

Facebook app crashed my iPhone

The facebook app crashed my iPhone this morning. I was opening a friend’s photo album that they had posted… and the music started skipping and then it just hung.

I know Optus’s data connection isn’t the best, but that shouldn’t stop the whole phone from working. I think it’s a buggy app. It’s always telling me I have unread messages when I don’t and generally clunky.

Lucky for me I managed to guess the reset combination… press and hold both button simultaneously for about 10 seconds.

Lifecycle ride – Nudgee Loop

Here’s my bike ride from this morning, recorded using instamapper for iphone.

http://www.instamapper.com/trk?key=11265370479177707018

For some reason, I can’t put iframes in wordpress….