Posts Tagged ios

Figuring out iOS crashes

I was troubleshooting some iOS crashes with the team today and remembered this post I’d saved by Matthijs Hollemans, My App Crashed, Now What? It’s a great introduction for anyone starting to pick apart all the wonderful ways an iOS app – and more generally Objective-C – can break.

There’s plenty here to make me yearn for the helpful stack traces in Java once in a while (possibly the only thing I prefer about Android development…), but it’s all good stuff to know if you’re supporting a widely used iOS app in production. Enjoy!

,

1 Comment

Fixing Xcode 5 DP3 storyboard could not be opened / verify document content error

After updating from Xcode 5 DP2 to DP3 I found that several storyboards in my iOS project could no longer be opened, and would also prevent the project from building, with an error like:

The document “MainStoryboard_iPhone.storyboard” could not be opened. Could not verify document content.

storyboarderror

Interestingly it did not affect several storyboards which hadn’t been touched in awhile, and for those exhibiting the problem some trial and error revealed that deleting the table view inside certain view controllers fixed it. Of course, recreating table contents with all of the layout and segue intricacies is not ideal so I poked around further using Open As Source Code and noticed that the suspect tables had become tableViews inside a viewController (versus tableViewController) which was now failing verification.

I’ve actually run into this problem before when a developer manually added a UITableView to a plain UIViewController, either by accident or because they changed their mind partway through the design, and saw unintended side effects. But in this case it seems to have started out just fine and been mangled by Xcode 5 somewhere along the way. In any case, using Open As Source Code and manually changing the involved XML tags fixes the problem, allows Xcode 5 DP3 to open the storyboard, and most importantly preserves all the content without deleting anything or disabling auto layout.

For example, change:

<viewController storyboardIdentifier="MyScreen" id="Brq-Jb-DRf" customClass="MyScreenViewController" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="70" sectionHeaderHeight="22"
...
</viewController>

to:

<tableViewController storyboardIdentifier="MyScreen" id="Brq-Jb-DRf" customClass="MyScreenViewController" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="70" sectionHeaderHeight="22"
...
</tableViewController>

If you’re seeing this error without the scenario above there may be other similar problems that now fail Xcode validation which are also solved by a quick fix in the storyboard XML. I’d recommend deleting scenes or objects in the storyboard until things work, then add back a new/empty version of the object causing the problem, compare source, and if it’s a simple fix then revert and update the XML yourself similar to above.

Please comment if you find any others and I’ll add them here.

,

5 Comments

Code coverage for iOS testing with OCUnit

I recently posted steps for setting up iOS unit testing with Jenkins, but working on a new project realized I wanted to take this a step further and start seeing code coverage metrics. The configuration has evolved quite a bit but fortunately there’s a great Code Coverage with Xcode 4.2 guide from Infinite Loop that covers this and seems to work with both Xcode 4.3 and 4.4 in my testing. Be sure to check the included links, related posts, and followup in comments for troubleshooting various errors that creep up across different Xcode versions. Once tests have been run against instrumented code you can view the coverage output graphically in CoverStory on a Mac. Finally, if you want to convert this output to Cobertura-style for display in Jenkins using gcovr you’ll want to configure your Jenkins build as described in iOS Code Coverage, Cobertura and Jenkins. Good stuff!

, ,

2 Comments

Automated iOS Jenkins builds with application tests and Core Data

