Code Repo    |     RSS
MD's Technical Sharing



Monday, June 9, 2014

Archiving iOS projects from command line using xcodebuild and xcrun

In one of my recent projects, I need to provided 16 different builds from a single iOS application code base. Although the different builds targeting different customers, having different application names, server addresses and build configurations have already been configured as different schemes in xCode, having to perform these builds manually using xCode is still time consuming and error-prone.

Generating IPA file from command line

Fortunately, cleaning, building, archiving and exporting an iOS project to IPA file can be done easily using the following xcodebuild commands (assuming xCode 5 is installed):

xcodebuild -project Reporter.xcodeproj -scheme "InternalTest" -configuration "Release Adhoc" clean

xcodebuild -archivePath "InternalTestRelease.xcarchive" -project Reporter.xcodeproj -sdk iphoneos  -scheme "InternalTest" -configuration "Release Adhoc" archive

xcodebuild -exportArchive -exportFormat IPA -exportProvisioningProfile "My Release Profile" -archivePath "InternalTestRelease.xcarchive" -exportPath "InternalTestRelease.ipa"

After cleaning, the project is archived to a .xcarchive file, exported to an IPA file and signed using the given provisioning profile, ready to be distributed for internal testing.

Bug with xcodebuild

This seems easy. However, as with xCode (or many other Apple developer tools for that matter), xcodebuild comes with bugs, and sometimes hard-to-find ones. After a few round of testing, I realized that the generated signed IPA file would sometimes fail to be installed on the device. When attempting to install the IPA file, the iPhone configuration utility says "The executable was signed with invalid entitlements" with the following detailed messages in the console log:

Admin-iPhone installd[31] : 0x2ffee000 MobileInstallationInstall_Server: Installing app com.ios.testapp
Admin-iPhone installd[31] : 0x2ffee000 verify_signer_identity: MISValidateSignatureAndCopyInfo failed for /var/tmp/install_staging.9sdyqR/TestApp.app/TestApp: 0xe8008016
Admin-iPhone installd[31] : 0x2ffee000 do_preflight_verification: Could not verify executable at /var/tmp/install_staging.9sdyqR/TestApp.app


Basically the IPA file was not signed properly and could not be installed. What made this very strange is that although all the provisioning profiles and signing identities were configured correctly, the signing issue still occurred intermittently - one attempt would produce an incorrectly signed IPA file while the next attempt would produce a correctly signed IPA file that can be installed on the device. Frustrated, I decided to investigate and found out the root cause of the issue.

First I checked if the correct provisioning profile was used to sign the IPA file by extracting the file as if it was a ZIP file and searching the .app folder for a file called embedded.mobileprovision. It was indeed the correct signing profile - even when the generated IPA file was corrupted.

Secondly, I compared the extracted files from the correctly signed IPA package and the corrupted IPA package to see the differences. The digital signatures for the signed components can be found in a file named CodeResources, an XML-formatted file located in the .app folder, and they look like below:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>files</key>
    <dict>
        <key>32x32_arrow.png</key>
        <data>
        P/XDWeYKpPpwSzLCFxSXV23inIQ=
        </data>
        <key>logo.jpg</key>
        <data>
        L+8Od1POJVPM7BFJPofhiR2rDso=
        </data>
......
    </dict>
</dict>
</plist>

After comparing the CodeResources file of the correct and corrupted IPA packages, I realized that most of the digital signatures were actually the same, with the only difference being the signature for the application executable file, e.g. TestApp. I checked the MD5 hashes of these two files and they were indeed different, explaining the difference in the signatures. This however did not yet explain the reason for the corrupted package, until I checked the MD5 hash of all the extracted files from both packages and realized that, although the generated core data model files (.mom and .omo) from the two packages were different, they both had the same digital signatures in CodeSignatures! This explained why the corrupted package failed the integrity check and could not be installed on the device. To verify this theory, I tried to overwrite the .omo and .mom files in the corrupted package with those from the correct IPA package while keeping the rest of the files the same. The modified IPA file could indeed be installed and run on the device. This showed that the incorrect digital signatures were indeed the issue.

