Frame geometry macros to improve your UIKit code

Frame geometryI find myself doing more and more dynamic UI layout in iOS apps these days. When elements of a UI need to resize to fit their contents, or move to accommodate other elements, the layout code can get complex and verbose. We’ve developed a set of Objective-C UIKit macros that help make this code more readable, self-documenting, and easy to change.

When a view is offset from another view, you might write something like this:

someView.frame = CGRectMake(someOtherView.frame.origin.x + someOtherView.frame.size.width + padding...

These macros help make that code more concise and scannable:

someView.frame = CGRectMake(RIGHT(someOtherView) + padding...

You can download the macros from our TBMacros Github repo. First let’s take a look at an example Continue reading

GSM 0338 encoding for SMS

Here’s a little Objective-C helper class we wrote to make it easier to convert NSString’s to GSM 03.38 encoding on the iPhone, iPad, or Mac:

  • Handles stripping out characters not included in the GSM0338 character set
  • Calculates the length of a GSM 0338 string (e.g. ^,[,|,etc each require 14 bits instead of 7
  • Gives you a truncate method (e.g. ^hello truncated to length 4 is ^he because ^ is 14 bits)
#import <Foundation/Foundation.h>

@interface GSM0338Helper : NSObject
+(int)gsmStringLength:(NSString *)textToShare;
+(NSString *)gsmStringFromString:(NSString *)textToShare replacementChar:(NSString *)replace;
+(NSString *)gsmShorten:(NSString *)text length:(int)length truncationText:(NSString *)truncationText;
@end
#import "GSM0338Helper.h"

@implementation GSM0338Helper

// gsm has certain characters like pipe that require 14 bits instead of 7 so we need to double count those
+(int)gsmStringLength:(NSString *)textToShare {
    if (textToShare == nil || [textToShare length] == 0) return 0;
    NSError *error = NULL;
    // count the number of times special escape characters appear that will take up 2 characters instead of one
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\^\\{\\}\\[~\\]\\|€\\\\]" options:NSRegularExpressionCaseInsensitive error:&error];
    NSArray *matches = [regex matchesInString:textToShare options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [textToShare length])];
    return [textToShare length] + [matches count];
}

// replaces non gsm characters with the replace string you specify, can be empty string @""
+(NSString *)gsmStringFromString:(NSString *)textToShare replacementChar:(NSString *)replace {
    if (textToShare == nil || [textToShare length] == 0) return textToShare;
    NSError *error = NULL;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9@£$¥èéùìòÇ\fØø\nÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%%&'()\\*\\+,-./:;<=>\\?¡ÄÖÑܧ¿äöñüà\\^\\{\\}\\[~\\]\\|€\\\\]" options:NSRegularExpressionCaseInsensitive error:&error];
    NSString *modifiedString = [regex stringByReplacingMatchesInString:textToShare options:0 range:NSMakeRange(0, [textToShare length]) withTemplate:replace];
    return modifiedString;
}

// shortens gsm string to length gsm chars and if it truncates it, it appends the truncation text
+(NSString *)gsmShorten:(NSString *)text length:(int)length truncationText:(NSString *)truncationText {
    if (textToShare == nil || [textToShare length] == 0) return text;
    int gsmTextLength = [self gsmStringLength:text];
    if (gsmTextLength > length) {
        // We have to first figure out how many characters to remove since gsm length is possibly > [title length]
        int numCharsToRemove = gsmTextLength - length;
        int newTextLength = [text length] - numCharsToRemove;
        if (newTextLength > 0 && newTextLength < [text length])
            text = [NSString stringWithFormat:@"%@%@", [text substringToIndex:newTextLength], truncationText];
    }
    return text;
}

@end

Kickstart your apps with the iOS Xcode Starter Project

We created the iOS Xcode Starter Project to make it quick and easy to start a new iPhone or iPad app. Our goal was to create a template to save the hours of effort it takes on a new project configuring essential open source libraries, the unit and functional testing environments, analytics, multiple targets, multiple app IDs (for development and production), and so on.

Starting a new project with the iOS Xcode Starter Project couldn’t be easier: Continue reading

Slim down your iOS app by excluding files from production builds

If you use additional files or libraries during development like HockeyKit or TestFlight you’ll want to exclude those in your production app store builds. Likewise, if you have an iPad and iPhone app that share the same Xcode project but are published under separate app IDs, this technique is handy for excluding iPad assets from your iPhone builds and vice-versa.

You won’t find this feature in Apple’s documentation. But it does exist in the form of a user-defined build setting called EXCLUDED_SOURCE_FILE_NAMES and it’s a really handy feature. I’ll use TestFlight in this example but you can use this approach to exclude any files from a build: Continue reading

Symbolicating (mostly) fixed in Xcode 4.1

I previously posted why Xcode 4′s symbolication is broken, along with a patched script that fixes it. As of Xcode 4.1, Apple has fixed the problems in the symbolicatecrash script, though I’ve found that it still often does not symbolicate correctly. Here are the most common problems (and solutions) I’ve come across:
Continue reading

Empty or incorrect URL with webViewDidStartLoad

If you’re using a UIWebView in your iPhone or iPad app and you want to execute some code when a page starts loading you’ll likely turn to the webViewDidStartLoad: method in UIWebViewDelegate. However, you may have surprisingly unpredictable results. For example, the following code won’t work as expected:

- (void)webViewDidStartLoad:(UIWebView *)webView {
    if ([webView.request.URL hasPrefix:@"http://mywebsite.com"]) {
        // Show buttons specific to my site
    }
}

The issues you’ll run into are:
Continue reading

Adding Unit Tests to an existing iOS project with Xcode 4

When you build a new iPhone or iPad app from scratch, you can generate a new project with tests using Xcode’s project templates, and the test dependency is set up correctly.

However, if you add a unit test target to an existing iOS project, there are some manual steps required to set up the target correctly. Continue reading

Can’t symbolicate XCode4 archive builds?

Update: symbolication is fixed in Xcode 4.1. Check this post for troubleshooting tips if you’re still having problems.

Since XCode 4 was released, several iOS developers have reported that their crash reports are no longer symbolicated correctly, meaning that they can’t trace crashes to the code that caused them. I’ve traced this problem down to the Perl script that XCode uses to map the addresses in a crash report to their symbols, symbolicatecrash. I’ve patched the script to get it working, which you can download here. This is not beautiful Perl (if there is such a thing), and it makes some assumptions about how files are nested, so YMMV. But it works for me.

Continue reading