I recently started some iOS development with a new team and had the chance to set up a server for continuous integration builds. Since our teams are already familiar with Jenkins that seemed like the logical place to start, and the Xcode plugin available works reasonably well out-of-the-box for simply building the application. However, running tests – especially what Apple calls application tests (anything accessing a bootstrapped NSApplication through the simulator) using Core Data – took a little bit of tweaking. Here’s what was needed as of Xcode 4.2.

  1. Install the latest version of Jenkins, either on a Mac with Xcode installed or another server that has such a Mac available as a slave for any iOS builds
  2. Install a plugin for your source control system of choice if not already present (I’m using Git with the default Git plugin - considering the great Git support added in Xcode 4 with Git repos enabled by default, if you’ve been looking for an excuse to switch this is it!)
  3. Install the Xcode plugin
  4. Create a build following the plugin build instructions for a basic release build of the application, and especially pay attention to the keychain notes (I went ahead and entered the login password of the build account so that Jenkins could unlock it itself)
  5. Run a build and verify that everything is successful up to this point, and if not, troubleshoot
  6. Edit the build and add a separate build target for testing following the plugin test instructions, specifying iphonesimulator as the SDK (you can enter a full absolute path like the example if you wish, but simply iphonesimulator should find the latest), and make sure it points to your project’s test target instead of the primary build target (e.g. MyAppTests instead of MyApp)
  7. Run a build and look for errors. Assuming you have one or more application tests expecting to run in the iOS simulator it will probably have skipped all the tests with an error like ”Skipping tests; the iPhoneSimulator platform does not currently support application-hosted tests (TEST_HOST set).”
  8. To fix the error above (or prepare for it, if application tests don’t exist yet but will eventually), on the Jenkins server (or slave if running the iOS build on a slave) edit:
    /Developer/Platforms/iPhoneSimulator.platform/Developer/Tools/RunPlatformUnitTests (if Xcode <= 4.2)
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Tools/RunPlatformUnitTests (if Xcode >= 4.3)

    and change line 95 from:

    Warning ${LINENO} "Skipping tests; the iPhoneSimulator platform does not currently support application-hosted tests (TEST_HOST set)."

    to these 2 lines:

    export OTHER_TEST_FLAGS="-RegisterForSystemEvents"
    RunTestsForApplication "${TEST_HOST}" "${TEST_BUNDLE_PATH}"

    as described in Xcode 4: Running Application Tests From The Command Line in iOS (and if you want to be able to run command-line builds with tests on any local developer environments, like xcodebuild -target MyAppTests -sdk iphonesimulator, this is the step to do on each of those as well)

  9. Run a build again and look for errors. Assuming you have application tests for portions of the application using Core Data, you may see an error relating to the storage URL like “Error validating url for store”
  10. To fix the error above (or prepare for it, if the application doesn’t use Core Data yet but will eventually), you’ll need to change several of the configuration methods for Core Data to operate differently during command-line tests as described in iOS Unit Testing with Xcode 4 and Core Data; not covered here is how exactly to detect if you’re in a test, which I did by checking the WRAPPER_NAME environment variable like this (and I have local data being written inside the generated ./build directory):
    /**
    * Determine if application is running from a test inside an external command-line build by xcodebuild.
    */
    - (BOOL) isExternalBuild
    {
        NSString *wrapper = [[[NSProcessInfo processInfo]environment] valueForKey:@"WRAPPER_NAME"];
        return wrapper != nil && ([wrapper rangeOfString:@"octest"].location != NSNotFound);
    }
    
    /**
    Returns the managed object model for the application.
    If the model doesn't already exist, it is created from the application's model.
    */
    - (NSManagedObjectModel *)managedObjectModel
    {
        if (__managedObjectModel != nil) {
            return __managedObjectModel;
        }
    
        if([self isExternalBuild]) {
            NSArray *bundles = [NSArray arrayWithObject:[NSBundle bundleForClass:[self class]]];
            __managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:bundles];
        } else {
            NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
            __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        }
    
        return __managedObjectModel;
    }
    
    /**
    Returns the URL to the application's Documents directory.
    */
    - (NSURL *)applicationDocumentsDirectory
    {
        if([self isExternalBuild]) {
            NSString *dir = [NSString stringWithFormat:@"%@/build", [[NSFileManager defaultManager] currentDirectoryPath]];
            return [NSURL fileURLWithPath:dir];
        } else {
            return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
        }
    }
    
    
  11. Run another build and look for errors. If you see a more generic error along the lines of “UISplitViewController is only supported when running under UIUserInterfaceIdiomPad” or messages like “Timed out trying to acquire capabilities data.” during the run, your build server may not have the necessary scheme metadata from Xcode.
  12. To fix the error above, first make sure you’ve checked the Shared checkbox in the Manage Schemes dialog of your project, which will place some additional metadata in the project directory that you can commit to source control (but make sure you don’t then ignore them!) as described in Managing Schemes
  13. If you still see the error the iOS simulator probably has not been launched on the build machine since the last restart; it must have run once to start the necessary services but also cannot be open when the test runs, so to handle this automatically you may want to add a simple execute shell targetat the beginning of your build like:
    open '/Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app/'
    sleep 3
    killall 'iPhone Simulator'
  14. Run another build and hopefully this one works! If not, post the additional steps required and I will add them to this checklist.
  15. Once everything is working perfectly in Jenkins you’ll want to confirm that you can still run both the application and any test targets successfully in the simulator or on a device directly through Xcode. The steps here should support this unless you’ve missed something.

, , , ,

3 Comments