Synchromation - Code

Avoiding image re-compression on cellular networks

One problem encountered when trying to upload images programmatically to an app over the cellular network is that some networks dynamically re-compress images uploaded over their networks. This means that the quality of JPEG images can be significantly degraded, with visible artefacts.

This problem is all the more subtle, as it will not be detected when developing an app in the simulator due to the fact that the development platform is unlikely to be connected to a cellular network.

Luckily, re-compression can be avoided by the inclusion of simple HTTP header parameters...


request = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: URL]
cachePolicy: NSURLRequestReloadRevalidatingCacheData
timeoutInterval: 60.0f];

// Add some header parameters to ensure that images are not compressed over a mobile network
[request addValue: @"no-cache"
forHTTPHeaderField: @"Pragma"];

[request addValue: @"no-cache"
forHTTPHeaderField:@"Cache-Control"];

NSData *imageData = [NSURLConnection sendSynchronousRequest: request
returningResponse: &response
error: &error];
// Convert data to an image (if we were successful)
if ((imageData != nil) && (error == nil))
{
originalImage = [[[UIImage alloc] initWithData: imageData] autorelease];
}

Please note that for the sake of brevity, error checking and garbage collection has been omitted in this example.
Comments

Using Accessorizer with Xcode 4

Accessorizer is one of our favourite tools at Synchromation as it allows you to avoid some of the dull side of Objective-C development by helping automate the creation of @property and @synthesize statements (and it can be customised to conform to a wide range of coding styles).

Unfortunately, it does not seamlessly work with Xcode 4 as the Accessorizer key bindings are now used by Xcode 4 itself. Luckily, this can be simply solved by re-assigning new shortcuts in the Xcode services menu.



You can get Accessorizer here
Comments

Signing Amazon CloudFront REST Requests

Whilst creating signed REST requests for Amazon S3 services can be a complicated affair as detailed here, signing for CloudFront is a far simpler and can be performed by a simple category on NSMutableURLRequest.

Call the method just before sending a synchronous (or indeed asynchronous) request.

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: cloudFrontURL];
[request signRequestForCloudFront];

Category...
@implementation NSMutableURLRequest (CloudFront)
- (void) signRequestForCloudFront
{
// Set Date header to date string in one of the RFC 2616 formats
// e.g. "Tue, 27 Mar 2007 21:06:08 +0000"
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat: @"EEE, d MMM yyyy HH:mm:ss zzzz"];
NSString *date = [dateFormatter stringFromDate:[NSDate date]];

[self addValue: date forHTTPHeaderField: @"Date"];

// For cloudfront the signature is calculated purely from the date sting only
NSString *signature = [self base64forData:[self HMACSHA1withKey: secretAccessKey forString: date]];

NSString *authorization = [NSString stringWithFormat:@"AWS %@:%@", accessKey, signature];

[self addValue: authorization forHTTPHeaderField: @"Authorization"];
}
€end

Please note this snippet requires a Base64 encoder and the SHA-1 hashing code from All-Seeing's asi-http-request on GitHub
Comments

Using Categories with UIColor

One way of globally caching frequently used UIColor values can be by using a category on UIColor.

This allows code such as..
aLabelView.textColor = [UIColor lightBeigeColor];

This is especially useful when optimising cellForRowAtIndexPath in a UITableView where all the subviews of the UITableViewCell being returned are being created programmatically for speed.

Example header file...
//  Add a category on to UIColor to add some of our own regularly used colours
#import <UIKit/UIKit.h>
@interface UIColor (UIColor_SDJColor)
+ (UIColor *) lightBeigeColor;
+ (UIColor *) darkBeigeColor;
@end

Example source file...
#import "UIColor+SDJColor.h"
@implementation UIColor (UIColor_SDJColor)
+ (UIColor *) lightBeigeColor;
{
static UIColor *lightBeige = nil;
// If we haven't already initialised the colour, then do it now
if (!lightBeige) {
lightBeige = [UIColor colorWithRed: 0.925f
green: 0.902f
blue: 0.835f
alpha: 1.000f];
[lightBeige retain];
}
return lightBeige;
}

+ (UIColor *) darkBeigeColor;
{
static UIColor *darkBeige = nil;
// If we haven't already initialised the colour, then do it now
if (!darkBeige) {
darkBeige = [UIColor colorWithRed: 0.875f
green: 0.839f
blue: 0.723f
alpha: 1.000f];
[darkBeige retain];
}
return darkBeige;
}
@end

This method can also be used to cache frequently used objects with other classes such as UIFont and UIImage where otherwise repeated calls could be expensive in terms of CPU time.
Comments

Detecting if an iPhone can Mutlitask

Whilst iOS 4 can be installed on a wide variety of devices, not all of the functionality will be available on all devices. One example is that multi-tasking (which is key to allowing the latest generation of radio streaming apps to play audio in the background) is not available on the iPhone 3G (although it is available on the iPhone 3GS & iPhone 4).

To correctly detect whether multitasking is available, we need first to check if iOS 4 (or above) is running on the iPhone, which can be done by detecting the currentDevice supports to the isMultitaskingSupported method. If successful, the presence of multi-tasking can be determined by calling that method.

// Detect if the iOS version is >= iOS 4.0 and the device supports multitasking
if ([[UIDevice currentDevice] respondsToSelector: @selector(isMultitaskingSupported)]
&& [[UIDevice currentDevice] isMultitaskingSupported] == TRUE) {
NSLog (@"Multitasking capable device detected");
}
else {
NSLog (@"Non-multitasking capable device detected");
}


Comments