Xcode fails to run when switching between versions

I haven’t posted in a while, but I’d really like to start getting back into it. I figured I would start with something easy.

I was trying to switch between iOS 7 and iOS 7 beta 5 today, and I struggled to get the simulator to start on the iOS 7 beta 5. I rebooted and it worked, but then I couldn’t get iOS 7 to work.

Recap, to change between iOS versions:
sudo xcode-select --switch /Applications/Xcode.app/ # or whatever Xcode you want to use

But sometimes, even when you close Xcode, its processes can hang around and interfere with the other version that you want to run. Use this command to take care of that:
ps -ea | grep 'Xcode' | grep -v 'grep' | cut -d' ' -f 1 | xargs kill

Have fun switching between stable and non-stable Xcode’s!

Giving The Crash Reporter Time To Do Its Work

At my place of business, I’ve been having many issues with corrupt data crashing my apps. Often times, when my app crashes, it will continue to crash on startup, forcing the user to have to uninstall and reinstall my app. The biggest issue with this entire scenario is that the crash reporting framework is unable to submit the crash report because of the immediate nature of the crash, and thus I never receive any crash reports, and never solve the problem. Obviously, an application that cannot submit its crash reports is not a suitable production app.

I use HockeyApp for our AdHoc deployment, and QuincyKit for AdHoc and App Store crash reports. QuincyKit does a fantastic job of submitting crash reports (when the application allows it to). I needed to engineer a way to ensure that QuincyKit received the time it required to submit crash reports before trying to reinitialize the app (and likely crash it again), and so I came up with this simple application delegate solution below using NSRunLoop:

// This solution assumes usage of both HockeyApp and QuincyKit
// Changing the source to work with a custom QuincyKit configuration should be trivial

@interface SimpleAppDelegate : NSObject <UIApplicationDelegate, BWQuincyManagerDelegate>

@property (nonatomic, retain) UIWindow *window;
@property (nonatomic) BOOL waitForCrashReportToSend;

@end

@implementation SimpleAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self installCrashHandlers];

    // initialize any other frameworks and your app

    return YES;
}

- (void)installCrashHandlers {
    // Configure QuincyKit
    [[BWQuincyManager sharedQuincyManager] setAppIdentifier:[self hockeyappAppId]];
    [[BWQuincyManager sharedQuincyManager] setDelegate:self];
    [[BWQuincyManager sharedQuincyManager] setAutoSubmitCrashReport:YES];

    // Only wait for crashes to be submitted if there are crashes to submit
    self.waitForCrashReportToSend = [[BWQuincyManager sharedQuincyManager] didCrashInLastSession];

    // Block this thread, but allow the run loop to continue until QuincyKit is finished
    while (self.waitForCrashReportToSend) {
        [[NSRunLoop currentRunLoop] runUntilDate:[[NSDate date] dateByAddingTimeInterval:0.1]];
    }

    NSLog(@"Crash handlers are installed");
}

// From BWQuincyManagerDelegate, called when QuincyKit is finished summitting crash reports
- (void)connectionClosed {
    self.waitForCrashReportToSend = NO;
}

- (NSString *)hockeyappAppId {
    // Return the hockeyapp app id
}

@end

Using the above source, I have been very successful in receiving the crashes from our users. It does not solve my primary issue of data corruption, but it ensures that I have as much information about the problem as possible.

Please feel free to ask questions or make comments!

Running UI Tests on an iOS Device in a Continuous Integration Environment

In testing, there are many different ways to stress your code. In a previous post, I discussed running unit tests in a continuous integration environment. However, no matter how hard you try, in a Model-View-Controller pattern codebase, unit tests can really only tackle the model, and maybe a little bit of the controller. A higher level of testing is required in order to ensure the visual elements exist and function as expected.

Enter KIF

KIF is a test framework by the iOS team at Square. The UI testing that Apple provides is Javascript based, and near to impossible to automate. Square decided to create a testing framework driven by Objective-C, and runnable via the command line, instead of proprietary software. This is awesome, because it allows iOS developers to be able to write UI tests in their native language, and test via the iPhone Simulator from command line.

KIF for iPhone Simulator Tests

I have found that the documentation on square/KIF is effective for enabling and writing tests with KIF on the iPhone Simulator. They even have a section for running the KIF tests on the simulator from the command line.

Changes I would recommend with the instructions
  • My updated cowboygneox/KIF contains a build script to make a universal libKIF.a. If you take the universal library and the header files for KIF, you don’t have to bring in the KIF project and further obfuscate your project files.
  • For running the KIF tests via the command line, use my fixed cowboygneox/Waxsim. WaxSim was not honoring the sdk provided, so I fixed it.

KIF for Device Tests

