Web Services and Unicode

An application that I’m working on uses a SOAP web service to send user registration data to a server, which I implemented using Web Services Core Framework. After the user enters his details, I create a SOAP request to send it to the server:


	NSDictionary* headers = [NSDictionary dictionaryWithObjectsAndKeys: soapaction, @"SOAPAction",  "text/xml; charset=utf-8", @"Content-Type", nil];

	fRef = WSMethodInvocationCreate((CFURLRef) url, (CFStringRef) method, (CFStringRef) kWSSOAP2001Protocol);
	WSMethodInvocationSetProperty(fRef, kWSHTTPExtraHeaders, headers);
	WSMethodInvocationSetProperty(fRef, kWSSOAPBodyEncodingStyle, kWSSOAPStyleDoc);
	WSMethodInvocationSetProperty(fRef, kWSSOAPMethodNamespaceURI, Namespace);
        WSMethodInvocationSetParameters(fRef, (CFDictionaryRef)params, nil);
	fResult = (NSDictionary*)WSMethodInvocationInvoke(fRef);

This works perfectly unless the user enters multi-byte Asian characters. I immediately suspected that I was assuming somewhere that characters are one byte, but it turned out to be crashing deep inside CFNetwork code.

Read more

Useful XCode tricks

XCode provides several ways to make your work easier by cutting down on repetitive typing. The simplest (from a usage standpoint) is text macros. You simply type an abbreviation and hit Escape to substitute it. A text macro can have variable substitutions where you can add text.

I’ve seen a few contradictory articles telling how to add your own text macros, which don’t work in XCode 3.1, but here’s how I was able to get it to work.

First, open /Developer/Applications/Xcode.app/Contents/PlugIns/ and look for TextMacros.xctxtmacro. Make a copy of TextMacros.xctxtmacro and place it in ~/Library/Application Support/Developer/Shared/XCode/Specifications. You may need to create the Specifications folder if it doesn’t exist.

In the copy of TextMacros.xctxtmacro, go to Contents/Resources and edit any of the .xctxtmacro files such as C.xctxtmacro (anything defined there also works in C++ and Objective-C). Any changes here will override the version in XCode. If you only modify one of the macro files, you can delete the others from the copy.

My development team is supposed to add a standard copyright header to each source file. I created a macro that allows me to type ‘stdcopy’ and have it expand to the entire header, with highlighted items that I can tab to and add text. I also added a ‘history’ macro which adds a history comment below the standard copyright block.

For those two macros, I added the following definitions to C.xctxtmacro:

        {
            Identifier = c.stdcopy;
            BasedOn = c;
            IsMenuItem = YES;
            Name = "Standard Copyright Header";
            TextString = "//---------------------------------------------------------------------\n// Copyright (c) $(YEAR) $(ORGANIZATIONNAME).  All rights reserved.\n// Reproduction or transmission in whole or in part, in any form or\n// by any means, electronic, mechanical or otherwise, is prohibited\n// without the prior written consent of the copyright owner.\n//\n// < #summary#>\n//\n// < #remarks#>\n//\n// Date\t\t\tRelease\t\t\tTask\t\tWho\t\tSummary\n// =====================================================================\n// < #date#>\t\t< #release#>\t\t< #task#>\t\t< #who#>\t\t< #summary#>\n//---------------------------------------------------------------------\n";
            CompletionPrefix = stdcopy;
            IncludeContexts = ( "xcode.lang.c", "xcode.lang.java" );    // this works in Java too
        },

        {
             Identifier = c.history;
             BasedOn = c;
             IsMenuItem = YES;
             Name = "History Comment";
             TextString="// < #date#>\t\t< #release#>\t\t< #task#>\t\t< #who#>\t\t< #summary#>\n";
             CompletionPrefix = history;
             IncludeContents = ( "xcode.lang.c", "xcode.lang.java" );
        },

Scripts, which are listed in the script menu, are more powerful & flexible since they can use shell scripts, AppleScript, Perl, Ruby, or any other scripting language.

Since I’m working with CFPlugin bundles, I need to deal with UUIDs. In most cases, you would use CFUUIDGetConstantUUIDWithBytes to specify a UUID, which requires a list of byte values. The shell command ‘uuidgen’ returns a string, so converting it for use with CFUUIDGetConstantUUIDWithBytes involves some tedious editing. With a script, you can simply insert the properly formatted UUID byte values.

Here’s my ‘Insert UUID as bytes’ script:

#! /usr/bin/perl -w
my $re = "\(..\)\(..\)\(..\)\(..\)-\(..\)\(..\)-\(..\)\(..\)-\(..\)\(..\)-\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\n";
my $uuid = `uuidgen`;

my $bytes = $uuid;
$bytes =~ s/$re/0x$1, 0x$2, 0x$3, 0x$4, 0x$5, 0x$6, 0x$7, 0x$8, 0x$9, 0x$10, 0x$11, 0x$13, 0x$14, 0x$15, 0x$16/;

print "// UUID: ".$uuid;
print "CFUUIDGetConstantUUIDWithBytes(NULL, ".$bytes.")";

I’m sure there’s a more elegant way to do it in a single line of Perl 🙂

Languages & Locales are different

If you’re writing a Mac or iPhone application that supports localization, you should be aware that the language & locale could be different. The preferred language can be changed in the International preference pane by changing the order of languages in the list.

