Decoding Long Long Integer Values in Objective-C

Decoding Long Long Integer Values in Objective-C

In the interest of saving someone else some grief, here’s how to decode long long (64-bit) integers in Objective-C:

long long veryLong = 0;

[[NSScanner scannerWithString:@"100001621738699"] scanLongLong:&veryLong];

NSLog(@"Checking '100001621738699': %qi", veryLong);

One usually runs into long longs when dealing with Facebook Ids. I’ve written on this before, here.

Happy Coding.

Sorting NSMutableArrays

Sorting NSMutableArrays

For whatever reason, sorting arrays in Objective-C perplexes a great many budding iOS developers.

However, it can be entirely straightforward.

The example below takes a mutable array (in this case, a set of businesses) and sorts them either by a point value or by a distance value, depending upon the state of a button from the UI.

The heavy lifting is all done by the comparison functions compareProximity and comparePoints.

The code is fairly self explanatory. I’ve written the comparison functions in a way as to be totally explicit about what values need to be returned.

Pay particular attention; the comparison function comparePoints is written to sort the array in descending point order; compareProximity sorts the array in ascending distance order.

// Compare Result for Sorting by Proximity
NSComparisonResult compareProximity(Business *b1, Business * b2, void * context) {
	if (b1.distance<b2.distance) {return NSOrderedAscending;}
	if (b1.distance>b2.distance) {return NSOrderedDescending;}
	if (b1.distance==b2.distance) {return NSOrderedSame;}

	return NSOrderedSame;
}

// Compare Result for Sorting by Points, Descending
NSComparisonResult comparePoints(Business *b1, Business * b2, void * context) {
	if (b1.points<b2.points) {return NSOrderedDescending;}
	if (b1.points>b2.points) {return NSOrderedAscending;}
	if (b1.points==b2.points) {return NSOrderedSame;}

	return NSOrderedSame;
}

// React to a button push from the screen
-(IBAction)doSort:(id)id {
	UIButton* btn = (UIButton*)id;

        // Toggle button on display
	if (btn.tag==0) {
		[btn setImage:[UIImage imageNamed:@"points_image.png"] 
                        forState:UIControlStateNormal];
		btn.tag=1;
		}
	else {
		[btn setImage:[UIImage imageNamed:@"proximity_image.png"]
                        forState:UIControlStateNormal];
		btn.tag=0;
		}

        // Order Elements (Perform the Sort)
	[self orderElements];

        // Refresh the data in UITableView
	[self.table reloadData];
}

// Perform the sort, depending upon button state
-(void)orderElements {
	if (self.sortButton.tag==1) {
		[self.businesses sortUsingFunction:compareProximity context:NULL];
	        }
	else {
		[self.businesses sortUsingFunction:comparePoints context:NULL];
	        }
}
Download and Play a WAV file… using Objective-C

Download and Play a WAV file… using Objective-C

A client had a question this morning about loading a WAV file over the web and playing it from within an iPhone app.

Turns out it’s not too difficult to do:

// Grab the sound we wish to play from across the web
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@”http://www.dailywav.com/0510/worstSunburn.wav”%5D%5D;

// Load the data into an AVAudioPlayer object
AVAudioPlayer * theAudio = [[AVAudioPlayer alloc] initWithData:data error:NULL];

// Play the sound file
[theAudio play];

Easy, Peasy.

The Right Way… and The Best Way

The Right Way… and The Best Way

If you’ve ever wanted to put a custom image into the title bar of an iPhone app, no doubt you’ve used – or at least run across – code similar to the following:


UIImage * titleImage = [UIImage imageNamed:@"nav_bar_background.png"];
UIImageView * titleImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 42)];
titleImageView.image = titleImage;
titleImageView.contentMode = UIViewContentModeScaleAspectFit;
titleImageView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
[self.myBar addSubview:titleImageView];
[titleImageView release];

And this is a very workable solution.

It’s just not the BEST solution.

The problem with this approach is that if the navigation controller pushes another UIView on the stack, odds are the UIImageView is going to cover your navigation buttons.  Even if the title background image is sent to the back of the z-order.

Double-plus un-good.

The optimal approach to solving this is to implement a category for the UINavigationBar.

What’s a category?

It’s a handy way to extend the capabilities of a built in class without fully having to subclass the whole darn enchillada.  Technically, categories permit a programmer to add methods to an existing class without the need to recompile that class or even have access to its source code.

Take a look:

// UINavigationBar catergory for painting background image in a way that doesn't hide nav buttons.

@implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: @"nav_bar_background.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}

@end

Include this little nugget in your project, and voila – you have a navigation bar image background that works in any orientation and doesn’t hide navigation buttons.

