New “When In Use” Location Tracking in iOS 8

Location AlertIf your app tracks the user’s location, you may notice when you run it in iOS8, the location tracking alert asks for permission to “access your location even when you are not using the app“. If your app only accesses the user’s location when the app is running, you can present a less scary message by requesting “when in use” authorization only.

Continue reading

Not Cuckoo for CocoaPods

CocoapuffsThe projects we work on generally have have dependencies on one or more (often several) open source projects, including our own libraries. We’ve always managed those dependencies using git submodules. As submodules have some quirks, a few projects have cropped up trying to make dependency management easier. The one that’s gained the most traction is CocoaPods.

Recently one of the projects we work on moved from submodules to CocoaPods. While CocoaPods has some nice aspects, I’m not crazy about the move. Here are some of the challenges I’ve faced. Continue reading

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

Objective-C Singleton Pattern Updated For Testability

SingletonAt Two Bit Labs we do a fair amount of unit testing. In places where we use singletons we use a variation on the the Objective-C dispatch_once pattern of thread safe singleton creation. This variation supports resetting the singleton or replacing it with a mock object.

That way in our unit tests we can do the following:
Continue reading

Objective-C Blocks Cheat Sheet

iOS Blocks Objective-CBlocks are an incredibly powerful addition to Objective-C, introduced in iOS 4. However, their syntax can be maddeningly difficult to remember. Matt Gallagher has an excellent post that breaks down the syntax to help you understand it. If you haven’t read this article, go do it now.

Even after working with blocks for a while, I still get tripped up. So I created the cheat sheet below, which I frequently refer to when declaring blocks. 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

Block initialization for testability and reuse

BlocksSince Apple introduced block support in iOS 4, more and more APIs are moving from delegation to block callbacks. While block callbacks can be declared inline, in most cases you should initialize your block callback in a method that returns the block. This keeps the code that calls an external API succinct, allows you to reuse the block in different contexts, and makes it easier to unit test the code in the block.

Let’s see what this looks like in practice.
Continue reading

The reviews are in!

So be honest: What’s your answer to the question “Do you enjoy reading the latest app store reviews for your app?”

(a) “Our users are so smart, kind, and thoughtful that I relish the chance to read their pearls of wisdom.”

(b) “Unless they’re written on the bottom of a pint glass, no.”

(c) “I’d rather be run over by a rhino.”

Continue reading