Brian Coyner

Excellence in Software Engineering

Failing Fast

Failing fast is the decision to abort a running application when the application encounters a bad state. The decision, however, to abort a running application is often difficult. This post does not attempt to convince you to adopt a fail fast policy. Instead this post shows a way of using simple macros to make it easy to abort an iOS app with crash report logging.

Assert Expression

The BTSAssert macro aborts the app if the given expression evaluates to false. The macro performs the following:

  • evaluates the expression
    • if true, nothing happens
    • if false
      • log a message
      • abort the app using abort()

This code is always on for debug and release versions. Why? Because bugs still exist in production/ release code, so we still want to fail-fast. The runtime cost is minimal.

1
2
3
4
5
6
7
8
#define BTSAssert(expression, ...) \
    do { \
        if (!(expression)) { \
            NSString *__BTSAssert_log_string = [NSString stringWithFormat:@"Assertion failure: %s. %@", #expression, [NSString stringWithFormat:@"" __VA_ARGS__]]; \
            __BTS_LOG(@"%@", __BTSAssert_log_string); \
            abort(); \
        } \
    } while(0)

Example Usages:

1
2
3
BTSAssert(someCounter >= 5);
BTSAssert(someCounter >= 5, @"Counter is wrong.");
BTSAssert(someCounter >= 5, @"Counter is wrong: %d", someCounter);

Force Abort

The BTSFail macro aborts the app. The macro performs the following:

  • log a message
  • abort the app using abort()

This code is always on for debug and release versions. Why? Because bugs still exist in production/ release code, so we still want to fail-fast. The runtime cost is minimal.

1
2
3
4
5
6
#define BTSFail(...) \
    do { \
        NSString *__BTSFail_log_string = [NSString stringWithFormat:@"%@", [NSString stringWithFormat:@"" __VA_ARGS__]]; \
        __BTS_LOG(@"%@", __BTSFail_log_string); \
        abort(); \
    } while(0)

Example Usages:

1
2
3
BTSFail();
BTSFail(@"Counter is wrong.");
BTSFail(@"Counter is wrong: %d", someCounter);

Intro To Crashlytics

Crashlytics is a very simple and powerful crash reporting tool for iOS and Android. The __BTS_LOG macro used in BTSAssert and BTSFail enables logging to the Crashlytics API for release builds. Logging to Crashlytics, in my opinion, only makes sense for release builds. There are numerous reasons why. One reason is because there is no need to link Crashlytics with OCUnit test targets. Debug builds log to NSLog.

Crash Report Logging

1
2
3
4
5
#ifdef DEBUG
#define __BTS_LOG(__FORMAT__, ...) NSLog((__FORMAT__), ##__VA_ARGS__);
#else
#define __BTS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#endif

The CLSLog is part of the Crashlytics logging API. Crashlytics submits log statements with an iOS crash report. This means that Crashlytics combines a symbolicated crash report with the programmer provided abort message. Very nice!

Summary

  1. You should adopt a fail fast policy.
  2. You should write OCUnit tests.
  3. You should use Crashlytics.

St. Louis CocoaHeads, July 30, 2013

July "Blast" Talks

Various Speakers

The July "Blast" Talks are short (5 to 20 minute) presentations about an iOS/ Objective-C technology.

  • 6:00pm - Meet/Greet
  • 6:15pm - July "Blast" Talks
  • 7:30pm - Next Meeting Details, Questions / Discussions

St. Louis County Library Headquarters (East Room); (314) 994-3300; (1640 S. Lindbergh Blvd).

Xcode Run Scripts

Xcode provides a mechanism to run custom scripts when building a target.

  1. Select your project file
  2. Select your app's target
  3. Select "Build Phases"
  4. Tap "Add Build Phase"
  5. Select "Run Script"

Code Convention Violation Script

My current client project executes a custom shell script that scans all source files looking for code convention violations. If a violation is found, the script fails the build and notifies the developer of the offending code. The greatest part is that Xcode allows us to quickly jump to the offending code, just like a compiler error.

Three Strings To Echo

There are three main pieces needed for Xcode to fail and provide "jump to" capability:

  1. Absolute path to the source file
  2. Line and column number
  3. Custom message

Here is a complete line example that may be echoed in a script:

1
/Users/briancoyner/Development/Private/MediaTiming/MediaTiming/BTSViewController.m:13:17: Some Custom Message

Quick Sample

You can see how this works by simply echoing a few lines in the "Run Script" console.

Here is an example screenshot:

When you build the target, Xcode fails the build. You are now able to jump between
each "code convention violation" error just like a compiler error.

Very awesome!

A Word of Warning

You can also add warning: to the string to generate a warning instead of an error.

1
/Users/briancoyner/Development/Private/MediaTiming/MediaTiming/BTSViewController.m:13:17: warning: Some Custom Warning Message

St. Louis CocoaHeads, June 25, 2013

Organizing Your App's Code Using Static Libraries and Resource Bundles

By Brian Coyner

If you are working on an iOS team (you + 1 or more), then you definitely need to know about Static Libraries and custom Resource Bundles. This session shows how to effectively utilize Xcode + Static Libraries to organize your app's code into logical units.

  • 6:00pm - Meet/Greet
  • 6:15pm - Static Libraries and Resource Bundles
  • 7:30pm - Next Meeting Details, Questions / Discussions

St. Louis County Library Headquarters (East Room); (314) 994-3300; (1640 S. Lindbergh Blvd).

Counting Characters

A few days ago I read a STL Lambda Lounge post about Counting DNA Nucleotides. After solving the Nucleotides problem in Objective-C, I decided to solve a slightly different problem. I decided to count and print the number of times each character (assume ASCII text) appears in Moby Dick. I used the same data structure to solve both problems: an array of unsigned int values, where the array index represents the ASCII character and the value is the count.

Reading Moby Dick using Grand Central Dispatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// This function blocks until all characters are counted.
void CBRCountCharactersInFileAtPath(NSString *path, unsigned int *characters)
{
    // The dispatch_io_read is an asynchronous call. We use a semaphore to provide synthetic synchronization.
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    dispatch_queue_t readQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, [path UTF8String], 0, O_RDONLY, readQueue, NULL);

    // SIZE_MAX means keep going until we reach EOF. 
    dispatch_io_read(channel, 0, SIZE_MAX, readQueue, ^(bool done, dispatch_data_t data, int error) {
        if (done) {
            // All bytes have been read. Let's notify that we are done.
            dispatch_semaphore_signal(semaphore);
        } else if (data != NULL) {

            // As we read, we count.
            NSString *string = CBRStringFromDispatchData(data);
            CBRCountCharactersInString(string, characters);
        }
    });

    // Block the calling queue/ thread until the IO read completes (i.e. the channel is "done")
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    // What we open, we must close. 
    dispatch_io_close(channel, 0);
}

