PIXEL
DOCK

I like the smell of Swift in the morning…

A little category to get an NSDictionary from an NSString containing a plist

Posted: | Author: | Filed under: iOS, Objective-C | Tags: , , | 2 Comments »

I really like the plist format. Being able to transform a plist file into a NSDictionary with one line of code really appeals to the lazy guy in me. So when our backend developer asked me what my preferred format for server responses would be, I told him to send his responses in plist format. That’s when I came across a little problem: When accessing the response body of the request, all I got was a NSString. The string contained the plist, but it was still a string and not the usual XML structure that you normally get when you open a plist file.

I found a way to parse a NSString into a NSDictionary on StackOverflow (thanks to Peter N Lewis for his answer).

So I decided to write a little category to extend NSDictionary so that it can initialize a NSDictionary with a plist NSString:

@interface NSDictionary (DictionaryWithString)
   + (NSDictionary *)dictionaryWithString:(NSString *)string;
@end

#import "NSDictionary+DictionaryWithString.h"

@implementation NSDictionary (DictionaryWithString)

+ (NSDictionary *)dictionaryWithString:(NSString *)string {
   NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
   NSString *error;
   NSPropertyListFormat format;
   NSDictionary *dict = [NSPropertyListSerialization 
      propertyListFromData:data 
      mutabilityOption:NSPropertyListImmutable 
      format:&format 
      errorDescription:&error];
   if(!dict){
      NSLog(@"ERROR: could not parse NSString: %@",error);
      [error release];
   }
   return dict;
}

@end

Feel free to use it in your own projects.

Embed Javascript files into your iOS App

Posted: | Author: | Filed under: iOS, Xcode | Tags: , | 1 Comment »

When i tried to compile a Javascript file into my iOS App I ran into a strange problem. The javascript files just were not embedded. Whenever I tried to access them using this method:

[[NSBundle mainBundle] pathForResource:@"script.js" ofType:nil];

The method always returned nil. Somehow the javascript file just did not want to be embedded.

At first I could not find out what I was doing wrong, because embedding resources into an App is really easy. Just drag them into your resources folder and you’re done, right? Well, for most file types that’s true. However, with Javascript files things are a bit different:

When compiling the App the compiler treats Javascript files as script files. Meaning that it tries to interpret them instead of just including them into the App bundle uninterpreted.

Here’s how to make the compiler turn a blind eye on the javascript files and just include them into the App bundle without interpreting them:

1. In Xcode’s project navigator (left column) klick on the blue project icon.
2. Select the “Build Phases” tab in the main window
3. Open the “Compile Sources” section
4. Find your Javascript file in the list and remove it.
5. Open the “Copy Bundle Resources” section
6. Add your Javascript file to the list

And that’s it. Now the compiler treats your Javascript file like any other resource file and copies it into the App Bundle.

UPDATE:
Xcode 4.5 now creates a warning when you just add a javascript file to your project without removing it from the “Compile Sources” section:

warning: no rule to process file '*FILEPATH*' of type 
sourcecode.javascript for architecture armv7

So the problem is easier to spot now. You still have to fix it manually as I described above.

How to compare two NSDecimalNumber values

Posted: | Author: | Filed under: iOS, Objective-C | Tags: , , | No Comments »

Recently I had to use NSDecimalNumber objects to avoid the rounding errors that come with float values. While comparing two float values is as easy as it gets comparing two NSDecimalNumbers works a bit different:

NSDecimalNumber is a subclass of NSNumber so you have to use NSNumber’s compare: method to compare the values:

NSDecimalNumber *number1 = [NSDecimalNumber numberWithFloat:0.1];
NSDecimalNumber *number2 = [NSDecimalNumber numberWithFloat:0.2];