But why is there such an issue? During my testing the corruption seemed to happen more often if xcodebuild is run repeatedly from command line to build one project after another. It does not happen that often if xcodebuild is run once in a while, or with sufficient delay between executions. Sounds kind of some caching issue, causing the program to use back the old digital signatures. The exact answer, of course, is only known by Apple.

The solution: xcrun

To fix this problem, we need to use xcrun, another tool with similar commands to build and export the project to an IPA file:

xcodebuild -project Reporter.xcodeproj -scheme "InternalTest" -configuration "Release Adhoc" clean

xcodebuild -project Reporter.xcodeproj -sdk iphoneos  -scheme "InternalTest" -configuration "Release Adhoc"

xcrun -sdk iphoneos PackageApplication -v "Internaltest/TestApp.app" -o "InternalTestRelease.ipa" --sign \"iPhone Distribution: My Company Pte Ltd (XCDEFV)"

A minor inconvenience is that xcrun requires the exact provisioning identity, e.g iPhone Distribution: My Company Pte Ltd (XCDEFV)) and the full path to the application to be signed. The project would therefore need to be built first using xcodebuild, with the build output path specified in the "Per-configuration Build Products Path" settings and passed to xcrun via the -v parameter.

However, at least with xcrun, I encountered no signing issues after repeated testings, and the generated IPA packages can always be installed on the device successfully. 
Read More »

Saturday, August 20, 2011

Xcode 4 bug: Crash when using Core Data Model from Xcode 3

I recently upgraded my Xcode from version 3 to 4.0.2 (build 4A2002a) and notice that Xcode keeps on crashing whenever I right click on a core data model used in an iOS application:


The error message reads:

Xcode encountered an internal logic error. Choose "Continue" to continue running Xcode in an inconsistent state.  Choose "Crash" to halt Xcode and file a bug with Crash Reporter. Choosing "Crash" will result in the loss of all unsaved data.

