Sunday, 3 December 2017

One stop solution for setting up CI and CD system for iOS applications using Jenkins and Xcode - A complete reference guide (Part 2)

Hi,

This is a continuation of One stop solution for setting up CI and CD system for iOS applications using Jenkins and Xcode - A complete reference guide (Part 1). If in case you haven't read it, please consider doing so before further reading this tutorial.

If you have followed the Part 1 properly, by now you must have Jenkins system installed on your Mac machine which is completely configured to work with your manually signed Xcode project. You have your SSH connection established and your Jenkins constantly polls your Github/Bitbucket branch every 5 minutes to check if there is a change in code base.

If you have followed the Part 1 and managed to set up Jenkins, pat your self on the shoulder! Believe me you did a great job. That was the hardest part of your journey. From here onwards life will be more easy!

Though you have your Jenkins completely set up its not doing much as there are no build jobs added yet. We will do that in this part.

What will be accomplished by the end of this part ?
  • Add a job to build the code and run the unit test against the code.
  • Generate a test report and represent test results in colorful graphs.
  • If even a single unit test fails sending a email to developer with attached build log
  • Else archiving and exporting the ipa 
  • Finally uploading ipa to iTunesConnect
We will do all the above mentioned steps using Xcode command line tools. So buckle up and get ready for a smooth ride with Jenkins.

Step 1: Fire up your Jenkins and add a build job to run the unit test.
To do it, click on the item you added for your project in Part1 from the Jenkins dashboard.


Click on configure link



Once done you should be able to see the same setting page you were at the end of Part 1. Now scroll to Build section and click on add Build step. In the drop down menu select Execute Shell


Now go ahead and paste the following Xcodebuild command to build all the test target.
Now thats great, but dont you wanna generate test reports and see a colorful graphs representing test results? For that we will use Junit plugin. Modify your Xcode command as follow.
If you are a person with sharp eye, you must have noticed the usage of xcpretty. This is needed to format the test report generated by Xcodebuild to a form understandable by Junit.

Before you move on and run the build job, make sure you have xcpretty installed on your Mac. To do that open up your terminal and type xcpretty. If you see the help page your system has xcpretty installed. On the other hand if you receive the error command not found go ahead and install xcpretty.

Finally if you have noticed the command properly, it is creating a test report named reports.xml  which is placed in test-reports folder. These paths are relative to your project workspace. So obviously Jenkins will try to search for a folder named test-reports in your workspace and if it cant find it will fail the build step.

So go ahead and create folder named test-reports in your project workspace. Its often seen that Xcode will ignore the empty folder and hence its advised to create a empty file lets say test.txt in test-reports folder. Once done commit the project settings to your remote branch. By the end of it your remote branch should have a folder named test-reports. Cross verify it before proceeding further.

Great! Lets add one last step before running Jenkins for the very first time. Scroll down to Post Build Actions, click on Add Post Build Action and select Publish Junit Test Result Report.


This will read the reports.xml generated by Junit plugin and represent the report in colorful graphical charts. In order to do that, this action needs to know where exactly reports.xml is located. We know that Junit will produce the reports.xml in test-reports folder. So go ahead and provide reports.xml file path.