The above code sets up and executes streaming file IO using Grand Central Dispatch. Now let's look at the two functions named CBRStringFromDispatchData and CBRCountCharactersInString.

Here is the code used to create a NSString from a dispatch_data_t.

1
2
3
4
5
6
7
8
9
10
NSString *CBRStringFromDispatchData(dispatch_data_t data)
{
    size_t dataSize = dispatch_data_get_size(data);
    NSMutableString *string = [[NSMutableString alloc] initWithCapacity:dataSize];
    dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {
        [string appendFormat:@"%.*s", (unsigned int)size, buffer];
        return true;
    });
    return string;
}

Here is the code that tracks the character counts.

1
2
3
4
5
6
void CBRCountCharactersInString(NSString *string, unsigned int *characters)
{
    for (unsigned int index = 0; index < [string length]; index++) {
        characters[[string characterAtIndex:index]]++;
    }
}

Sample Program

1
2
3
4
5
6
7
8
9
10
11
#import "CBRCounter.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        unsigned int characters[256] = {0};
        CBRCountCharactersInFileAtPath(@"/path/to/mobydick.txt", characters);
        CBRPrintCharacterCount(characters);
    }
    return 0;
}

Printing the Character Counts

1
2
3
4
5
6
7
void CBRPrintCharacterCount(unsigned int *characters)
{
    // This example only prints the "printable" characters. 
    for (unsigned int character = ' '; character <= '~'; character++) {
        NSLog(@"%c: %d", (char)character, characters[character]);
    }
}

Example Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
A: 2725
B: 1466
C: 1184
D: 802
E: 1361
F: 864
G: 751
H: 1497
I: 3641
J: 261
K: 185
L: 959
M: 782
N: 1241
O: 1050
P: 1158
Q: 323
R: 895
S: 2277
T: 2568
U: 284
V: 181
W: 1326
X: 25
Y: 360
Z: 38

