Manually Symbolicating iOS Crashlogs

I’ve been facing this problem for as long as i’ve been developing iOS apps. What’s the deal with crashlogs????…..

Apple allows you to download crashlogs through iTunesConnect. What are crashlogs you may say. Well, crashlogs are simply text files that allows us to debug crashes. You get to see the Stack Dump at the moment of the crash, as well as the architecture, and loaded libraries.

What is symbolication??… well, the crashes you download in iTunesConnect aren’t useful, unless you’ve got the dSYM file matching that crashlog. If you do, you’ll be able to symbolicate it. That means that… instead of seeing memory addresses in the stack dump, you’ll get to see method and class names.

You can symbolicate crashlogs using Xcode Organizer. Why would you wanna do it manually?. Because sometimes the crashlogs don’t get symbolicated, and.. if you do it manually, you get access to the verbose mode… and you miiight stand a chance of fixing the glitch that prevents symbolication.

So, let’s begin…. we assume the following:

– You’ve got a horrible .crashlog file.
– You’ve got the dSYM file that matches the binary that crashed.
– You’ve got Xcode 4.3 or superior.

Steps… steps!!

  1. Check and Fix Xcode Path. Fire up a terminal, and type:[cc lang=”bash”]/usr/bin/xcode-select -print-path[/cc]

    If it says anything but ‘/Applications/Xcode.app/Contents/Developer/’, then it’s wrong. In order to fix it, you should type:

    [cc lang=”bash”]sudo /usr/bin/xcode-select -switch /Applications/Xcode.app/Contents/Developer/[/cc]

  2. Locate the ‘symbolicatecrash’ script. You can do so by typing:[cc lang=”bash”]find /Applications/Xcode.app -name symbolicatecrash -type f[/cc]
  3. In your home directory (cd ~), you should edit the .profile file, and type the following:[cc lang=”bash”]
    SYMBOLICATECRASH=”Type the path returned by find”export SYMBOLICATECRASH

    export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer

    PATH=”:${SYMBOLICATECRASH}:${PATH}”
    export PATH[/cc]

That’s it. Now, you should simply go to the folder where the .crash file is located, and you should type…

[cc lang=”bash”]symbolicatecrash NAME.crash[/cc]

If everything goes fine, the stack dump should he human-readable!

Adding support for iPhone 5 screens

Not long ago, i had to extend an app to support iPhone 5. Yes… iOS has autolayout functionality, that should aid the development of UIView’s.

But how about assets?. How do you load assets specially crafted for 568px height screens?. Furthermore.. how do you patch classes that inherit from UITableViewCell, to return dynamic height, according to the device?.

Simple!. By means of a super short UIScreen extension. The header should look like this…:

[cc lang=”objc”]
@interface UIScreen (Lantean)
+(BOOL)isH568;
@end
[/cc]

Write the following snippet in the .m file:

[cc lang=”objc”]
@implementation UIScreen (Lantean)
+(BOOL)isH568
{
static BOOL isH568 = NO;
static dispatch_once_t _once;

dispatch_once(&_once, ^
{
isH568 = ([UIScreen mainScreen].bounds.size.height == 568);
});
return isH568;
}
@end
[/cc]

Fair enough. By checking [UIScreen isH568], you should be able to proceed as required… if the current device has the new 4 inches screen!.

Calculating NSData’s MD5

Suppose that you need to calculate a NSData’s MD5 signature.. for whatever reason. Say… you need to check the backend’s signature.

Well… simply add a NSData extension, with the following method:

[cc lang=”objc”]
– (NSString*)md5
{
unsigned char result[16];
CC_MD5( self.bytes, self.length, result ); // This is the md5 call
return [NSString stringWithFormat:@”%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x”,
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
[/cc]

Again, please, don’t you forget the imports!

[cc lang=”objc”]
#import
[/cc]

Encrypting and Decrypting NSData with AES256

Today we’re gonna encrypt information, stored in a NSData object, using AES-256 algorithm. As you may already know, AES-256 is a symmetric encryption schema. Which means that you need to share, somehow, the key you’re gonna use (in order to decrypt the data).

I’ve encapsulated a couple helper methods, as a NSData extension, for the sake of simplicity. I’ve based myself on this. Let’s see…

[cc lang=”objc”]
– (NSData*)aes256EncryptWithKey:(NSString*)key
{
// ‘key’ should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256 + 1];
bzero( keyPtr, sizeof( keyPtr ) );

// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

NSUInteger dataLength = [self length];

// See the doc: For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
// That’s why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );

size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted );
if( cryptStatus == kCCSuccess )
{
// The returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}

free( buffer );
return nil;
}
[/cc]

And now… the decrypt routine…

[cc lang=”objc”]
– (NSData*)aes256DecryptWithKey:(NSString*)key
{
// ‘key’ should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1];
bzero( keyPtr, sizeof( keyPtr ) );

// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

NSUInteger dataLength = [self length];

// See the doc: For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
// That’s why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );

size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted );

if( cryptStatus == kCCSuccess )
{
// The returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}

free( buffer );
return nil;
}
[/cc]

Ahhhh… don’t you forget about the imports!

[cc lang=”objc”]
#import
#import
[/cc]

Fixing WebKitLocalStorageDatabasePathPreferenceKey crash

I’ve been getting a lot of crashes with the following signature:

[cc lang=”objc”]Exception Type: SIGABRT
Exception Codes: #0 at 0x351be32c
Crashed Thread: 0

Application Specific Information:
*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: WebKitLocalStorageDatabasePathPreferenceKey)’
[/cc]

What is this?. It’s pretty well explained here.
My workaround?. Quite simple. Call this method as soon as your app launches:

[cc lang=”objc”]
-(void)fixWebkitCrash
{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSString* webkitPath = [defaults objectForKey:@”WebKitLocalStorageDatabasePathPreferenceKey”];
NSString* bundlePath = [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent];

if([webkitPath containsString:bundlePath] == NO)
{
[defaults removeObjectForKey:@”WebKitLocalStorageDatabasePathPreferenceKey”];
[defaults synchronize];
}
}
[/cc]

Hopefully, this will save you a couple hours!.