XCArchive to ipa

With Xcode 7 having come out recently, Apple has once again changed things in their CLI. After far longer than it should take, I believe I have finally stumbled upon the “proper” way to turn an xcarchive into an ipa.

The best thing you can do with this is have your CI server do the archive, and then turn the archive into the ipa, and upload to wherever.

Archive Path


The path to the archive should be fairly obvious when doing this by hand; however, many people get confused on this one when it comes to CI.

The easiest thing I have found to do is to access the archive path from a post archive step. If you do so, it is just an environment variable. In ruby, you can access it through

You can also access the product name at the same time if you want to write a single generic scrip that you use everywhere.

I personally have run a ruby script in the post archive to save off these environment variables for later. In TeamCity, this is fairly easy.

This way, the environment variables will be carried over from the post archive step into the subsequent build steps performed by TeamCity. I prefer this over having the archive do the whole convert to ipa and upload because if I need to do an archive on my computer, I don’t need to have anything set up any differently. The archive will print a couple extra lines to the log, but they’ll be ignored. But when doing an archive in TeamCity, it will save off the environment variables, and use them in a rake script to do all that extra stuff.

Export Path


I really wasn’t going to give this one a section because it’s so obvious, but I figured, consistency.

The only thing you really need to know here is that the export path is not the path that the ipa will appear at. It is actually going to be a folder that has the ipa inside of it.

Export Options plist


This is the single most important option. As many people have discovered since the release of Xcode 7. This is the command that most people should be familiar with when it comes to converting their xcarchive to an ipa

alternatively, you may be familiar with this

or, if you’re doing enterprise apps, you may be familiar with this one

If you run those commands, you’ll be pleased when everything looks right. You get an ipa, and you go on your merry way. However, when uploading to the app store, you will be frustrated to find that you get some validation errors. In enterprise distributions, you may not even know anything is wrong.

If you unzip the ipa, you’ll find a big difference.

If you unzip the old ipa (or a new one, built with the old commands) you’ll get a Payload folder with your Product.app inside. However, you’ll be missing  BCSymbolMaps, SwiftSupport and Symbols.

If you export the ipa using Xcode, you won’t be missing these folders. This leads many people to believe that you simply can’t create your ipa from the command line any more. That is not the case. If you use the new command, then every option Xcode gives you in the GUI can be found inside of the export options plist.

The export options plist has a pretty simple set of keys. Unfortunately, Apple hides them in a part of the documentation that no one ever looks. The helps page.

All you have to do is run xcodebuild -help and one of the last things printed to the terminal is the export options plist’s available keys. The documentation also has the default values for the keys, so building your plist is pretty fast and easy. In many cases, your plist will look like this

I have seen posts where people say the minimum is actually this

But according to the documentation, the team id is not required, so I wouldn’t worry about it. If you do want to put it in, then go look inside of MyApp.xcodeproj/project.pbxproj and look for  DevelopmentTeam = <some 10 character thing>. It should be pretty easy to find.

If you’re doing enterprise builds instead of app store builds, you’ll need a plist with this

 


When you generate an ipa this way, it will result in a properly formed ipa without the need to write custom scripts to inject the required SwiftSupport  folder into your ipa.

I just wish Apple would have made this easier to find.

9 thoughts on “XCArchive to ipa

  1. Thank you so much for this write up Daniel! It has been very helpful.

    I am trying to generate an enterprise ipa and I notice now that -exportArchive has to be used with -exportOptionsPlist, I am no longer able to use -exportProvisioningProfile and -exportSigningIdentity. I get the following error:

    “xcodebuild: error: The flag -exportProvisioningProfile cannot be specified along with -exportOptionsPlist”

    Have you figured out how to build an ipa by specifying the provisioning profile and signing identity in the command line?

    • You shouldn’t need to use the exportProvisioningProfile flag for enterprise builds anymore.
      When you specify enterprise in your plist for the mode, that will cause the ipa to be built for enterprise distribution, which means embedding the provisioning profile that was specified in your target’s build settings.

      If you want to override your target’s signing/provisioning, then you can do it at build time with something like “xcodebuild PROVISIONING_PROFILE= CODE_SIGN_IDENTITY=
      But I don’t think that you can use the exportOptionsPlist to tell it to override the provisioning profile specified at build time.

      • It looks like very inconvenient. So if we have 100 enterprise customers, we should create 100 build for each customer?

        • I’m not entirely certain if I understand.
          Enterprise distribution is meant to be able to distribute internally to your own company. For example, distributing an internal tool, or distributing to your co-workers, etc.
          What you described sounds more like business to business https://developer.apple.com/programs/volume/b2b/

          That being said, using Apple’s new api for exporting an XCArchive to an ipa does not appear to have the ability to resign the ipa. If you had an ipa that was exported with one provisioning profile, you could probably write a script to unzip it, and resign it with a different key/profile so that you didn’t have to rebuild the code repeatedly. Probably a script that does something similar to what the gui tool https://github.com/maciekish/iReSign does

  2. Hi Daniel,

    Very nice post clearly explaining the new command. It took me half of last Friday to also notice that not using -exportOptionsPlist with xcodebuild is deprecated and you can see some signing warning while doing codesign check in my case.

    When you use that flag however, you cannot export ipa directly. Xcode only can makes an archive. What is the method that you use to extract .ipa form archive? Does it involve xcrun?

    Cheers,

    • I’m not quite certain that i understand completely, so if i get something wrong, please let me know.
      When you run the command
      xcodebuild -exportArchive -archivePath [path to archive] -exportPath [path to output] -exportOptionsPlist [path to export options plist]
      then your options plist will have a property in it “method”
      Method can be
      “app-store” which results in an ipa
      “ad-hoc” which results in an ipa (i think)
      “package” which i think is an xcarchive
      “enterprise” which is an ipa
      “development” which i think is specific to mac apps
      “developer-id” which i also think is specific to mac apps

      So you should be able to just use the -exportOptionsPlist parameter with the proper method value in the plist

      • This command doesn’t work when archive is generic archive. This can happen when app contains some other libs. Can you please suggest how to fix this issue

  3. xcodebuild clean -workspace Test.xcworkspace -scheme Test -sdk iphoneos -configuration Release

    xcodebuild -workspace Test.xcworkspace -scheme Test -archivePath Test.xcarchive archive

    xcodebuild -exportArchive -archivePath Test.xcarchive -exportOptionsPlist exportOptions.plist -configuration Distribution -exportPath build DEPLOYMENT_POSTPROCESSING=YES STRIP_INSTALLED_PRODUCT=YES SEPARATE_STRIP=YES COPY_PHASE_STRIP=YES EMBEDDED_CONTENT_CONTAINS_SWIFT=NO RETAIN_RAW_BINARIES=NO

    This is all what I did. Unzip the .ipa to see its content. Folder ‘Frameworks’ is bigger than the one exported by XCode.

    Anyone has same experience?

Leave a Reply

Your email address will not be published. Required fields are marked *