ASSERTION FAILURE in /SourceCache/IDEKit/IDEKit-303/Framework/Classes/StructureEditingAdditions/IDEStructureEditing+Additions.m:587
Details:  Assertion failed: [indexes lastIndex] <= [_targetGroup.subitems count]
Object:   
Method:   -structureEditingCanGroupSubitemsAtIndexes:
Thread:   {name = (null), num = 1}
Hints:   None
Backtrace:
  0  0x0000000100949773 -[IDEAssertionHandler handleFailureInMethod:object:fileName:lineNumber:messageFormat:arguments:] (in IDEKit)
  1  0x000000010006d394 _DVTAssertionFailureHandler (in DVTFoundation)
  2  0x00000001009a683e -[IDEContainerItemStructureEditingTarget structureEditingCanGroupSubitemsAtIndexes:] (in IDEKit)
  3  0x000000011e9e5661 -[IDEStructureNavigator _testOrGroupSelected:useContextualMenuSelection:] (in IDEStructureNavigator)
  4  0x000000011e9e2617 -[IDEStructureNavigator validateUserInterfaceItem:] (in IDEStructureNavigator)
  5  0x00007fff834a538b -[NSMenu _enableItem:] (in AppKit)
  6  0x00007fff834a50e8 -[NSCarbonMenuImpl _carbonUpdateStatusEvent:handlerCallRef:] (in AppKit)
  7  0x00007fff8348899c NSSLMMenuEventHandler (in AppKit)
  8  0x00007fff884357f7 DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) (in HIToolbox)
  9  0x00007fff88434d46 SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) (in HIToolbox)
 10  0x00007fff88452a81 SendEventToEventTarget (in HIToolbox)
 11  0x00007fff88481c35 SendHICommandEvent(unsigned int, HICommand const*, unsigned int, unsigned int, unsigned char, void const*, OpaqueEventTargetRef*, OpaqueEventTargetRef*, OpaqueEventRef**) (in HIToolbox)
 12  0x00007fff8849510f UpdateHICommandStatusWithCachedEvent (in HIToolbox)
 13  0x00007fff88431725 HIApplication::EventHandler(OpaqueEventHandlerCallRef*, OpaqueEventRef*, void*) (in HIToolbox)
 14  0x00007fff884357f7 DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) (in HIToolbox)
 15  0x00007fff88434d46 SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) (in HIToolbox)
 16  0x00007fff88452a81 SendEventToEventTarget (in HIToolbox)
 17  0x00007fff8849476f SendMenuOpening(MenuSelectData*, MenuData*, double, unsigned int, __CFDictionary*, unsigned char, unsigned char*) (in HIToolbox)
 18  0x00007fff885b77ae PopUpMenuSelectCore(MenuData*, Point, double, Point, unsigned short, unsigned int, Rect const*, unsigned short, unsigned int, Rect const*, Rect const*, __CFString const*, OpaqueMenuRef**, unsigned short*) (in HIToolbox)
 19  0x00007fff885b7dc2 _HandlePopUpMenuSelection7 (in HIToolbox)
 20  0x00007fff835da99b _NSSLMPopUpCarbonMenu3 (in AppKit)
 21  0x00007fff835dad4b -[NSCarbonMenuImpl _popUpContextMenu:withEvent:forView:withFont:] (in AppKit)
 22  0x00007fff8372bb37 -[NSMenu _popUpContextMenu:withEvent:forView:withFont:] (in AppKit)
 23  0x00007fff836304c0 -[NSControl _rightMouseUpOrDown:] (in AppKit)
 24  0x00007fff834270c7 -[NSWindow sendEvent:] (in AppKit)
 25  0x00007fff8335bafa -[NSApplication sendEvent:] (in AppKit)
 26  0x000000010085b36e -[IDEApplication sendEvent:] (in IDEKit)
 27  0x00007fff832f26de -[NSApplication run] (in AppKit)
 28  0x00007fff832eb3b0 NSApplicationMain (in AppKit)
 29  0x0000000100000eec

I may click 'Continue' on the dialog box to ignore the error, but subsequently Xcode would just crash randomly and too unstable to be used. For example, clicking on the Help menu would crash Xcode, but not any other menu.

The root cause, after much investigation, was the hierarchy of the .h and .m files for the core data class objects. In my original project from Xcode 3, the .h and .m files stayed under the .xcdatamodeld file in the project tree:

This was the default behavior on Xcode 3. However, Xcode 4 does not allow you to place these files under the .xcdatamodeld file, which is perhaps why it crashed since the case was never handled. I removed all the .h and .m files, regenerated them and put them under the CoreData group, and Xcode stopped crashing (for now):

I thought that was it, but unfortunately the problem did not stop there. Xcode again crashed when I attempted to commit the project to SVN:

The reason for the crash was 'editorDocument should be an instance inheriting from IDE-QuickLookdocument, but...' Perhaps only Apple knows what this really means... Nothing really helped, including restarting Xcode, closing all open source code files or cleaning/rebuilding the project. In the end, I committed to SVN from command line, checked out a fresh copy of the project and Xcode no longer crashed when committing!

Finally, I am somewhat disappointed at Xcode 4.0.2. The UI changed significantly and some of the important behavior also changed, perhaps without any documentation at all! For example, for each core data relationship, the Edit>Create NSManagedObject subclass... creates the methods to add/delete objects to the relationship in the .m file, but forgets to to put them in the header file. This results in plenty of compilation warnings if your code is from Xcode 3 and the core data class files are regenerated using Xcode 4. I have to resort to manually modifying the header files to add the correct method signatures...

I hope these issues will soon be fixed in the next release of Xcode.
Read More »