Verify the Counts

I verified my program's results using TextEdit.

  1. Open "Moby Dick" in TextEdit
  2. Type Command+F
  3. Deselect "Ignore Case"
  4. Select "Contains"
  5. Enter Character
  6. View TextEdit's Result

Using Git Sparse Checkout

There are times when all I want or need from a Git repo are a handful files.
For example, I use the awesome git-completion and git-prompt scripts
included in the Git project. Thus, I don't need the entire Git repo taking up space.

This is exactly what sparse checkouts enable.

Here are the steps to create a "sparse" Git local repository that only includes the "Completion" scripts.

Step 1: Create a directory.

I named mine git-completion. You can name the directory whatever you want.

1
2
mkdir git-completion
cd git-completion

Step 2: Initialize a Git repository

1
git init

Step 3: Enable Sparse Checkouts

1
git config core.sparsecheckout true

Step 4: Tell Git which directories you want

1
echo contrib/completion/ >> .git/info/sparse-checkout

Or you can modify the .git/info/sparse-checkout file directly. Either way is fine.

Step 5: Add the remote

1
git remote add -f origin https://github.com/git/git.git

Final Step: Fetch the files

1
git pull origin master

You should now have the contrib/completion directory. No other Git source files exist in your local copy.

Sourcing the files

Update your .bashrc file.

1
2
source ~/Development/git-completion/contrib/completion/git-completion.bash
source ~/Development/git-completion/contrib/completion/git-prompt.sh

West St. Louis CocoaHeads, May 28, 2013

Bluetooth LE: Using iOS CoreBluetooth to communicate with "the Internet of Things"

By Jason Graves

BLE or Bluetooth 'Low Energy' is a fairly new wireless spec designed to connect us to world around us. This includes gathering data from embedded sensors like a thermostat or heart rate monitor, or controlling the lights in your house or unlocking the front door without a key. Apple's CoreBluetooth framework has been around since iOS 5, but we are only now starting to see many new BLE enabled devices become available. We will cover some of the interesting uses of BLE and how to use CoreBluetooth to create your own apps that can communicate with these devices.

  • 6:00pm - Meet/Greet
  • 6:15pm - Bluetooth LE
  • 7:30pm - Next Meeting Details, Questions / Discussions

St. Louis County Library Headquarters (East Room); (314) 994-3300; (1640 S. Lindbergh Blvd).

West St. Louis CocoaHeads, April 30, 2013

iOS Tool Talks

Why I Bought AppCode

By Jason Hanson

AppCode, by JetBrains, is what an IDE should be. All the tools I have come to know and love in a ‘grown-up’ IDE for the Objective-C and iOS platform. My productivity, and more importantly, my confidence to refactor, skyrocketed when I started using AppCode. Come learn about some of the powerful features, tips, tricks, and shortcuts AppCode has to offer the iOS developer.

Lighten the load on your teams with Jenkins

By JP Revel

If you've ever been part of a software project with a team, it often falls to one person to compile the project when it comes time to deliver. There are lots of problems with this approach. One solution is to set up a CI server, such as Jenkins (formerly Hudson). I will talk about the concept of CI, go over a sample setup, show some configuration and (hopefully) have Jenkins build a project for us.

  • 6:00pm - Meet/Greet
  • 6:15pm - iOS Tools Talk
  • 7:30pm - Next Meeting Details, Questions / Discussions

St. Louis County Library Headquarters (East Room); (314) 994-3300; (1640 S. Lindbergh Blvd).

West St. Louis CocoaHeads, March 26, 2013

UIStoryBoards

By Rick Aurbach

While apps with straightforward UI designs can often be implemented effectively using a single storyboard and GUI-based segues, this approach is less effective for large complex UI designs. This talk will discuss techniques for dividing an application's UI into multiple storyboards and implementing programmatically-generated segues. We will discuss tab bar controllers, embedding sub-controllers into views, popover controllers, and navigation controllers. The talk will hopefully appeal to newbies and veterans alike.

  • 6:00pm - Meet/Greet
  • 6:15pm - UIStoryboards by Rick Aurbach
  • 7:30pm - Next Meeting Details, Questions / Discussions

St. Louis County Library Headquarters (East Room); (314) 994-3300; (1640 S. Lindbergh Blvd).