If your experience in iOS is anything like mine, you’ll find that the simulator does a very poor job of emulating the hardware it simulates. I have found many instances where the UI I designed functioned as expected in the simulator, but had major flaws when put on a device; flaws so large that had the same UI tests been run on the device, the UI tests would have failed. Thus, I decided that KIF is only valuable if it can be run on the device from the command line.

In my research, I could not find any public information about running iOS UI tests on the device in a continuous integration environment, so I sought out to do it myself. I needed a way to install and debug a compiled application on the device, and with a quick Google search, I found Fruitstrap.

Fruitstrap

Fruitstrap is a command-line utility intended to install and debug an iOS application on a device. It requires files that come with Xcode, but doesn’t directly require Xcode to be present or running. Although this project is no longer maintained, I decided to pickup the source as it was, clean it up a bit, and add the required mechanisms to work with KIF.

Installing Fruitstrap

I would recommend using the instructions I have written on cowboygneox/fruitstrap.

Using Fruitstrap with KIF Tests

The script below is what I use in my continuous integration environment (specifically Jenkins) for running KIF tests on a device. Here are some notes about the script:

  • The script is run in the project root
  • Fruitstrap has been compiled and placed in the project root
  • There is an Xcode project named UI Tests which compiles and runs the KIF tests.
  • The script is expected to have an argument of the 40 character UUID of the device used for testing
  • The script writes to a file /tmp/KIF-$$.out and to stdout
  • The script will end with a nonzero exit code if it cannot find ‘…0 failures’.

#!/bin/bash

# Exit on errors
set -e

# Build KIF for use on the device
xcodebuild -target "UI Tests" -sdk iphoneos clean build CONFIGURATION_BUILD_DIR=/tmp/build

# Use fruitstrap as a means to install and run the app
./fruitstrap -d -b /tmp/build/UI\ Tests.app -i $1 2>&1 | tee /tmp/KIF-$$.out

# Check the log to see if testing finished correctly
grep -q "TESTING FINISHED: 0 failures" /tmp/KIF-$$.out

Wrap Up

Combining together the unit test runner in my previous post, the KIF simulator test runner documented here, and the KIF device test runner shown above creates a pretty solid testing environment for iOS codebases. The mobile-sphere is quickly growing to have serious code bases, and the consumer expectation of quality in mobile apps is growing even faster. Using this test-bed combination allows us as developers to have strong confidence in our iOS products, and helps to eliminate the potential of any dumb bugs that will give our app a 1-star rating on the App Store.

Continuous Integration for iOS

In my opinion, a programming language is completely useless to me if I cannot create a test and build process that is consistent and reproducible on many different machines, and sometimes, different platforms; iOS/Objective-C is certainly no exception. Assuming that you, the reader, agree with me and just want to see how I have implemented CI with my iOS projects, then here we go!

Assumptions:

  • Xcode 4.x (Xcode 3.x will likely work, but I cannot confirm.)
  • The project/target is named FunProject, and tests are FunProjectTests
  • You can build for either device or simulator using -sdk iphoneos or -sdk iphonesimulator, but I will assume iphonesimulator
  • The configuration will be Debug unless specified, but I will use AdHoc

AdHoc build from commandline
To start, you can build a target for AdHoc deployments from the command line:

/usr/bin/xcodebuild -target FunProject -sdk iphoneos -configuration AdHoc clean build

A breakdown of options:

  • -target: not needed if you want the primary target, but its good to be specific
  • -sdk: specify the SDK. You can be specific (ex. iphonesimulator4.3) or assume version (ex. iphonesimulator).
  • -configuration: I believe it defaults to Debug, but best to be explicit.
  • clean build: Cleans the project, then builds it. I generally recommend cleaning the project before building.

Tests from the command line

/usr/bin/xcodebuild -target FunProjectTests -configuration Debug -sdk iphonesimulator clean build TEST_AFTER_BUILD=YES

More options:

  • TEST_AFTER_BUILD=YES: Without this, the tests won’t actually run.

Script that pulls it all together
In a future article, I will outline packaging the application for use on HockeyApp or TestFlight, but for simple builds in a continuous integration environment, a simple bash script looks like this:

#!/bin/bash

# End execution of this script if any execution returns a bad exit code
set -e

# Run the tests
/usr/bin/xcodebuild -target FunProjectTests -configuration Debug -sdk iphonesimulator clean build TEST_AFTER_BUILD=YES

# Build the AdHoc product
/usr/bin/xcodebuild -target FunProject -sdk iphoneos -configuration AdHoc clean build

Simple and effective. As always, I welcome feedback and questions, so please let me know if I can help you, the reader, make your iOS experience more effective.

Fixing iOS Simulator Launching via Command Line on Xcode 4.3

I, like many others, use the command line to start the iOS simulator for tests and other automated reasons. However, since Xcode 4.3, Apple has broken all variations of command line invoking of the iOS Simulator.