Enjoy.

Posting Photos using Objective-C

Posting Photos using Objective-C

Or more specifically, posting photos to tweetPhoto.com… but the method works for posting any image from an iPhone to any site that can accept POSTed images.

You can find the API documentation for tweetPhoto here.

Since using NSURLConnection and NSMutableRequest are covered numerous places elsewhere, I’m not going to describe much about how to make asynchronous HTTP calls.  The code below pretty much speaks for itself.

The interesting parts of this function (sendPhoto:tweet:photo:tags:longitude:latitude) are the portions involved in composing the HTTP form data to go over the wire.  Again, the code is fairly self explanatory.

The inbound (NSData*)photo should be the binary image of a picture that you will be sending to tweetPhoto (or whatever service you’re posting images to).  Please note that you will need to Change the Content-Type to match whatever images you’ll be sending (in my case, I always send .PNG images).

So, without further ado, here’s the deal-ee-oh:

-(void)sendPhoto:(NSString*)message tweet:(BOOL)tweet photo:(NSData*)photo tags:(NSString*)tags longitude:(float)longitude latitude:(float)latitude {
TweetPhotoAppDelegate* myApp = (TweetPhotoAppDelegate*)[[UIApplication sharedApplication] delegate];

NSString *url;
if (tweet) {
url = [NSString stringWithFormat:@"http://www.tweetphoto.com/uploadandpostapiwithkey.php"];
} else {
url = [NSString stringWithFormat:@"http://www.tweetphoto.com/uploadapiwithkey.php"];
}

NSString * boundary = @"tweetPhotoBoundaryParm";
NSMutableData *postData = [NSMutableData dataWithCapacity:[photo length] + 1024];

NSString * userNameString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"username\"\r\n\r\n%@", myApp.loginString];
NSString * passwordString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"password\"\r\n\r\n%@", myApp.passwordString];
NSString * apiString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"api_key\"\r\n\r\n%@", apiKey];
NSString * messageString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"message\"\r\n\r\n%@", message];
NSString * tagsString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"tags\"\r\n\r\n%@", tags];
NSString * latString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"latitude\"\r\n\r\n%f", latitude];
NSString * longString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"longitude\"\r\n\r\n%f", longitude];
NSString * boundaryString = [NSString stringWithFormat:@"\r\n--%@\r\n", boundary];
NSString * boundaryStringFinal = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary];