In your case on adding test-reports/*.xml you might see an error stating test-reports exists but *.xml does not exist. Thats ok because you have not run the Jenkins build before its likely that your test-reports folder does not contain any xml files.

But if Jenkins complains about test-reports folder being non-existent, thats a flag! Make sure your branch from which Jenkins pulls the code has a folder named test-reports. On failing to find such folder or file this action will mark the whole build as Failure. 

Finally absence of Unit test can not be treated as failure in running the unit test, hence don't forget to check the Do not fail the build on empty test results.

Thats it, now save the changes that should take you back to dashboard. Now click onBuild with Parameters and keep your fingers crossed.



If everything goes fine, you should see blue dot next to your build number in Build History. Now go ahead and click on drop down arrow next to build and select the console log to see the console logs.


In order to see the test reports, you need to run the build at least twice.

Step 2: Troubleshooting the simulator issue with xcodebuild.

Problem 1: SimDevice: iPhone X (AA063B5F-09D5-4908-A670-A4CE9236CC47, iOS 11.1 could not boot or Failed to boot iPhoneX simulator
If in case you open the console log and you happened to see the error that SimDevice: iPhone X (AA063B5F-09D5-4908-A670-A4CE9236CC47, iOS 11.1 could not boot or Failed to boot iPhoneX simulator best workaround is to run the code on Xcode manually and launch the simulator, once iPhone simulator is launched, reset the simulator. Now go ahead and kill the Xcode but leave the simulator alive. Head back to Jenkins and run the build again. Issue should be resolved.

Problem 2: Could not launch simulator because of duplicate simulator availability.
I have faced this issue only once, but I have not managed to reproduce the issue ever since. So I could  not post the exact error here. But I remember error says that Xcodebuild could not load the simulator because there are duplicate simulators available and Xcodebuild isn't sure of which one to launch. In that case modify your xcodebuild command in Build step to


All that we do here is rather than specifying the simulator by using its name and iOS version of it, you specify the UDID of the simulator itself. There by resolving all the confusions.

Step 3: Lets stop the build process even if a single unit test fails.
Unfortunately, even if all the unit test fails, the build step will still be considered as success as long as it completes the execution without any exception or error. But we would not want our build to upload to iTunesConnect even if a single unit test fails isn't it? Because there is no inherent support for that we have to deal with it on our own.

In order to stop the build even if a single unit test fails, we need a conditional build step. In order to get one go to Manage Jenkins and then select Manage Plugins. Select the available tab, and search for pipeline in search field. From the results that appear, check the Build Pipeline Plugin 



and click on install without restart. Once done with installation return to your project to continue configuration. 

Scroll to Build and click on Add Build step, this time you should see additional options namely Conditional Step (single) and Conditional Step (Multiple). Click on Conditional Step single.
Once added, select the drop down next to Run? and select Execute Shell


Now we should write a shell script that will conditionally decide whether to continue the with next jobs or to terminate the current job. We know that by the time we reach here our test reports are already generated and is kept in test-reports folder under project workspace. So we will now write a simple shell script which will parse this report.xml and look for a specific keyword like failure if found will call exit 1 on the thread there by stopping further builds from running, else it will let other jobs to continue execute. Sounds interesting? 

Lets go ahead and create a file named test.sh and lets keep it in Jenkins Document folder.
and enter the code below in test.sh

In order for Jenkins to execute test.sh it should have executable permission. But this is how the permission of a typical .sh file looks like

-rw-r--r-- 1 jenkins jenkins 162 Dec 3 23:51 /Users/Shared/Jenkins/Documents/test.sh

In order to provide executable permission, use chmod


Now if you look at the permission of test.sh it should look like

-rwxr--r-- 1 jenkins jenkins 162 Dec 3 23:51 /Users/Shared/Jenkins/Documents/test.sh

Clearly, Now you have executable permission to test.sh.

Now lets get back and add the command to execute test.sh in conditional build step we added just now. 

After adding the above command your Build section should look like



Now by default when a shell script fails the build will be considered as failed. But we rather all we want to do is to stop the further jobs being executed and denote the build as unstable. In order to do that click on Advanced button next to command field and select Don't run  for on evaluation failure.

Step 4: Archive and export the ipa using xcodebuild command
Select the Execute Shell for Builder option and add the following command to achieve and export the ipa using Xcode command line tools.

To create a archive use,

Parameter to be considered here is -archivePath. Obviously I have created a folder named build inside a document directory where I'll be saving the archive. You can decide where would you like to save it. If you are going to use the same command then make sure you create a folder named build inside Jenkins document directory. We will be using the path of the created archive in next command.

To export the ipa from created archive use,

As you can see the path provided to -archivePath argument here is exactly the same path you specified in previous command to create the archive. Second most important parameter here is the -exportPath as you can see for demo purpose am dumping the exported ipa on my desktop. You can decide to place it wherever you want it. Remember we will be using the path to the created .ipa in our next command to upload the build to iTunesConnect.

Wait a second! Xcode can't make the life of a developer so easy, there must be some thing fishy here! You are absolutely right. Though above commands executes absolutely fine on terminal when added to Jenkins will fail.

But why, Xcode why? Reason, both the commands try to access the distribution certificate to code sign the build and obviously Mac OSX restricts the access to Keychain so xcodebuild command fails miserably. Whats the work around then ?? Simple, allow the code sign to access your distribution certificate and private key.

To check, if code sign has access to your distribution certificate or not, go to Keychain -> login -> Certificates. Now click on the drop down arrow to expand your distribution certificate. Highlight the private key, and finally right click and select get info



When the pop appears, select the Access Control.  If your Access Control panel looks like 


That means code sign does not have an access to your distribution certificate and its private key and Jenkins will never be able to archive your code. In order to add code sign, you can either click on + below and add code sign if you know its path, or else simply open terminal, go to your local repo of the code and run the archive command

When you run it for the first time in terminal, OS will pop up authentication dialog asking you to enter the password for login keychain, after entering the password click on Always Allow. Thats it now if you go back and check the access control of your distribution certificate you must see code sign.


If in case you don't see code sign, make sure you kill the keychain and restart it. There seems to be some more bug with Keychain UI. Hah! Thats apple anyway.

Lets combine these archive and export ipa commands into a single command and add it as the command for Builder 
Go ahead and paste it in your Builder command field. After pasting the command, conditional step should look like


Now click on save. Return to Jenkins dashboard and click on Build with Parameters. If you have done everything right, your job should run successfully and should create a archive file in build folder and ipa file on desktop.

Step 5: Upload the ipa to iTunesConnect.
Make sure you have added the product in your iTunes Connect before proceeding further. Now that we have our ipa exported, all we need to do is to upload it to iTunesConnect using altool of Xcode.
In order to do it, scroll down to Build section and click on Add a Build Step and select Execute shell.

We will be using altool to upload the build to iTunesConnect. altool is usually present at

/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Support/altool

Simply copy and paste the above path in your terminal. If you see help page appearing, then you have altool available with you. Go ahead and add the below command to command field of Execute Shell

After adding the command it should look like


Few important things in the command above, --upload-app -f takes the file path of ipa as parameter. Because I have dumped my ipa at desktop for demo, I have given the link for the same. -u takes your iTunesConnect email id as argument and -p takes your iTunesConnect password as parameter.

Now click save and return to Jenkins Dashboard and click on Build with parameter. If everything goes fine you should be able to see that ipa has been uploaded to iTunesConnect. Check the console log for more info about the status of ipa upload.

In case you have any issue with uploading ipa using altool make sure you upload the build at least once manually using Application Loader to iTunesConnect. Try running the job again after that and everything should be resolved :)

Step 6: Send Email.
To send email from Jenkins, you need to configure Jenkins system. In order to do that, Click on Jenkins -> Manage Jenkins ->Configure System. Now scroll down to Extended email notification section. 

Specify the smtp server as smtp.gmail.com. Click on Advanced button. Check use SMTP authentication. Enter the email id from which email should be sent out as Username and its password in password field.

Check use SSL and enter the SMTP port as 465. Enter a comma separated email ids like 
abc@gmail.com,cc:bcd@gmail.combcc:xyz@gmail.com
to specify the Default Recipients. Click Save. After the configuration your extended email notification should look like 


Finally, scroll further down to E-mail Notification, Specify the smtp server as smtp.gmail.com. Click on Advanced button. Check use SMTP authentication. Enter the email id from which email should be sent out as Username and its password in password field.

Check use SSL and enter the SMTP port as 465.  Check Test configuration by sending test e-mail option, enter the email id to which you want to send test email and finally click Test Configuration.  Wait for sometime if everything goes fine, you should be able to see Email was successfully sent message. Now hit Save. After configuration your E-mail Notification should look like


Thats it. Now return to your project, click configure and scroll down to Post Build Actions. Click on Add Post build action and select Email Notification from drop down.


 Enter the white space separated email ids to which you want to send email when a build becomes unstable.

Click on the Add post build action again and this time select Editable Email Notification option from dropdown. Scroll down and click on drop down next to Attach build log and select Attach Build log. 



Now click on Advanced Settings. Scroll down to Triggers section. Click on Add Trigger and select Always.

Remove Developers and Recipient list by clicking red X and click on Advanced. Enter the comma separated email ids in the Recipient List to specify the email id to whom you wish to send email every time Jenkins builds your code. Finally click on Attach build log and select Attach Build log from the dropdown. 

Click Save.Go back to Jenkins Dashboard and click on Build with Parameter. Now if everything goes fine you should receive the email once Jenkins finishes all its jobs.

Step 7: Automate Jenkins job execution on committing code to remote branch.
We have already hooked up our Jenkins System to monitor a specific branch in Part 1 and we have specified Poll SCM to query the remote branch once every 5 minutes and if there is change in commit hash map Jenkins will automatically will pull the code and start the jobs.

So go ahead, make some push to the branch you have hooked up your Jenkins system and give Jenkins 5 Minutes. Within 5 Minutes Jenkins should automatically detect the change in the code and make a pull and start all its jobs and send out an email to you about the status of those jobs.

Thats it! Now you have a fully functioning Jenkins set up as CI and CD system for iOS application development using Xcode. Whats next? Take some time and lemme know how awesome I am by commenting below :) Go ahead and flaunt your new skill to set up CI and CD system on your own.

One stop solution for setting up CI and CD system for iOS applications using Jenkins and Xcode - A complete reference guide (Part 1)

Hi,

In a quest to streamline the product delivery process to QA and to app-store, I decided to set up a CI and CD system using Jenkins at my current company. Being a senior iOS developer, although I had experience working with CI systems before, I never had set up one on my own before. Being aware of Jenkins and Xcode's sour relationship, I knew I was up for a war :) and my beloved Xcode and Jenkins did not disappoint me a bit.
So, I decided to write a blog for those of you, who decide to go down the same road as I did.

What is CI ?
Continuous Integration (CI) is a development practice, which aids in improving the product quality by imposing frequent code push to the centralized repo, thereby making the code available to testers for testing more frequently and in smaller chunks. This approach helps in identifying and backtracking the bug, which makes it easier for the developers to fix them quickly as well.

What is CD?
Continuous delivery is the further extension of CI. On clearing all the automated tests, the system creates a build and deploys it to various environments automatically. This makes the executables available for testing, and helps in improving the transparency of development process as everybody is aware of current state of the build.

How will you achieve CI and CD?
Simple enough! At the high level, all you need to do is,
  1. Set up a branch and hook the Jenkins to monitor the branch. 
  2. Every time someone makes a push to the branch, Jenkins pulls the code from the remote branch and builds the code using Xcode command line tools.
  3. Jenkins then runs the unit tests written by developers to monitor the sanity of the code. (You can add the automated scripts written by your testers, as well, as a Jenkins job here). 
  4. If code fails to clear even a single unit test, a mail is sent out to developers to warn them about the instability of the code.
  5. If everything goes fine, Jenkins creates a .ipa file of the product and deploys it on iTunes connect, thereby making the build available to TestFlight.   
Sounds easy, isn't it? Trust me, it is indeed easy to implement it...just follow the tutorial entirely and you won't be disappointed! :)

To keep things simple, let's break the process it in two parts. In this part, let's install Jenkins, configure it, and modify the Xcode project settings to make it work with Jenkins.

And here's taking a deeper dive into the first part...

Step 1: Checking if JAVA exists and installing JDK 1.8 if it does not exists in your Jenkins machine.
This is one of the most important steps. Needles to say, I learnt it a very hard way. Most of the tutorials out there say, Jenkins needs JAVA but they do not mention that Jenkins expects you to have specifically JDK 1.8. Here is the link to official Jenkins Page, which clearly mentions that,
"At this point, Jenkins does not yet support Java 9 development releases. (Date : 2 December 2017)"
As JDK 1.9 is available as the latest version, just like me you too might be using it. So, remember to downgrade to JDK 1.8 to support Jenkins until Jenkins becomes compatible with the higher JDK versions.

To check if Java is installed in your system and to test the version of JDK installed in your system, open the terminal and type

If in case your system does not have JDK installed, Go to Oracle official website, select MacOS and download the dmg. On downloading the dmg, double click open it and follow the instructions to install JDK. Once done run the command above to confirm the installation.

Step 2: Download and Install Jenkins.
To download the Jenkins, visit the official website of Jenkins, scroll to long term support, select the MacOSX. This should start the dmg download immediately.


After you download the .pkg file, double click on it and the installer pops up. Follow the instructions to install the Jenkins. At the end of it your browser should automatically open up a new tab and show 


As it clearly shows, in order to confirm that Jenkins is being installed by the admin of the machine, the initial password is written in secret folder. In order to unlock the Jenkins you need to copy the content of the file and paste it here. In order to paste the content use 
Now, simply right-click and paste the copied password, and then click on Continue. On the next page, select the install suggested plugins. Jenkins takes a decent amount of time to download and install the plugins. Until then, grab a cup of tea, sit back and relax.

Once the download completes, Jenkins asks you to create an admin user. Go ahead and create your first admin account on Jenkins. Remember to save the username and password for future use
After entering the details, click on Save And Finish.  If everything goes fine, you should be able to see the Jenkins Dashboard.



Step 3: A dirty secret of Jenkins you should be aware of (shhhh, don't tell anybody. After all it's a bloody secret)
When you install Jenkins,  it creates a new anonymous user account of type Admin. (In case, you see a anonymous account created with Standard account type, you will have to change the account type to admin). You should be able to see it in System Preferences -> Users & Groups.


Now that we know, Jenkins runs in its own User and user has admin permission, we should switch to this user account to configure Jenkins further. But before we do that let's work out some more things being in current account, so that transition becomes smooth. 

Let's go ahead and rename this account, Am gonna call it as Jenkins. In order to rename the account, right-click on the account and select Advanced Options.



Jenkins creates an account without a password, so let's create a password for this user account. In order to set the password click on Reset password. Enter the new password and confirm it.

If in case, Jenkins created a Standard account (instead of the type, Admin), check the Allow user to administer this computer check box to make it an Admin account.



Step 4: Download and install Xcode on your Jenkins machine.
If your Mac does not already have Xcode installed, go ahead and download the Xcode from Apple's website

Step 5: Export your developer/distribution certificates and their private keys from KeyChain. 
No matter, whether you are setting up Jenkins on the same machine as your development machine (for testing purposes), or you are setting it up on an altogether different machine, you need to export your developer/distribution certificates. 

This is important because, Jenkins has its own account, Obviously the Keychain instance for Jenkins account and the Keychain instance for your current account will be different. So in order for Jenkins to be able to access your distribution certificate you need to copy it in Jenkins' own Keychain. Hence exporting is necessary.

As I had already mentioned in the beginning of the tutorial that we will be setting up the Jenkins to upload the build to iTunesConnect, and iTunesConnect will not accept .ipa, code signed with development certificate, so there is no point in exporting the development certificate from the context of this blog. Hence, I'll be exporting only the distribution certificate.

In order to export the distribution certificate, follow Keychain -> login -> Certificates. Select the distribution certificate of yours and then right click and select export. Enter the password and confirm password to encrypt the exported certificate. 



Each development/destribution certificate will have its private key counterpart in your key chain. Combination of private key and certificate is necessary to code sign your application. So we will have to export the keys as well. In order to export the keys, repeat the same procedure on the corresponding private key.

Now we need to pass these exported certificates to the Jenkins account. Use either air drop, gmail or pen drive/hard disk to copy these certificates. If in case you have both Jenkins and your development account on the same machine, simply copy these files to the Jenkins shared folder.

Path to Jenkins shared folder is /Users/Shared/Jenkins/Home. 

Step 6: Log in to Jenkins Account and continue further configuration.
Now that we have everything we need, it's time to say goodbye to the current user account. Hereon we continue our configuration in the Jenkins' account.

Step 7: Add the distribution certificate and private key exported to Jenkins Keychain.
In order to add the certificates to Keychain, simply double-click on the certificate. When prompted to enter the password, use the same password that you used while exporting the certificate. 

As of now, there seems to be some bug in the UI of Keychains and hence the added certificate might not reflect immediately. You might have to kill and restart the Keychain to see the added certificate and its key.

Many folks face issue with the Keychain UI. On double-clicking the certificate, sometimes, Keychain complains that it could not add your certificate. If you are one of such unlucky developers, don't worry, I have a solution for that as well.

The issue seems to be only a UI bug and you can circumvent the issue by turning to our old pal Terminal.
Now open up your Keychain and make sure your distribution profile and private keys are added to Keychain.

Step 8: Setting up SSH key with Github/Bitbucket.
In order for your Jenkins system to pull your code from Github/bitbucket you need to authenticate Jenkins with Github/Bitbucket. Now there are multiple ways, one that I prefer is setting up SSH key.
Though for the purpose of this tutorial, I'll be using BitBucket as the steps are identical to Github.  

As a first step, we need to generate the RSA private and public keys to authenticate the Jenkins system with Github/Bitbucket. In order to generate RSA keys, open your terminal and enter 
Once you are done with it, you should be able to see two files named id_rsa and id_rsa.pub in your ~/.ssh folder.

You will copy the content of id_rsa.pub (RSA public key) and add it to your Github/Bitbucket account. In order to copy the content of id_rsa.pub use
Now login to your GitHub / Bitbucket account and add the copied RSA public key. In order to add the SSH key to GitHub follow Github help page.

In order to add SSH key to Bitbucket, go to your Account -> bitbucket settings -> Security -> SSH Keys. Click on Add key and paste the copied RSA public key. That's all. If you still feel confused follow the official Bitbucket help page.

Finally in order to test the SSH connection open your terminal and type 
If everything goes fine, you should be able to see "You can use git or hg to connect to Bitbucket" That's it...now your Jenkins System can pull the code from Github/Bitbucket whenever it wants.

Step 9: Clone your repo to further configure your Project Settings to make it work with Jenkins.
Now that you have configured your system to use SSH connection go ahead clone your repo. This repo will not be used by Jenkins. We will use this repo to modify the project settings to make it work with Jenkins and will push the changes back to the remote repo. You can change the project settings on any system and you need not clone the repo on your Jenkins machine, but there is a very good reason why I am asking you to do it on the Jenkins machine. Once the changes are done and you push the code back to the remote repo, you can delete the local repo from the Jenkins machine.

Step 10: Login to Xcode and sync all the provisioning profiles.
Fire up your Xcode. Go to Preferences -> Accounts. Add your developer account, and click on Download Manual Profiles to download all the provisioning profiles associated with your developer account.

Step 11: Change your Project from Automatic Code signing to Manual Code signing.
I know that Xcode9 command tools now supports automatic code sign with the usage of -allowProvisioningUpdates. However, I have not had a chance to work with the automatic code sign yet, and I've heard that many developers are facing issues with it. That's why I plan to take some time to try it out myself, and will then update this post accordingly. 

So, for now, if your project happens to use Automatic Code sign, then you will have to change it to Manual Code Sign. If your project is already manual code signed, then you can skip this step.

In order to change to Manual Code Sign, you need to create a Distribution Provisioning Profile. To create one, Go to developer portal, select provisioning profiles and create a new distribution provisioning profile. Don't forget to add your distribution certificate (that you just imported and added to Jenkins Keychain) to the distribution profile.

Once done, come back to Xcode, go to preferences -> Accounts, and click on Download Manual Profiles to download the newly created provisioning profile. Now open your project setting for each target in your project and uncheck the automatically manage code signing. Select the provisioning profile that you have just created from the drop-down next to Provisioning Profile. 

In case, you don't see the provisioning profile you just created, download the provisioning profile from the developer portal, and double-click on it to install it on your machine. Once you do that, you should be able to see the provisioning profile in the drop-down automatically. 

Once you select the provisioning profile, Team and Signing Certificate should be selected automatically. Else, try selecting it on your own. 

Once done, try running the project just to confirm all your code signing changes are fine.

Step 12: Placing Provisioning profile in proper folder and finding its UUID.
If in case you happened to skip step 10/11, then you might not have the provisioning profile in its place. Xcode command that we gonna use soon will try to look for provisioning profile at /Users/Shared/Jenkins/Library/MobileDevice/Provisioning Profiles. In order for your xcodebuild command to run fine you must add the provisioning profile to  Provisioning Profiles folder.

This you can do in two ways.
1. Use your Xcode and sync all your manual provisioning profile as mentioned in Step 10.
2. Download the provisioning profile manually and place it at path /Users/Shared/Jenkins/Library/MobileDevice/Provisioning Profiles. You might find that Folder MobileDevice/Provisioning Profiles does not exists, in such case you have to create one on your own. Because it involves loads of manual work I prefer using Xcode.

We need to find the ID of the provisioning profit that we jus created. ID of the provisioning profile is nothing but the part of file name prior to .mobileprovision extension. You can open up /Users/Shared/Jenkins/Library/MobileDevice/Provisioning Profiles folder and you should see at least one Provisioning profile by now.

If there is only one, then there is no confusion, simply copy the file name till .mobileprovision extension.

If there are multiple mobile provisioning profiles, then it becomes difficult to identify which is the correct provisioning profile or if you have installed provisioning manually even then file name will not tell you the ID of provisioning profile.

In such cases, download the provisioning profile manually from developer portal and open it using TextEdit, search for key <key>UUID</key> value for this key will be the identifier of your provisioning profile. Copy it. We will use this key in next step.

Step 13: Create a ExportOptions.plist to assist xcodebuild tool
Starting from Xcode 9, you need to pass ExportOptions.plist as a runtime argument to  xcodebuild command to archive and export your ipa. This ExportOptions.plist is a simple plist which contains the configuration details for archiving and exporting ipa. Few such configuration details are

1. SigningStyle : Manual or Automatic
2. Method : app-store, adhoc, development
3. ProvisioningProfiles : Name of provisioning profile/id of provisioning profile
4. TeamID : Team ID associated with your distribution certificate.

Though the structure of ExportOptions.plist is very simple, there seems to be a tons of confusion about it among developers. Thanks to Apple, for changing the structure of ExportOption.plist significantly from Xcode 8 to Xcode 9 there by creating a huge confusion among developers. Here is a copy of working ExportOptions.plist.
Though it looks simple, in case you are wondering how I figured it out, I followed this tutorial . The hack of actually exporting an ipa using Xcode 9 and then copying the content of ExportOptions.plist was amazing and helped me a lot to understand the structure of it.

Add this file to your projects root folder and now you are in good place to commit your code back from your local repo to remote. 

Step 14: Fire up your Jenkins and and join the pieces  together to set up our CI.
Though this is a Jenkins integration tutorial, other than installation part we haven't talked much about Jenkins or about its configuration. To be frank, its simple the biggest pain was Xcode and we dealt with it already. 

Open your web browser and enter localhost:8080, this should open Jenkins webpage and ask you to login. On successfully logging in, click on New Items, and enter the name of your project in the textField. Select the Freestyle project and click OK


On creating a new Item,  check This project is parameterized option and select String Parameter. In the name field enter branch and in default value field enter the name of the branch from which you would want Jenkins to pull the code from. By default it always pulls the code from master.



scroll to Source Code managed meant and select Git. Enter the SSH repository URL for  Github/Bitbucket. Point to consider here is to use SSH url and not HTTPS url because we have set up our Jenkins machine to use SSH connection in Step 8. Once you add the URL you should see something like



Clearly, its complaining that Jenkins could not access your Github repo. You need to add credentials to allow Jenkins to access the repo.

To do that click on add, when the popup appears select the Kind to SSH username with Private key. Enter your Github/Bitbucket username in username field. 

Select Enter directly option for Private Key, now copy the content of /Users/Shared/Jenkins/.ssh/id_rsa using pbcopy and paste it here.

Enter the pass phrase you used, while creating RSA key in the pass phrase field. 

Once done it should look like


Now click on Add and return to the Source Code Management section and select credential that you just created from drop down. Give it sometime and all the errors should disappear. 

Now scroll down and select the branch, your Jenkins should pull the code from. We have already added a variable named Branch to point to the branch we need earlier using This project is parameterized option remember? Simply use that variable now.


There you go! Now your Jenkins is fully configured to pull the code from the branch you specified whenever it needs.

Before we wind up this part, Lets do one last thing. We would want Jenkins to pull the code from Github/Bitbucket every time somebody pushes the code to your specified branch don't we? Now we can do it with WebHook plugin, But because not all of us have a public IP on our Jenkins machine, we might not be able to set up Webhook so as an alternative we will use Poll SCM.

We will set up our Jenkins system to poll the Github/Bitbucket branch we specified at a regular interval and check the hash map of the previous commit. If there is change in hash map (That means some new code has been pushed to Branch) Jenkins will pull the code base and will continue with its build operations. To do that simply scroll to Build Triggers and check the Poll SCM and enter H/5 * * * *



Thats all. Now your Jenkins will poll your specified branch every 5 minutes and check if there is any new code being pushed. If yes, it will pull the code and build it else it will continue to ignore it. Thats great!! Isn't it?

Thats all for Part 1. Now we have Jenkins installed and completely configured to work with our Xcode project. But the actual build job yet to be added to make it work. Believe me thats the easiest part. See you in Part 2.