// check if number1 is less than number2
if ([number1 compare:number2] == NSOrderedAscending {
   NSLog(@"number1 is less than number2")
}

CoreData error: “The model used to open the store is incompatible with the one used to create the store”

Posted: | Author: | Filed under: iOS, Xcode | Tags: | 4 Comments »

When I started to play around with CoreData for the first time, I came across a strange error. The error description said “The model used to open the store is incompatible with the one used to create the store”. At first I just couldn’t figure out what the problem was. As soon as I tried to access the NSManagedObjectContext the app crashed.

The solution is really simple: Whenever you change something your CoreData data model, you have to delete the old compiled app from the simulator or device. CoreData uses SQLite3 which is nothing more than a file in your app’s filesystem. If you do not delete the old version of your app the file remains in the file system and does not reflect the changes that you made in the data model. The SQLite file will only be deleted when you delete the whole app.

So the golden rule is: Whenever you change something in the data model of your CoreData driven App, make sure to delete the old version of the app before compiling and running the new one.

How to avoid a blocked user interface during UIView animations with blocks

Posted: | Author: | Filed under: iOS | Tags: , , | No Comments »

When you use the new iOS 4 UIView animations with blocks you will notice that the animations will lock user interaction while they are running. With really short animations this might be uncritical but it is surely unacceptable when a user cannot scroll a UIScrollView because another UIView is being animated with a longer duration like 2 seconds.

The reason for this “frozen UI problem” lies in the default value for the options mask of UIView’s animateWithDuration: methods. Apparently the option UIViewAnimationOptionAllowUserInteraction is not part of the default options mask. Unfortunately only one of the three animateWithDuration: methods allows to set the animation options. So to avoid the freezing of the user interface use animateWithDuration:delay:options:animations:completion: like in this example where a view is faded out over the duration of two seconds without blocking the user interface:

[UIView animateWithDuration:2.0 
        delay:0.0 
        options:UIViewAnimationOptionAllowUserInteraction 
        animations:^(void){yourview.alpha = 0.0} 
        completion:^(BOOL completed){yourview.hidden = YES;}];

Set the height of a UIWebView to the height of it’s HTML content

Posted: | Author: | Filed under: iOS, Objective-C | Tags: , | 19 Comments »

Sometimes you need to know the height of a html document that is loaded into a UIWebView. For example if you want to set the height of the UIWebView to the height of its content.

The logical way would be this:

1. add a Javascript function to the HTML that returns the height of the document.
2. add the call to this Javascript function to the UIWebViewDelegate’s method webViewDidFinishLoad:

This sounds easy, but if you look at the results you’ll realize that the values for the document height are not correct and pretty random.

The problem is: webViewDidFinishLoad: get’s called when the HTML is fully loaded BUT it still has to be fully rendered before you can determine its height!

So the call comes too early. You could delay the call but that’s not the way to go here as this is still unreliable and you want the height as soon as possible.

The solution is to revert the process. Instead of Objective-C asking the Javascript for the height, have the Javascript call Objective-C as soon as it knows the height of the document.

Here’s how to do it:

1. Add a Javascript function to the HTML document

Add this function either to the head or the body of your HTML

This function gets called as soon as the HTML document is fully rendered. It puts the height into an URL and sends a request with this URL.
If you are asking yourself why I use “ready://” instead of “http://”: I do this because sending a request is the only way, how the Javascript inside a UIWebView can send messages to the UIWebViewDelegate. So this is not a “real” HTTP Request. Instead you can use the URL scheme to make things easier on the Objective-C part (as you will see in step 2).

2. Intercept the request in the UIWebView’s delegate

A UIWebViewDelegate has a method, that get’s called everytime the HTML inside the UIWebView sends a request. Here’s what to do in this method:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { 
  NSURL *url = [request URL]; 
  if (navigationType == UIWebViewNavigationTypeOther) { 
    if ([[url scheme] isEqualToString:@"ready"]) { 
      float contentHeight = [[url host] floatValue]; 
      CGRect fr = webview.frame; 
      fr.size = CGSizeMake(webview.frame.size.width, contentHeight); 
      webview.frame = fr; 
      return NO; 
    } 
  }
  return YES; 
}

Here you can see why I used the custom URL scheme “ready”. It makes it easy to identify my request. The URL that the javascript requested has the format “ready://1200” meaning that the HTML content is 1200px high. So I use the scheme “ready” to identify my request and the “host” part of the URL to sent the actual height. Then it’s easy to set the height of the UIWebView to the height of the HTML document’s content.

Return NO to stop the UIWebView from trying to load the request. Don’t forget to return YES for all other requests or the HTML content won’t even get loaded into the UIWebView in the first place.

Build an ad hoc distribution .ipa file for a project that includes static libraries (e.g. Open Ears)

Posted: | Author: | Filed under: iOS, Xcode | Tags: | No Comments »

When I wanted to generate an .ipa file for my latest project I ran into the following problem: my project uses the Open Ears library for speech recognition. Open Ears is included as a static library. When I tried to archive the app to share it with others I could not create the .ipa file. After clicking on “share” the option to save it as an .ipa file was grayed out with the message “No Packager exists for the type of archive”. So no chance to build the necessary .ipa file that is needed for ad hoc distribution.

The solution for this problem is quite simple but it took me some time to find it. The problem is that you have to make sure that the static libraries are included in the application binary. You achieve this by setting the “Skip Install” build setting to “YES” for the libraries: Right click on the library project icon (the blue one) and find the “Skip Install” property in the “Deployment category” in the build settings. Set it to “YES”.

Important: leave the “Skip Install” property of your main project at “NO”.

When you archive your project now, it should offer the save as .ipa option in the share section.

UIColor+colorWithHex: a category to get an UIColor from a hexadecimal integer value or string

Posted: | Author: | Filed under: iOS, Objective-C | 5 Comments »

I always find it really cumbersome to instantiate an UIColor object from a hexadecimal color value. To make life a bit easier I wrote this category for the UIColor class:

@interface UIColor (ColorWithHex) 

+(UIColor*)colorWithHexValue:(uint)hexValue andAlpha:(float)alpha;
+(UIColor*)colorWithHexString:(NSString *)hexString andAlpha:(float)alpha;

@end

@implementation UIColor (ColorWithHex)

+(UIColor*)colorWithHexValue:(uint)hexValue andAlpha:(float)alpha {
    return [UIColor  
                colorWithRed:((float)((hexValue & 0xFF0000) >> 16))/255.0 
                green:((float)((hexValue & 0xFF00) >> 8))/255.0 
                blue:((float)(hexValue & 0xFF))/255.0 
                alpha:alpha];
}

+(UIColor*)colorWithHexString:(NSString*)hexString andAlpha:(float)alpha {
    UIColor *col;
    hexString = [hexString stringByReplacingOccurrencesOfString:@"#" 
                           withString:@"0x"];
    uint hexValue;
    if ([[NSScanner scannerWithString:hexString] scanHexInt:&hexValue]) {
        col = [self colorWithHexValue:hexValue andAlpha:alpha];
    } else {
        // invalid hex string         
        col = [self blackColor];
    }
    return col;
}

@end

Because I often need to convert a hex string (e.g. from a parsed XML file) to an UIColor I added 2 methods to this category. You can now get an UIColor from a hexadecimal uint value or an NSString that can have the 3 following formats: “0xFFFFFF“, “#FFFFFF” or “FFFFFF“.

Just add this category to your project and you can use it like this:

UIColor *col1 = [UIColor colorWithHexValue:0xFFFFFF andAlpha:1.0];
UIColor *col2 = [UIColor colorWithHexString:@"0xFFFFFF" andAlpha:1.0];

You can download the category here.

Feel free to use it.

NSURLConnection: How to avoid blocked file loading during scrolling of UIScrollView or UITableView

Posted: | Author: | Filed under: iOS | Tags: , , | 10 Comments »

When you want to load a file (like a large image) into your app, you don’t want to block the user interface during the loading. It’s always a good idea to load large files asynchronously.

The easiest way to load a file in the background is via NSURLConnection.

NSURLConnection *connection = [[NSURLConnection alloc] 
     initWithRequest:request delegate:self startImmediately:NO];
[connection start];

However, if you use the default settings the connection object will be scheduled in the NSDefaultRunLoopMode. That means that the connection is only executing the request when the app’s run loop is in NSDefaultRunLoopMode.

Now, when a user touches the screen (e.g. to scroll a UIScrollView) the run loop’s mode will be switched to NSEventTrackingRunLoopMode. And now, that the run loop is not in NSDefaultRunMode anymore, the connection will not execute. The ugly effect of that is, that the download is blocked whenever the user touches the screen. And that can be a looong time when the user is scrolling a UITableView, because the download is stopped until the scrolling completely stops. And when the user continues to scroll, the download is blocked again.

Fortunately the solution to this problem is quite simple: You can schedule the connection in another NSRunLoopMode. When you schedule the connection in NSRunLoopCommonModes it will execute in all run loop modes (that have been declared as a member of the set of “common” modes, to be precise). That means that the connection is not only working in NSDefaultRunLoopMode but also in NSEventTrackingRunLoopMode (when the user touches the screen).

[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] 
     forMode:NSRunLoopCommonModes];

Bingo! The async file loading is not blocked anymore during user interaction.

Xcode: Dividing long NSString constants over multiple lines

Posted: | Author: | Filed under: Objective-C, Xcode | Tags: | No Comments »

Just a little Objective-C “trick” to make long NSString constants a little bit more readable in your Code:

To break a part of the long string to a new line you have to wrap it in quotation marks. The compiler will then put it back together to one long string.

So instead of doing this:

NSString *htmlStr = @"PageTitle

Hello World

";

You could do this:

NSString *htmlStr = @""
                       ""
                          "PageTitle"
                       ""
                       ""
                          "

Hello World

" "" "";

Of course the cleanest thing to do would be to separate the html code completely from your Objective-C code by putting it into a separate HTML file and load that into a string. But hey, who wants to be perferct all the time?