[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[userNameString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[passwordString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[apiString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];

if (message != nil && ![message isEqualToString:@""]) {
[postData appendData:[messageString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];
}

if (tags != nil && ![tags isEqualToString:@""]) {
[postData appendData:[tagsString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];
}

if (longitude && latitude) {
[postData appendData:[latString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[longString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[boundaryString dataUsingEncoding:NSUTF8StringEncoding]];
}

[postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"media\";\r\nfilename=\"media.png\"\r\nContent-Type: image/png\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:photo];
[postData appendData:[boundaryStringFinal dataUsingEncoding:NSUTF8StringEncoding]];

NSMutableRequest * theRequest=(NSMutableURLRequest*)[NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

[theRequest setHTTPMethod:@"POST"];

[theRequest addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
[theRequest addValue:@"www.tweetphoto.com" forHTTPHeaderField:@"Host"];
NSString * dataLength = [NSString stringWithFormat:@"%d", [postData length]];
[theRequest addValue:dataLength forHTTPHeaderField:@"Content-Length"];
[theRequest setHTTPBody:(NSData*)postData];

NSURLConnection * theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if (theConnection) {
receivedData=[[NSMutableData data] retain];
}
else {
[myApp addTextToLog:@"Could not connect to the network" withCaption:@"tweetPhoto"];
}
}

Objective-C and HTTP Basic Authentication

Objective-C and HTTP Basic Authentication

For all the really nice stuff Objective-C lets you do on the iPhone, there are many, many holes left for very common tasks.

One of those tasks is Base64 encoding.

Base64 encoding is used to convert binary data into text that can be transmitted using HTTP (like, say, for embedding images in email) or for somewhat obfuscating user ids and passwords to be sent “in the clear” over HTTP (like for Basic Authentication).

Without much further ado, I present one of a gagillion implementations of Base64 encoding in C:

static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/";

int encode(unsigned s_len, char *src, unsigned d_len, char *dst)
{
unsigned triad;

for (triad = 0; triad < s_len; triad += 3)
{
unsigned long int sr;
unsigned byte;

for (byte = 0; (byte<3)&&(triad+byte<s_len); ++byte)
{
sr <<= 8;
sr |= (*(src+triad+byte) & 0xff);
}

sr <<= (6-((8*byte)%6))%6; /*shift left to next 6bit alignment*/

if (d_len < 4) return 1; /* error - dest too short */

*(dst+0) = *(dst+1) = *(dst+2) = *(dst+3) = '=';
switch(byte)
{
case 3:
*(dst+3) = base64[sr&0x3f];
sr >>= 6;
case 2:
*(dst+2) = base64[sr&0x3f];
sr >>= 6;
case 1:
*(dst+1) = base64[sr&0x3f];
sr >>= 6;
*(dst+0) = base64[sr&0x3f];
}
dst += 4; d_len -= 4;
}

return 0;

}

And here is how one may transmit a user id and password (using NSURLConnection) to establish a connection using Basic Authentication:

myApp.loginString    = (NSMutableString*)[[NSUserDefaults standardUserDefaults] stringForKey:kloginKey];
myApp.passwordString = (NSMutableString*)[[NSUserDefaults standardUserDefaults] stringForKey:kpasswordKey];

NSMutableString *dataStr = (NSMutableString*)[@"" stringByAppendingFormat:@"%@:%@", myApp.loginString, myApp.passwordString];

NSData *encodeData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
char encodeArray[512];

memset(encodeArray, '\0', sizeof(encodeArray));

// Base64 Encode username and password
encode([encodeData length], (char *)[encodeData bytes], sizeof(encodeArray), encodeArray);

dataStr = [NSString stringWithCString:encodeArray length:strlen(encodeArray)];
myApp.authenticationString = [@"" stringByAppendingFormat:@"Basic %@", dataStr];

// Create asynchronous request
NSMutableURLRequest * theRequest=(NSMutableURLRequest*)[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.somewebdomain.com"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[theRequest addValue:myApp.authenticationString forHTTPHeaderField:@"Authorization"];

NSURLConnection * theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
else {
[myApp addTextToLog:@"Could not connect to the network" withCaption:@"MyApp"];
}

Hopefully, this will help people get cracking that were having a hard time getting a handle on the fact that Objective-C is really C – albeit with funky class extensions.

Simple Formatting in a Cocoa Touch Text Edit Field (UITextField)

Simple Formatting in a Cocoa Touch Text Edit Field (UITextField)

Several challenges of the XCode / Cocoa Touch development environment involve doing things that are trivial in other places, but no so much so within XCode.

One of those challenges is formatting a text edit field.  Social security numbers, phone numbers, postal codes, VINs… the kind of mundane crap that is uber-trivial in tools like MS Access, but often prove to be ball-busting-ly frustrating to do in Objective-C.

For me particularly, I had a price field that I wanted to display.

Apple supplies a couple of keyboard choices for the iPhone that could have done the trick for supplying numeric data entry, but they had about 40 characters more than I needed (0-9, and decimal point were all I really desired, and being lazy, didn’t want to write a regular expression for parsing).

There is a phone number pad (no good) and a number pad (almost what I need – but no decimal point).  But the number pad would be “good enough” if I could just embed an implied decimal point.

Apple has no built in / native way to format a text field with say an embedded decimal (so I wouldn’t have to type it). I had to write my own handler to take care of this.

Here is my home brewed work around for this situation. I’m not going to focus on setting up UITextField events, only on the problem at hand.

Consider the following Edit Changed code:

// Our price changed... we want to format it with an implied decimal point.
-(void)textFieldPriceDidChange:(id)textField {

static BOOL toggle = NO;
if (toggle) {
toggle = NO;
return;
}

NSString* strPrice = [[textField object] text];

strPrice = [strPrice stringByReplacingOccurrencesOfString:@"." withString:@""];
toggle = YES;
price.text = [@"" stringByAppendingFormat:@"%0.2f", [strPrice floatValue]/100.0];

}

The code assumes that I have a UITextField named price somewhere on a form. When the field’s text is changed, the event fires, and I:

-) Strip out any existing decimal point,
-) Divide the float representation of the value by 100, to give an implied 2 decimal point precision,
-) Use the printf-styled %0.2f formatting string to pretty up the output the way I want,
-) Put the value back in the field.

But wait a minute… won’t putting a value back into the price field trigger yet another Edit Change event, and cause an endless loop?

Yes. It will.

But take a look at the BOOL static toggle.

That toggle essentially says “OK. If I see that I am already handling the formatting of this field, reset the toggle, and return.”

This is a simple but very effective way to add formatting to a text edit field, and the concept can easily be extended to other formats without a lot of sweat (%05d-%04d, %3d-%2d-%4d, %2d-%7d, %2d/%2d/%4d – you get the point).

Enjoy… and happy coding.