Now that we have that in place, we can also rely on Jenkins to generate Ad Hoc & Distribution builds. Even more interesting, we can have Jenkins deploy these new binaries and notify your alpha/beta-testers there is a new version for them to play with.
References
There is actually a bunch of information out there on how to do this.General Information:
- Distributing Applications - official docs from Apple
- Distributing Enterprise Apps for iOS 4 Devices - if you have an Enterprise License
- iOS Wireless App Distribution
- How To Distribute Your IOS Apps Over The Air
- Distribute Ad Hoc Applications Over the Air (OTA)
- Managing Static Library project as a module like Framework on iOS project in Xcode4
- The workspace with the iOS project and related a static library project
- XCode 4 Archive Help
- How to create an IPA (Xcode 4) - good instructions on building
.ipa
, even if you don't use TestFlightApp.com
- Continuous Deployment of iOS Apps with Jenkins and TestFlight
- HOW TO: Extreme iPhone Distribution Automation
- Xcode4: Alternative Way to build IPAs
- Building iOS apps for OTA AdHoc distribution - one of the best
Preparing SampleApp for IPA
Our SampleApp project, although very simple, tries to mimic a real project, where you have 3rd party and/or your own libraries all inside a Xcode4's workspace.Because of that, if you just go ahead and try to "Product -> Archive" it'll most likely fail. That's why there a few links under the "Gotchas" section above :)
Let's prepare SampleApp so that is able to generate an IPA from within Xcode. Even if you don't actually build the IPA from Xcode, is good to have it all prepared just in case you want to do it.
SampleLib Project
- Deployment = Skip Install = Yes - set in project level
- Packaging = Public Headers Folder Path = Coverage & Debug = ../include/$(PROJECT_NAME)
- Packaging = Public Headers Folder Path = Release = ../../include/$(PROJECT_NAME)
- Search Paths = User Header Search Paths = $(BUILT_PRODUCTS_DIR)/../include $(BUILT_PRODUCTS_DIR)/../../include $(BUILT_PRODUCTS_DIR)/** - set in project level
- RootViewController.m, change the import statment to
#import <SampleLib/NSDate+Humanize.h>
- would also be nice to add an icon for your app...
Note: if Archive is grayed out, that's because you don't have SampleApp -> iOS Device scheme selected.
Quickest Way
If you don't want to use Jenkins and just want to get you App out there for people to give it a try, I'd say the best is to use TestFlightApp.com.- Just create an
.ipa
for your application (assuming you adjusted your project settings as described above...), using Xcode's "Product -> Archive"
- Follow the instructions on TestFlightApp.com to upload the
.ipa
and share if your friends.
Automated Way - Basic
If your project is not contained on a Xcode4 Workspace, then my suggestion is just to follow the instructions from Shine Tech's blog. It relies on the XCode plugin for Jenkins and describes how to automatically deploy your IPA to TestFlightApp.com.Automated Way - Advanced
As our SampleApp is currently contained on XCode4 Workspace, we cannot rely on XCode plugin for Jenkins as it does not currently supports workspaces.That's not a problem, we can manually build the IPA by running a simple script (thanks to Mike Nachbaur's post). We'll also use Jenkins to copy the build IPA to your Dropbox account and send a message to your testers, so then can play with the new version (and log a bunch of new bugs for you :).
We'll be also updating the Build version (CFBundleVersion) using the current date & build version. It's good for tracking purposes, but feel free to include it or not in your project.
SampleApp
- add the following configuration file under a newly created scripts folder:
rolima:~ $ cd ~/Work/SampleApp rolima:~/Work/SampleApp (master)$ mkdir scripts rolima:~/Work/SampleApp (master)$ cd scripts rolima:~/Work/SampleApp (master)$ vi build.config # For AdHoc, I'd rather send a DEBUG build. CONFIGURATIONS="Debug Release" # AdHoc Debug settings Debug_ProvisionFile=AdHoc.mobileprovision Debug_Codesign="iPhone Developer" # Release settings Release_ProvisionFile=AppStore.mobileprovision Release_Codesign="iPhone Distribution" # OTA settings -- used to build the link OTAURL="http://dl.dropbox.com/u/YOUR_USER_ID/MyApps" OTASmallIcon=SampleApp/icon.png OTALargeIcon=SampleApp/icon-512.png OTADeployPath=~/Dropbox/Public/MyApps OTATitle=SampleApp OTASubtitle=SampleApp for blog post
- Also, let's add a Run Script build phase to SampleApp, so that Jenkins is able to find the SampleApp.app
echo "Make sure Output folder exists..." mkdir -p $SRCROOT/../output echo "Save path to SampleApp.app..." echo $CODESIGNING_FOLDER_PATH > $SRCROOT/../output/$PROJECT_NAME.app.filepath.txt
- Let's start by creating a new job on Jenkins, so that we can schedule when it will build, and not mess with testing/code coverage automation jobs already there.
- Job Name = SampleApp-AdHoc - better if it does not have spaces, as it will be part of the IPA file name
- Build a free-style software project
- Source Code Management = Git
- Repository = /Users/YOURUSERNAME/Work/GIT-REPO/public/SampleApp.git or your GitHub repository URL
- Build Triggers = Build periodically = @weekly - for example, if you want a new AdHoc every week. Feel free to play with these settings
- Build - add an Execute Shell magic step that: bumps CFBundleVersion, compiles the app, signs it, generates the IPA, creates the plist for the app, and copy all that is needed to a Dropbox public folder. You should modify this accordingly to whatever your needs/requirements for deployment.
#!/bin/sh function failed() { echo "Failed $*: $@" >&2 exit 1 } # properties export OUTPUT=$WORKSPACE/output rm -rf $OUTPUT mkdir -p $OUTPUT PROFILE_HOME=~/Library/MobileDevice/Provisioning\ Profiles/ # read build configuration . "$WORKSPACE/scripts/build.config" # bump version BUNDLE_VERSION=`date +'%y%m%d'`.$BUILD_NUMBER cd SampleApp agvtool new-version -all $BUNDLE_VERSION cd .. # process builds for config in $CONFIGURATIONS; do echo "\n==================================================================\n" echo "COMPILE $JOB_NAME-$BUILD_NUMBER-$config ..." provfile=$(eval echo \$`echo ${config}_ProvisionFile`) codesign=$(eval echo \$`echo ${config}_Codesign`) cert="$PROFILE_HOME/$provfile" fileprefix="$JOB_NAME-$BUILD_NUMBER-$config" ipaname="$fileprefix.ipa" otaname="$fileprefix.plist" #invoke build on workspace xcodebuild -workspace SampleApp.xcworkspace -scheme SampleApp -configuration $config -sdk iphoneos4.3 build || failed build; #generate the IPA app_path=$(cat output/SampleApp.app.filepath.txt) xcrun -verbose -sdk iphoneos PackageApplication \ "$app_path" -o "$OUTPUT/$ipaname" \ --sign "$codesign" --embed "$cert" echo "\n==================================================================\n" echo "DEPLOY $OTADeployPath/$fileprefix...." #make sure folder exists mkdir -p $OTADeployPath/$fileprefix #copy icons cp $WORKSPACE/$OTASmallIcon $OTADeployPath/$fileprefix/Icon-57.png cp $WORKSPACE/$OTALargeIcon $OTADeployPath/$fileprefix/Icon-512.png #copy IPA cp $OUTPUT/$ipaname $OTADeployPath/$fileprefix #read properties info_plist=$(ls SampleApp/SampleApp/*Info.plist | sed -e 's/\.plist//') bundle_version=$(defaults read $WORKSPACE/$info_plist CFBundleVersion) #generate PLIST cat << EOF > $OTADeployPath/$fileprefix/$otaname <?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>items</key> <array> <dict> <key>assets</key> <array> <dict> <key>kind</key> <string>software-package</string> <key>url</key> <string>$OTAURL/$fileprefix/$ipaname</string> </dict> <dict> <key>kind</key> <string>display-image</string> <key>needs-shine</key> <true/> <key>url</key> <string>$OTAURL/$fileprefix/Icon-57.png</string> </dict> <dict> <key>kind</key> <string>full-size-image</string> <key>needs-shine</key> <true/> <key>url</key> <string>$OTAURL/$fileprefix/Icon-512.png</string> </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string>com.rolima.SampleApp</string> <key>bundle-version</key> <string>$bundle_version</string> <key>kind</key> <string>software</string> <key>title</key> <string>$OTATitle</string> <key>subtitle</key> <string>$OTASubtitle</string> </dict> </dict> </array> </dict> </plist> EOF done
- We now just need to email people with the link to the new IPA, so they can proceed with their testing.
- Just check Editable Email Notification and add something like this for the email message text:
<h3>$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS</h3> <p> Check console output at $BUILD_URL to view the results. </p> <p> To install this version on your iPhone, just click <a href="itms-services://?action=download-manifest& url=http://dl.dropbox.com/u/YOUR_USER_ID/MyApps/$PROJECT_NAME-$BUILD_NUMBER-Debug/ $PROJECT_NAME-$BUILD_NUMBER-Debug.plist">here</a>. If you have any problems, please contact <a href="mailto:administrator@yoursite.com">me</a> </p> <hr/> <b>Changes</b> <code> ${CHANGES} </code>
- Note that I used the Debug version of the app, and that is currently using the AdHoc.mobileprovision (see
build.config
). That requires that the UUID of the tester's device to be registered so he/she is able to install and run the app.
- Under the Advanced section for Editable Email Notification, you need to add a trigger for Success builds otherwise you'll not receive emails. Note that you can also add different recipients for each build case -- failure, success, etc -- so you can email your testers only when the build is successful.
- You should be able to save the Jenkins job, run it and start receiving the email notifications.
Either way, it may be helpful for you to become versed on some of the most common iOS app development terms and protocols that can help your unique organization run smoother and become more profitable. Read below as we discuss some of these and offer advice for your project, and good luck as you embark upon a journey to leverage the technological flexibility of applications to fit your needs.
ReplyDeleteios jobs