<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Rui on Medium]]></title>
        <description><![CDATA[Stories by Rui on Medium]]></description>
        <link>https://medium.com/@rygen?source=rss-2a8e219c534a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*n2ahLMgo09jtdQokkXAXWw.jpeg</url>
            <title>Stories by Rui on Medium</title>
            <link>https://medium.com/@rygen?source=rss-2a8e219c534a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 13 Jul 2026 10:29:54 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@rygen/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[A Tale of Xcode Server]]></title>
            <link>https://medium.com/karlmax-berlin/a-tale-of-xcode-server-d1649a4141b3?source=rss-2a8e219c534a------2</link>
            <guid isPermaLink="false">https://medium.com/p/d1649a4141b3</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[continuous-delivery]]></category>
            <category><![CDATA[xcode-server]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[ios-development]]></category>
            <dc:creator><![CDATA[Rui]]></dc:creator>
            <pubDate>Thu, 07 Jan 2021 08:54:54 GMT</pubDate>
            <atom:updated>2021-01-07T08:54:54.735Z</atom:updated>
            <content:encoded><![CDATA[<p>At <a href="https://karlmax-berlin.com">Karlmax Berlin</a> we’re transitioning our iOS Continuous Integration (CI) and Continuous Delivery (CD) workflow to Xcode Server.</p><p>In this blog post, I’ll tell you about our experience with it and mention a few caveats.</p><h3><strong>Before</strong></h3><p>As an agency, we work with different clients. The mobile apps we build are not only distributed internally but also to the clients and their testers. Oftentimes we also coordinate their App Store releases. <br>Behind the scenes, there are quite a few certificates and provisioning profiles to juggle.</p><p>Traditionally, we’ve used Jenkins and App Center / HockeyApp to build and distribute our apps.<br>For iOS, our CI/CD workflow consisted mostly of Fastlane or build scripts, triggered by Jenkins on a dedicated machine.</p><h4><strong>Pain points</strong></h4><ul><li><a href="https://docs.fastlane.tools/actions/match/">Fastlane Match</a> was used to sync certificates and profiles across the team. Configuring it for new projects was quite a time-consuming ordeal. Maybe we were just missing internal documentation for it. The few of us who had worked with it before couldn’t remember all the details of how to set it up consistently. Our Fastfiles were outdated and in need of a revision to follow current best practices.</li><li>Build scripts are harder to maintain than Fastlane and are also harder to reason about, at least to someone new to them. Using this approach, certificates and profiles still had to be moved around manually. <br>The huge raw build log isn’t fun to inspect. A syntax highlighting plugin could certainly improve that, but we didn’t look for one.</li></ul><p>We had a meeting to discuss the state of affairs in our iOS CI/CD workflow and propose alternatives. Some of us had heard of Xcode Server before but hadn’t played with it yet.<br>The two main action items from that meeting were:</p><ul><li>Use a dedicated Apple ID for all CI/CD tasks.</li><li>Try out Xcode Server.</li></ul><h3><strong>The Road to Xcode Server</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GvSL5FUEcU4ofHY-htluqQ.png" /><figcaption>WWDC 2017: Automatic Signing being introduced.</figcaption></figure><p>Apple <a href="https://developer.apple.com/videos/play/wwdc2017/403/">added support for automatic signing</a> to Xcode Server in 2017 and bought <a href="https://buddybuild.com">BuddyBuild</a> sometime after. <br>Automatic code signing and device provisioning was the main selling point for us. On top of that, Xcode Server gives you a nice user interface.<br>It also has an <a href="https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/XcodeServerAPIReference/index.html">API</a> that allows integrating it with our own tools and processes, shall we ever need it.<br>Xcode Server features a <a href="https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/xcode_guide-continuous_integration/MonitorBotsandDownloadProductsfromaWebBrowser.html#//apple_ref/doc/uid/TP40013292-CH10-SW1">web interface</a> with simplified reports and the possibility to download the build artefacts — .xcarchive and .ipa files. Using the web interface, other team members can trigger builds without needing Xcode on their machine.<br>Conceptually, Xcode Server bots are similar to Jenkins jobs and integrations similar to Jenkins builds.</p><p>We were convinced. Time to get our hands on it!</p><h3><strong>Configuration</strong></h3><p>I’ll keep it short as there are already several resources out there with instructions on how to set it up.</p><ul><li>You need to activate it under <strong>Xcode </strong>&gt;<strong> Preferences </strong>&gt;<strong> Server &amp; Bots</strong> on the machine where your CI/CD is.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UKCl_wDfzxbDfqFVhpDD3Q.png" /><figcaption>Server &amp; Bots tab in Xcode 12.</figcaption></figure><ul><li>Each developer shall add an Xcode Server account to their own Xcode, in order to create and configure bots, trigger integrations and see the results. Bots are saved on the machine with Xcode Server.</li><li>You can easily add pre-integration and post-integration scripts to carry out the tasks required by your project. <br>An example of a pre-integration script could be running pod install, if your project uses CocoaPods.<br>Post-integration script examples: uploading to App Store Connect / TestFlight (using <a href="https://help.apple.com/itc/apploader/#/apdATD1E53-D1E1A1303-D1E53A1126">altool</a>) and posting the integration results to Slack.</li></ul><p>If you’ve never configured Xcode Server before, you might want to check out a recent <a href="https://www.raywenderlich.com/12258400-xcode-server-for-ios-getting-started">tutorial by Adam Rush on raywenderlich.com</a>.</p><h3><strong>Caveats</strong></h3><h4><strong>Xcode upgrades</strong></h4><p>After upgrading Xcode to a newer version, Xcode Server also needs to be upgraded before new integrations are run.<br>Besides that, a bot can be configured to perform a test integration after each upgrade, to isolate any upgrades which might break an integration. However, before enabling this, you might want to check if the integration won’t have unintended consequences, such as uploading a new build to the client’s App Store Connect.</p><h4><strong>Building from a specific git branch</strong></h4><p>If you schedule an integration to run after new commits appear on a remote repository, it should run on the same branch you have checked out locally. However, this behaviour has changed between Xcode versions. You could add a pre-integration script with git checkout your-target-branch to prevent building from an unexpected branch.</p><h4><strong>Exporting to App Store Connect</strong></h4><p>You’ll need to provide a custom export options plist with method set as app-store. It’s one of the export options in the build configuration, inside <strong>Actions</strong> &gt; <strong>Archive</strong>.<br> This is all you need if you have the necessary permissions to sign the app and automatic signing is enabled for this bot.<br> Example ExportOptions.plist for automatic signing:</p><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br>&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;<br>&lt;plist version=&quot;1.0&quot;&gt;<br>&lt;dict&gt;<br>    &lt;key&gt;method&lt;/key&gt;<br>    &lt;string&gt;app-store&lt;/string&gt;<br>    &lt;key&gt;uploadSymbols&lt;/key&gt;<br>    &lt;true/&gt;<br>&lt;/dict&gt;<br>&lt;/plist&gt;</pre><p>The option to upload symbols can also be configured, as included in the example.</p><h4><strong>Code signing and provisioning</strong></h4><p>If you’re migrating an existing project and would like to take advantage of Xcode Server’s automatic code signing, you’ll need to ensure its build settings contain:</p><ul><li>A Code Signing Identity for automatic signing (<em>e.g.</em> iOS Developer or Apple Developer)</li><li>Automatic Code Signing Style</li><li>Automatic Provisioning Profile</li></ul><p>In case you must use manual code signing and device provisioning: in the custom export options plist you’ll need to define the signing style as manual and specify the team id, signing certificate, and provisioning profile. You can then upload both the certificate and the profile from your machine to Xcode Server, all via the bot configuration pane.</p><h3><strong>Final Remarks</strong></h3><p>The experiment with Xcode Server went quite well. A few of us have already successfully migrated projects to it!</p><p>Xcode Server isn’t as widely used as Fastlane and Jenkins, therefore there are fewer ready-made integrations with other services. Luckily, the tasks we currently need can be carried out by relatively simple post-integration scripts. <br>Depending on your requirements, you might also have to write a few scripts 🙀. The good news is you can write them in Swift! 😎<br>Alternatively, Fastlane could also be used for the post-integration part of the process, leaving code signing, device provisioning, and app export to Xcode Server.</p><p>Considering our workflow, using both Jenkins and Xcode Server — although possible — would be redundant. Thus, we’re slowly transitioning away from Jenkins for iOS apps.<br>The downside is that we don’t have a central place to monitor the CI and CD for both our Android and iOS apps anymore.</p><p>Updating the existing tools and writing good documentation would probably have yielded similar results. However, when we met to discuss improvements to our iOS CI and CD, most of us seemed interested in checking out Xcode Server. After all, who doesn’t enjoy the opportunity to experiment with a promising tool?</p><p>We’re excited about the future of Xcode Server!</p><h3><strong>Further reading</strong></h3><p>These resources have been useful during the process.</p><ul><li><a href="https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/xcode_guide-continuous_integration/adopt_continuous_integration.html">Apple’s documentation</a></li><li><a href="https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/xcode_guide-continuous_integration/EnvironmentVariableReference.html">Xcode Server Environment Variable Reference</a></li><li><a href="https://medium.com/xcblog">XCBlog</a></li><li><a href="https://honzadvorsky.com/pages/xcode_server_tutorials/">Honza Dvorsky’s 2015 write-ups</a></li></ul><p><em>This article was written in December 2020.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d1649a4141b3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/karlmax-berlin/a-tale-of-xcode-server-d1649a4141b3">A Tale of Xcode Server</a> was originally published in <a href="https://medium.com/karlmax-berlin">Karlmax Berlin</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>