Apple decided to remove the /Developer folder as the installation path, and now Xcode 4.3 can be found in /Applications/Xcode.app. Many compiled libraries require the /Developer at run time, so lucky enough for us, that’s an easy fix:

sudo ln -s /Applications/Xcode.app/Contents/Developer/ /Developer

When running a command line launcher for the simulator, you may run into the following irritating exception:

dyld: Library not loaded: @rpath/DevToolsFoundation.framework/Versions/A/DevToolsFoundation

Apple moved the DevTools frameworks from /Developer/Library/PrivateFrameworks/ to /Applications/Xcode.app/Contents/OtherFrameworks/. This is an odd change, but it can be speculated that Apple did not want developers using those particular frameworks any more. Fixing this is also trivial:

sudo ln -s /Applications/Xcode.app/Contents/OtherFrameworks/DevToolsCore.framework /Developer/Library/PrivateFrameworks/
sudo ln -s /Applications/Xcode.app/Contents/OtherFrameworks/DevToolsCParsing.framework /Developer/Library/PrivateFrameworks/
sudo ln -s /Applications/Xcode.app/Contents/OtherFrameworks/DevToolsFoundation.framework /Developer/Library/PrivateFrameworks/
sudo ln -s /Applications/Xcode.app/Contents/OtherFrameworks/DevToolsInterface.framework /Developer/Library/PrivateFrameworks/
sudo ln -s /Applications/Xcode.app/Contents/OtherFrameworks/DevToolsKit.framework /Developer/Library/PrivateFrameworks/
sudo ln -s /Applications/Xcode.app/Contents/OtherFrameworks/DevToolsRemoteClient.framework /Developer/Library/PrivateFrameworks/
sudo ln -s /Applications/Xcode.app/Contents/OtherFrameworks/DevToolsSupport.framework /Developer/Library/PrivateFrameworks/

Note: I did try to recompile the source for WaxSim and ios-sim, trying to get the build to be compatible with both Xcode 4.2 and 4.3, but I could not create a build that worked on both. If someone figures it out, let me know, because using an Xcode 4.3 binary would be the ideal situation.

Comments are always welcome :)

Don’t Put Git Repositories in Dropbox

Recently, my wife’s laptop started to become excruciatingly slow. The CPU fans were blaring and applications like Firefox took ages to do anything. After investigating the CPU spikes, I came to find that it was the trusty Dropbox that we share, and it was making her computer have a heart attack due to all of the files it had to index and deal with.

Long story short, Git, Mercurial, and other source control applications often have thousands of files on top of the source that you are maintaining. If you put a repository in a Dropbox, it is basically source controlling the source control, and the implications of that cause a lot of wasted CPU cycles and bandwidth consumption.

I do not blame Dropbox for not being able to effectively work with source control repositories, since the algorithmic complexity of managing hundreds of thousands of individual files is a daunting task, and I applaud their efforts. However, if you still choose to put repositories in your Dropbox, I recommend you configure it as so:

  1. Place all repositories in a separate folder
  2. On a server computer, configure Dropbox to monitor updates on the repository folder (and any other folders you care about)
  3. On all client computers, make sure to configure Dropbox to NOT monitor updates on the repository folder

The client machines should not directly touch the repositories in the Dropbox; leave that for the server to do. Pushing updates to the server is more Git-centric anyway.

I have used the configuration above with success, but I have yet to find any real reason to put my repositories in my Dropbox, so I will host my source controlled code on a remote repository (such as GitHub) and I will keep local repositories out of my Dropbox; my computers will thank me.

Migrating to Xcode 4.3

While drinking my cup of coffee and clean installing Mac OS X Lion on my MacBook Pro this morning, I came across an unexpected inconvenience: Xcode had been changed on the App Store. Not just updated, though, Apple completely changed the location of major files that I use daily. (ex. git) Frustrating for me, but for you the reader, I will get you back online the way that you expect.

To start, you’ll notice that the /Developer folder no longer exists, and better yet, the PATH variable isn’t set to anything new, so anything developer is not set on your behalf. Not to fret, though, since the Developer folder has been moved to /Applications/Xcode.app/Contents/Developer. It seems that only the location has changed, and that the Developer folder’s layout stayed the same, which is a nice bonus.

If you are like me and use JetBrain’s AppCode, or some other tool that makes external use of Xcode’s libraries, then you’ll need to tell those applications which hole Xcode has decided to hide in. From the command line, type the following:

sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer

I was able to get git back by doing the following in Xcode:

Go to the menu Xcode > Preferences... > Downloads tab

You’ll notice that there is a new “Command Line Tools” option that hasn’t been seen in the past. This will install applications like git into your /usr/bin folder.

Once Xcode is finished installing the “Command Line Tools”, confirm a successful install by firing up the Terminal and typing git. You should see a series of options.

Let me know if this helps anyone, or if I can improve this post!

Follow

Get every new post delivered to your Inbox.

Join 54 other followers