Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create iOS IPA from command line #13065

Closed
passsy opened this issue Nov 17, 2017 · 84 comments
Closed

Create iOS IPA from command line #13065

passsy opened this issue Nov 17, 2017 · 84 comments

Comments

@passsy
Copy link
Contributor

@passsy passsy commented Nov 17, 2017

Flutter doesn't allow me to build an .ipa which can be published to hockeyapp/fabric or the app store.

# creates an .app not an .ipa 
flutter build ios --release

Manual bundling is required:

xcodebuild -exportArchive -archivePath build/ios/iphoneos/Runner.app -exportPath build/ios/iphoneos/ -exportOptionsPlist ci/enterprise-options.plist

Flutter includes tools such as measuring the ipa size #4609, but I can't find a way to create an .ipa.

A followup issue for Generate a production-ready IPA from a single command is still missing

@eseidelGoogle
Copy link
Contributor

@eseidelGoogle eseidelGoogle commented Nov 17, 2017

I don't know the details, but if it's really a simple as just one command we could certainly wrap that in flutter build ipa or similar. :) I had previously been under the impression that the XCode GUI had to be involved with .ipa generation (e.g. it wasn't possible from the command line). @cbracken or @xster would know more.

@sethladd
Copy link
Contributor

@sethladd sethladd commented Nov 17, 2017

cc @mit-mit @devoncarew for CLI

@passsy
Copy link
Contributor Author

@passsy passsy commented Nov 17, 2017

Also running xcodebuild -exportArchive currently fails with xcode8 and xcode9

12:50:17 + xcodebuild -exportArchive -archivePath build/ios/iphoneos/Runner.app -exportPath build/ios/iphoneos/ -exportOptionsPlist ci/enterprise-options.plist
12:50:17 2017-11-17 13:50:17.672 xcodebuild[55483:4933639] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/nl/b7mbgsx16_g8zpdvhg37hm0h0000gn/T/Unnamed archive_2017-11-17_13-50-17.671.xcdistributionlogs'.
12:50:17 error: exportArchive: exportOptionsPlist error for key 'method': expected one of {}, but found enterprise
12:50:17 
12:50:17 Error Domain=IDEFoundationErrorDomain Code=1 "exportOptionsPlist error for key 'method': expected one of {}, but found enterprise" UserInfo={NSLocalizedDescription=exportOptionsPlist error for key 'method': expected one of {}, but found enterprise}
12:50:17 

with enterprise-options.plist

<?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>method</key>
	<string>enterprise</string>
	<key>teamID</key>
	<string>OUR_ID</string>
	<key>provisioningProfiles</key>
	<dict>
		<key>com.example.fluttertest</key>
		<string>comExample Enterprise Wildcard Distribution</string>
	</dict>
</dict>
</plist>
@xster
Copy link
Contributor

@xster xster commented Nov 17, 2017

@mit-mit
Copy link
Member

@mit-mit mit-mit commented Jan 8, 2018

@cbracken can you elaborate?

@cbracken
Copy link
Member

@cbracken cbracken commented Jan 8, 2018

Thanks for filing this issue!

Our currently supported iOS releasing process is described in Releasing for iOS and as noted, makes use of the Xcode UI. We don’t yet support a pure command-line mechanism for releasing to the App Store, and it’s not something we currently have prioritised but I’d certainly be more than happy to review pull requests.

@oemera
Copy link

@oemera oemera commented Jan 10, 2018

@cbracken thanks for your response. The error also happens when archiving within the Xcode UI. Please look into my issue: #13070. Based on the reactions I guess there are a couple of others who have this issue and can't release their app with the new version of Xcode.

If you have any questions please let me know.

@cbracken
Copy link
Member

@cbracken cbracken commented Jan 10, 2018

Thanks for pointing to #13070, I'll take a look at that today.

@alibitek
Copy link
Contributor

@alibitek alibitek commented Jan 10, 2018

xcodebuild: Deploy iOS app from Command Line
A really good summary of xcodebuild command line capabilities.
It even includes an example of how you can submit the .ipa to iTunes Connect from the command line.

@peace2knowledge
Copy link

@peace2knowledge peace2knowledge commented Jan 13, 2018

change to project ios directory,excute following command:

xcodebuild -workspace Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath $PWD/build/Runner.xcarchive
xcodebuild -exportArchive -archivePath $PWD/build/Runner.xcarchive -exportOptionsPlist exportOptions.plist -exportPath $PWD/build/Runner.ipa

exportOptions.plist is on ios directory.
file content like this:

<?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>compileBitcode</key>
    <true/>
    <key>method</key>
    <string>enterprise</string>
    <key>provisioningProfiles</key>
    <dict>
        <key>com.company.appname</key>
        <string>provisioningProfiles name </string>
    </dict>
    <key>signingCertificate</key>
    <string>iOS Distribution</string>
    <key>signingStyle</key>
    <string>manual</string>
    <key>stripSwiftSymbols</key>
    <true/>
    <key>teamID</key>
    <string>xxxxxx</string>
    <key>thinning</key>
    <string>&lt;none&gt;</string>
</dict>
</plist>

@oemera

@xster
Copy link
Contributor

@xster xster commented Jan 27, 2018

This is theoretically possible. I got it to work using Fastlane (which really just invokes xcodebuild archive).

We can document the flow more or possibly integrate Fastlane into flutter tools, though there are no immediate plans yet.

In the meantime, you can flutter build ios --release --no-codesign first and then use build_ios_app(export_method: "app-store", ...) in Fastlane with whichever signing method you prefer.

@csga5000
Copy link

@csga5000 csga5000 commented May 22, 2018

+1 on this. I think building iPa's would be great. During beta phase of apps I sometimes have to release new versions every couple days, and it takes forever when I have to test the app by running it on a device, then do flutter build --release, and then open xcode, and do product archive, and then in organizer upload to app store, and finally wait for apple to process my app.

I think this could be streamlined to just: flutter build ipa --release and then open application loader and deliver my app, (and then still wait for apple to process the app). It's still a bit slow, but at least I don't have to wait forever for flutter to build my app so I can wait another forever for xcode to archive my app. Admittedly it may be so slow partially cause my mac (not my main machine) is a mac mini and runs like a slug.

If you're not aware of application loader, you can open it through xcode XCode > Open Developer Tool > Application Loader, but you can also pin it to your task bar to open it without opening xcode (or install it separately, though that generally is very out of date).

@fuzzybinary
Copy link

@fuzzybinary fuzzybinary commented May 29, 2018

I would also appreciate this since we want to build ipas for distrubution through CI.

One (fairly crazy) issue I'm having with the current build system is that changing between flutter build ios --debug and flutter build ios --release actually modifies the Podfile.lock, and requires a fresh pod install, which is overriding a change I need to make in one of our Pods to avoid grpc/grpc#7406

Also because of this, we have to run flutter build ios --release followed by xcodebuild archive followed by xcodebuild exportArchive to build an ipa. This causes the app to be built twice (kind of a waste of time).

It seams like a lot of the build system could use refactoring. In general:

  • You should be able to generate ipas for a given flavor from the command line
  • flavors should be supported more directly from flutter cli (see #14648)
  • switching flavors shouldn't modify the Podfile.lock. This may require modifying Debug/Release.xcconfig to not rely as much on the Generated.xcconfig (or at least removing the location of the Flutter SDK from Generated and putting it in Debug/Release.xcconfig

I'm actually more than willing to help with this with some direction from the flutter team on what they think the best approach would be.

@eseidelGoogle
Copy link
Contributor

@eseidelGoogle eseidelGoogle commented May 29, 2018

@xster xster added this to Tooling in iOS Framework Oct 23, 2018
@xster xster moved this from Tooling to Publishing in iOS Framework Oct 23, 2018
@MisterJimson
Copy link

@MisterJimson MisterJimson commented Nov 26, 2018

This is what I ended up using as a complete release script, ready for uploading to the Play Store/App Store.

flutter build apk
cp build/app/outputs/apk/release/app-release.apk buildOutput/
flutter build ios
cd ios
xcodebuild -workspace Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath $PWD/build/Runner.xcarchive
xcodebuild -exportArchive -archivePath $PWD/build/Runner.xcarchive -exportOptionsPlist exportOptions.plist -exportPath $PWD/build/Runner.ipa
cd ..
cp ios/build/Runner.ipa/Runner.ipa buildOutput/

Edit: More detail.

You need to create the buildOutput folder in your project before running.

You need to create a exportOptions.plist file in your ios folder. See more into on that here

@zoechi
Copy link
Contributor

@zoechi zoechi commented Dec 11, 2018

Might be related to #24641, #13070, #12774

@xster xster added t: xcode and removed tool labels Jan 5, 2019
@zoechi zoechi added tool and removed tool labels Jan 7, 2019
@Hixie Hixie added this to the Goals milestone Feb 27, 2019
@neelu004
Copy link

@neelu004 neelu004 commented Apr 25, 2019

Hi, I am using Mac and in cmd I am running flutter build ios this command which will generates my Runner.app in build folder. I want to know how to run this Runner.app in real iOS device.
Please suggest some solution.
Thanks!

@jonahwilliams
Copy link
Member

@jonahwilliams jonahwilliams commented Aug 28, 2020

@MisterJimson thats a good suggestion for an initial pass - we could support accepting the plist via command line parameter.

I don't know if it would also be useful to support generating the config from command line options, since that would probably take a bit more research on my end. Bear with me, because I've literally never released software on the App Store, but is it common to generate the export options on the fly with other software, or is it something that is treated as a secret like the Android keystore

@xster
Copy link
Contributor

@xster xster commented Aug 28, 2020

Another option we have is to still break out the current flutter build ios into composable flutter build get my xcconfig ready for xcode and a flutter build actually xcodebuild parts but for better ergonomics, integrate flutter build get my xcconfig ready for xcode into a Flutter fastlane build action #17402 or document how to follow up with https://docs.fastlane.tools/actions/gym/ directly

@jonahwilliams
Copy link
Member

@jonahwilliams jonahwilliams commented Aug 28, 2020

@xster that is what the suggestion for --config-only basically is. Though at the point where you are archiving through another tool there isn't anything else for the flutter CLI to do.

@xster
Copy link
Contributor

@xster xster commented Aug 28, 2020

ya, we're saying the same thing. My only addition on the above comment is adding more ergonomics with fastlane, but that's a separate bonus step either way.

@MisterJimson
Copy link

@MisterJimson MisterJimson commented Aug 28, 2020

@jonahwilliams I've definitely seem both ways. I generally see 2 types of approaches that companies use, depending on the use case and preference. (And to be clear this is just for Ad hoc / App Store release, not day to day debugging)

Fastlane, which I think Flutter should integrate with or provide some type of easy path for usage at some point. I feel it would be difficult to replicate and maintain a similar feature set in Flutter.

Static exportoptions.plist that scripts pass to xcodebuild.

@jonahwilliams
Copy link
Member

@jonahwilliams jonahwilliams commented Aug 28, 2020

I think its unlikely we'll consider deeper Fastlane integration in the short-term. Integration with any sort of 3rd party service tends to have a high maintenance cost, both in terms of the actual integration, updates and the inevitable support requests ( mostly involving the service itself) which will start getting routed through our issue tracker.

We should only do so where absolutely necessary - i.e. we integrate with Xcode even though it is a pain because that is existential for iOS development.

@avi-mastov
Copy link

@avi-mastov avi-mastov commented Aug 28, 2020

I'm totally in favor of breaking flutter build ios into --config-only and the actual build so developers could then start xcode archive immediately and build only once.

That would require very little change to current developers' build process / CI/CD while speeding it up significantly, and on the other hand won't require any integration on Flutter's part.

@jonahwilliams
Copy link
Member

@jonahwilliams jonahwilliams commented Aug 29, 2020

Would love if someone wanted to checkout + sanity check #64848 :)

@Pe-te
Copy link

@Pe-te Pe-te commented Sep 7, 2020

Just a crazy idea after reading everything: Would it be possible to only run "fastlane gym" and add a build step to the project that does the "flutter --config-only" stuff? Or will this create all kinds of other problems?

@xster
Copy link
Contributor

@xster xster commented Sep 7, 2020

Not crazy. That's the intent here. Once you flutter --config-only, you can then build the iOS app via standard iOS mechanisms such as xcodebuild if you do so in your CI. Or just run or archive from Xcode UI, or use fastlane.

@Pe-te
Copy link

@Pe-te Pe-te commented Sep 8, 2020

That would be perfect, thanks! I first had understood that we still need to call it before xcodebuild.

@szotp
Copy link

@szotp szotp commented Sep 8, 2020

Seems like @xster misunderstood you. It's not possible, because --config-only changes some variables that affect the xcode project, and it also takes parameters that need to be be specified somehow, like entry file or dart defines.

@xster
Copy link
Contributor

@xster xster commented Sep 8, 2020

@Pe-te I think you're asking if you can just build with flutter build ios --config-only then fastlane gym right? Yes. If you run fastlane gym in verbose mode, I think it just wraps xcodebuild calls.

@szotp I might have misunderstood :) what do you mean by "it also takes parameters that need to be be specified somehow, like entry file or dart defines"? If you meant things like flutter run --target etc, they're actually Xcode project variables behind the scene as well.

@szotp
Copy link

@szotp szotp commented Sep 8, 2020

Would it be possible to only run "fastlane gym" and add a build step to the project that does the "flutter --config-only" stuff?

@xster Pe-te was asking if it was possible to automate flutter build ios --config-only as a build step inside xcodeproj, so that the only thing needed is fastlane gym. But that's not possible because it would mean changing xcodeproj while you are building it.

@xster
Copy link
Contributor

@xster xster commented Sep 9, 2020

Ah I misunderstood. You mean rather than in your CI script or in your Fastfile, whether you can recursively invoke flutter build ios --config-only inside Xcode itself with a build phase?

(Not tested) I think that works if you invoked it early enough before the build Flutter phase or the compile sources phase but doesn't that defeat the point? In other words, the whole point is to tell Xcode what configuration to build and archive etc. If Xcode already knows, then it could just hardcode those values from Generated.xcconfig (which is more or less all flutter build ios --config-only does) in your .xcodeproj directly.

@Pe-te
Copy link

@Pe-te Pe-te commented Sep 10, 2020

Thanks for all the clarifications, just as an addition why I asked:

Right now I have in my buildscript:

fastlane init_keychain
flutter build (soon --config-only)
fastlane build_my_app
fastlane close_keychain

So it would be nice to reduce it to "fastlane build_my_app" and do all the other stuff inside the Fastfile to have the fastlane error handling for everything. I know, I could call flutter through a fastlane sh() call, but that feels weird. 😉

So the idea was to pack the --config-only also into the build phases, but I am not sure what it does exactly and how it affects the build process if you change stuff while it runs.

@jmagman
Copy link
Member

@jmagman jmagman commented Sep 25, 2020

Can we track the --config-only flag in a different feature request issue? I only just saw this, and have a few concerns, but I don't want to further muddy this feature request.

As for flutter build ipa and why it isn't just done already:
There are so many options when you export an archive that would have to be exposed in that command just to pipe 1:1 into xcodebuild/exportOptionsPlist. You pretty much need an entire UI wizard flow in the CLA to make it manageable, which Xcode already has (and keeps updated with new App Store requirements). So either we build an identical wizard flow, or we expose all the below flags, and if you can successfully use them, then congrats! you are an Xcode guru and are probably already using xcodebuild directly.

xcodebuild -exportArchive -archivePath <xcarchivepath> [-exportPath <destinationpath>] -exportOptionsPlist <plistpath>
Available keys for -exportOptionsPlist:

	compileBitcode : Bool

		For non-App Store exports, should Xcode re-compile the app from bitcode? Defaults to YES.

	destination : String

		Determines whether the app is exported locally or uploaded to Apple. Options are export or upload. The available options vary based on the selected distribution method. Defaults to export.

	distributionBundleIdentifier : String

		Reformat archive to focus on eligible target bundle identifier.

	embedOnDemandResourcesAssetPacksInBundle : Bool

		For non-App Store exports, if the app uses On Demand Resources and this is YES, asset packs are embedded in the app bundle so that the app can be tested without a server to host asset packs. Defaults to YES unless onDemandResourcesAssetPacksBaseURL is specified.

	generateAppStoreInformation : Bool

		For App Store exports, should Xcode generate App Store Information for uploading with iTMSTransporter? Defaults to NO.

	iCloudContainerEnvironment : String

		If the app is using CloudKit, this configures the "com.apple.developer.icloud-container-environment" entitlement. Available options vary depending on the type of provisioning profile used, but may include: Development and Production.

	installerSigningCertificate : String

		For manual signing only. Provide a certificate name, SHA-1 hash, or automatic selector to use for signing. Automatic selectors allow Xcode to pick the newest installed certificate of a particular type. The available automatic selectors are "Mac Installer Distribution" and "Developer ID Installer". Defaults to an automatic certificate selector matching the current distribution method.

	manifest : Dictionary

		For non-App Store exports, users can download your app over the web by opening your distribution manifest file in a web browser. To generate a distribution manifest, the value of this key should be a dictionary with three sub-keys: appURL, displayImageURL, fullSizeImageURL. The additional sub-key assetPackManifestURL is required when using on-demand resources.

	method : String

		Describes how Xcode should export the archive. Available options: app-store, validation, ad-hoc, package, enterprise, development, developer-id, and mac-application. The list of options varies based on the type of archive. Defaults to development.

	onDemandResourcesAssetPacksBaseURL : String

		For non-App Store exports, if the app uses On Demand Resources and embedOnDemandResourcesAssetPacksInBundle isn't YES, this should be a base URL specifying where asset packs are going to be hosted. This configures the app to download asset packs from the specified URL.

	provisioningProfiles : Dictionary

		For manual signing only. Specify the provisioning profile to use for each executable in your app. Keys in this dictionary are the bundle identifiers of executables; values are the provisioning profile name or UUID to use.

	signingCertificate : String

		For manual signing only. Provide a certificate name, SHA-1 hash, or automatic selector to use for signing. Automatic selectors allow Xcode to pick the newest installed certificate of a particular type. The available automatic selectors are "Mac App Distribution", "iOS Developer", "iOS Distribution", "Developer ID Application", "Apple Distribution", "Mac Developer", and "Apple Development". Defaults to an automatic certificate selector matching the current distribution method.

	signingStyle : String

		The signing style to use when re-signing the app for distribution. Options are manual or automatic. Apps that were automatically signed when archived can be signed manually or automatically during distribution, and default to automatic. Apps that were manually signed when archived must be manually signed during distribtion, so the value of signingStyle is ignored.

	stripSwiftSymbols : Bool

		Should symbols be stripped from Swift libraries in your IPA? Defaults to YES.

	teamID : String

		The Developer Portal team to use for this export. Defaults to the team used to build the archive.

	thinning : String

		For non-App Store exports, should Xcode thin the package for one or more device variants? Available options: <none> (Xcode produces a non-thinned universal app), <thin-for-all-variants> (Xcode produces a universal app and all available thinned variants), or a model identifier for a specific device (e.g. "iPhone7,1"). Defaults to <none>.

	uploadBitcode : Bool

		For App Store exports, should the package include bitcode? Defaults to YES.

	uploadSymbols : Bool

		For App Store exports, should the package include symbols? Defaults to YES.

And that will just export the archive, it doesn't run any validators.

@jmagman
Copy link
Member

@jmagman jmagman commented Sep 25, 2020

Can we track the --config-only flag in a different feature request issue? I only just saw this, and have a few concerns, but I don't want to further muddy this feature request.

Never mind, it was already merged :-(

@jmagman
Copy link
Member

@jmagman jmagman commented Sep 25, 2020

The value add from flutter tooling would be the creation of the xcarchive--that's what is going to use all the build settings, compiling dart/embedding frameworks/thinning logic. So a flutter build xcarchive command.

The last step of making the ipa is entirely specific to the developer and what they are using the ipa for, and Flutter can't and shouldn't assume any of the exportOptionsPlist values. So the developer is already signed up for reading some Apple docs to get the right values, and at that point the value of running

xcodebuild -exportArchive -archivePath <xcarchivepath> [-exportPath <destinationpath>] -exportOptionsPlist <plistpath>

vs

flutter build ipa -exportOptionsPlist <plistpath>

is not that valuable (though we could add that wrapper, it's just not the hard or valuable part).

@jmagman
Copy link
Member

@jmagman jmagman commented Sep 25, 2020

Created #66619 to discuss there.

@MisterJimson
Copy link

@MisterJimson MisterJimson commented Sep 25, 2020

@jmagman I see your point but instead of mapping all of the exportOptionsPlist options we could just take a path to that file. Similar to how your keystore needs to exist on disk and how you have to manually setup your build.gradle file before you want to release for Android. There can be a happy medium for flutter build ipa.

@jmagman jmagman mentioned this issue Oct 8, 2020
9 of 10 tasks complete
@jmagman
Copy link
Member

@jmagman jmagman commented Oct 9, 2020

flutter build xcarchive is now available on 1.23.0-18.0.pre.

@jmagman jmagman mentioned this issue Oct 9, 2020
9 of 10 tasks complete
iOS Framework automation moved this from Publishing to Done Oct 22, 2020
@jmagman
Copy link
Member

@jmagman jmagman commented Oct 22, 2020

Command has been renamed flutter build ipa (alias flutter build xcarchive still works) and takes an optional --export-options-plist flag to make an IPA from the archive bundle. See flutter build ipa -h on the master channel for details.
Website documentation forthcoming at flutter/website#4852.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
iOS Framework
  
Done
Tools - Xcode review
  
Engineer reviewed
Linked pull requests

Successfully merging a pull request may close this issue.

You can’t perform that action at this time.