International.jpg

However, when you change the language order, it doesn’t change the locale, which defines the date, time, and currency formats.

In your application, you can find the preferred language using this bit of code (note that I’m using functions that are available in 10.3 or later):

CFArrayRef locs = CFBundleCopyBundleLocalizations(CFBundleGetMainBundle());
CFArrayRef preferred = CFBundleCopyPreferredLocalizationsFromArray(locs);
CFStringRef lang = CFLocaleCreateCanonicalLocaleIdentifierFromString(NULL,CFArrayGetValueAtIndex(preferred, 0));

With French selected as the preferred language, as shown above, it will return ‘fr’ as expected. However, you’re working with a web service that takes a locale name instead of a language, so you do something like this to obtain the locale:

CFLocaleRef locale = CFLocaleCopyCurrent();
CFStringRef lstr = CFLocaleGetIdentifier(locale);

The locale you get is ‘en_US’, which may seem counter-intuitive. If you pass that to your web service, instead of French language text, it will appear in English.

Sigma 10-20mm Lens

Today I got my first wide angle lens, a Sigma 10-20mm f/4-5.6D EX DC HSM. I’ve been very impressed with the quality of Sigma lenses since I got the 70-300mm and this lens didn’t disappoint me. The 10-20mm is very sharp and the auto focus is extremely fast and quiet.

Sigma 10-20mm lens

Since most of my lenses are longer zooms, this one gives me a new perspective. It should be great for landscape photography.

DSC_4400

Both of these shots were taken from the same spot at 20mm and 10mm.

DSC_4404

DSC_4403

iDjembe 2.0 submitted

I just submitted iDjembe 2.0 to the app store. I decided to change the version number to 2.0 because I did a major rewrite of the audio player code.

For the first version, I just wanted to make something quick that hopefully would cash in on the app craze and make some money. This time I’m serious about making it good.

The major new feature is Virtual Drum Circle, which lets you connect to other players on the local network and broadcast your beats to them.

The major under the hood change is that it now uses OpenAL instead of the simple audio player (which pretty much sucked). I tried several sound APIs including Audio Queue and AVAudioPlayer, but I found that OpenAL is the most straightforward and gives the best performance.

Unfortunately Apple’s iPhone developer documentation doesn’t cover OpenAL, although there are several code samples that use it. I found the best information to be at Ben Britten’s blog and the official OpenAL site.

After some experimentation, I determined the best strategy is to use a pool of OpenAL sources and prepare a buffer for each sound. Instead of using a single source for each sound, when I need to play a sound, I get the next available source, and add the desired sound buffer to it. As a result, it can now play more than one sound at a time and can play the same one repeatedly without a click.

Simple fix for 1Password & Safari 4

When Safari 4 was released today, a lot of people reported that 1Passsword won’t work with it. Since I rely on 1Password, I was very reluctant to upgrade. The developers of 1Password came up with an easy work around, which must be re-applied with every 1Password update until they officially support Safari 4. I came up with a simple automatic patcher, which you can download here, including full source code.

I Can Has swipe action

I’ve had several reports that swiping no longer works in I Can Has Cheezburger 1.1. A small part of it is that when the image is zoomed, it will not let you swipe to switch images since that same gesture will reposition the enlarged image. Double-tapping to un-zoom will once again let you switch images by swiping.

The real reason is because of how I was detecting a swipe gesture.

In the original version I simply tested for a movement larger than the minimum swipe distance (which was 24 pixels).


float deltaX = fabsf(startTouchPosition.x - currentTouchPosition.x);
float deltaY = fabsf(startTouchPosition.y - currentTouchPosition.y);
// If the swipe tracks correctly.
if (deltaX >= HORIZ_SWIPE_DRAG_MIN)
.... handle swipe action

However, in version 1.1 I also check that the vertical movement is less than the minimum distance.


float deltaX = fabsf(startTouchPosition.x - currentTouchPosition.x);
float deltaY = fabsf(startTouchPosition.y - currentTouchPosition.y);
if (!zoomed && (deltaX >= HORIZ_SWIPE_DRAG_MIN) && (deltaY < = VERT_SWIPE_DRAG_MAX)) .... handle swipe action

I've reverted to the old behavior for the next update, which will support the Cheezburger submission API that isn't ready yet. I may release a small update just for this and a few other tiny fixes rather than wait for the API.

Renewing iPhone signing certificates

My iPhone development signing certificate expired yesterday. Thankfully I found that creating a new certificate is quick and painless. Luckily I still have my Certificate Signing Request (CSR).

All you really have to do is upload the CSR. If you don’t have it, you’ll have to create a new one by following the instructions in the certificates tab in ADC’s iPhone center.

After uploading the CSR, you’ll have to approve it by clicking the ‘approve’ button in the certificates tab. In a few minutes, the certificate is ready to download.

iPhone Developer Certificates are expiring

Thanks to Craig Hockenberry for pointing out that iPhone developer & distribution certificates are beginning to expire. I checked my keychain and found that my developer certificate expires tomorrow and my distribution certificate expires on the 25th. I’m not sure if I still have my original certificate signing request (CSR), so I’ll probably have more work to do.

For any other iPhone developers reading this, open Keychain Access, do a search for ‘iPhone’ and note the expiration dates. Don’t be caught by surprise.