<?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[iOS in Source Diving on Medium]]></title>
        <description><![CDATA[Latest stories tagged with iOS in Source Diving on Medium]]></description>
        <link>https://sourcediving.com/tagged/ios?source=rss----217db81c7a01--ios</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>iOS in Source Diving on Medium</title>
            <link>https://sourcediving.com/tagged/ios?source=rss----217db81c7a01--ios</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 24 Apr 2026 14:33:14 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/sourcediving/tagged/ios" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <item>
            <title><![CDATA[Solving Mysterious Logout Issues on iOS 15]]></title>
            <link>https://sourcediving.com/solving-mysterious-logout-issues-on-ios-15-8b818c089466?source=rss----217db81c7a01--ios</link>
            <guid isPermaLink="false">https://medium.com/p/8b818c089466</guid>
            <category><![CDATA[developer]]></category>
            <category><![CDATA[bugs]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Liam Nichols]]></dc:creator>
            <pubDate>Thu, 25 Nov 2021 14:36:33 GMT</pubDate>
            <atom:updated>2022-02-04T17:04:30.744Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="A screenshot of Xcode and the iOS Simulator — The loaded Xcode Project is the Cookpad application and the simulator is displaying the login screen within the Cookpad app." src="https://cdn-images-1.medium.com/max/1024/1*NvRaYk-DFuuyJ-QGw2y4nw@2x.png" /></figure><p>At Cookpad, after the public launch of iOS 15 we started to receive reports from users telling us that they were being repeatedly logged out when opening our app. This came as quite a surprise since this wasn’t something that we spotted during our testing with the iOS beta releases.</p><p>If you are here for a fix then scroll right down to the conclusion, but if you want to understand more on how we debugged this particular issue then let’s begin.</p><h3>Reproducing the reported issue</h3><p>The details in the user reports were limited and all we knew was that starting with iOS 15, people would return to the app and find themselves logged out.</p><p>We had no video or no concrete steps to reproduce the issue so I found myself trying to launch the app in every way possible hoping to see it for myself. I tried reinstalling the app, I tried launching with and without a network connection, I tried force quitting and after a good thirty minutes with no luck I gave up and started responding on the ticket with my (lack of) findings.</p><p>It wasn’t until after that when I unlocked the phone again and launched Cookpad without even trying that I found myself at the login screen just as our users had been reporting!</p><p>After that, I wasn’t able to reproduce accurately but it seemed to be related to leaving my phone for a period of time before coming back to use it again.</p><h3>Narrowing down the cause</h3><p>I was afraid that reinstalling the app from Xcode could potentially stop the issue from being reproducible so before doing so, it was time to look through the code and try to narrow the issue down. I came up with three potential causes based on our implementation:</p><ol><li>Data was being cleared from UserDefaults.</li><li>An unexpected API call returning HTTP 401 and triggering a logout.</li><li>An error being thrown from the Keychain.</li></ol><p>I was able to eliminate the first two potential causes thanks to a few subtle behaviours that I had observed after reproducing the issue myself:</p><ul><li>The login screen didn’t ask me to pick a region — This indicated that data in the UserDefaults was fine because our <em>‘has shown region selection’ </em>preference was still being persisted.</li><li>The main UI was not shown, even briefly — This indicated that there was no attempt to make a network request so it was probably too soon for the API to have been the cause.</li></ul><p>This left us with the Keychain leading me onto the next question: What has changed and why is it so difficult to reproduce?</p><h3>What has changed and why is it so difficult to reproduce?</h3><p>I skimmed through the release notes, ran a quick search in google and I couldn’t find anything so I had to keep digging to better understand the issue.</p><p>Access to the Keychain data is provided via the <a href="https://developer.apple.com/documentation/security">Security framework</a> which can be notoriously tricky to grasp. While there are lots of third-party libraries that wrap this framework to make things easier, we maintain our own tiny abstraction based on some Apple sample code.</p><p>Looking at this code, we call the <a href="https://developer.apple.com/documentation/security/1398306-secitemcopymatching?language=objc">SecItemCopyMatching</a> method to load our access token and it returns the data along with an <a href="https://osstatus.com">OSStatus</a> code that describes the result. Unfortunately for us however, while our wrapper would throw non-success results with the status code for debugging purposes, we were discarding this information in the next layer up by just treating an error as nil.</p><p>We operate a weekly release schedule thanks to a lot of automation and at this time, the next cut-off point for our upcoming release (code freeze) was the following day. Because we still didn’t fully understand how widespread the issue was and we weren’t sure if we were going to be able to ship a fix before code freeze, I took this opportunity to fix the lack of observability by adding some extra non-fatal logging using Crashlytics:</p><figure><img alt="A diff of code showing how we improved error recording and documentation around behaviours when loading session information." src="https://cdn-images-1.medium.com/max/1024/1*1OrqueRbAPNedHg3o0kxpA.png" /><figcaption>While we weren’t in a position to change the behaviour of how we load the session, we were able to start logging errors and to better document the current behaviour of our implementation.</figcaption></figure><p>The result gave us some great insights that we could then observe over the following weeks:</p><figure><img alt="A screenshot from the Crashlytics console showing the metrics of the non-fatal issue slowly decrease as a fix was rolled out" src="https://cdn-images-1.medium.com/max/1024/1*PMLjsVB8_HoXt85do_8d0A.png" /><figcaption>The number of impacted users slowly decreases in version 10.58.0 and 10.59.0 due to a mitigation we introduced while we worked to identify the root cause that was fixed in 10.60.0</figcaption></figure><p>At this point, I was able to catch the exact error code being returned. The culprit was <a href="https://developer.apple.com/documentation/security/errsecinteractionnotallowed?changes=_3"><em>errSecInteractionNotAllowed</em></a>:</p><blockquote>Interaction with the Security Server is not allowed.</blockquote><p>This error is telling us that we’re trying to read from the Keychain at a point in time when the data is not available to us. This can typically happen if you try to read data that has been stored with the accessibility set to something like <a href="https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlocked"><em>kSecAttrAccessibleWhenUnlocked</em></a> while the device is still locked.</p><p>Now this makes total sense but the only problem is that in Cookpad, we only ever read from the Keychain when the app is launching, and my assumption was that the user must have tapped the app icon to launch the app and therefore the device should always be unlocked at that point, right?</p><p>So what has actually changed then? Even when I am able to reproduce the issue, I am 100% sure that my phone was unlocked at the point of me tapping the app icon so I can’t understand why this Keychain error would have been thrown.</p><p>Determined to find the cause, I replaced the implementation of our app with a debugging tool that would attempt and log Keychain reads at different points in its lifecycle:</p><figure><img alt="A screenshot of a debugging interface in the app showing log messages that indicate failure or success when attempting  to read keychain data at different points in time." src="https://cdn-images-1.medium.com/max/404/1*XQBpYRC3wZGo2K39OT9bNQ@2x.png" /></figure><p>In scenarios where I was able to reproduce the issue, I observed the following results:</p><ul><li><em>main.swift </em>— Failed (<a href="https://developer.apple.com/documentation/security/errsecinteractionnotallowed?changes=_3"><em>errSecInteractionNotAllowed</em></a><em>)</em></li><li><em>AppDelegate.init()</em>— Failed (<a href="https://developer.apple.com/documentation/security/errsecinteractionnotallowed?changes=_3"><em>errSecInteractionNotAllowed</em></a><em>)</em></li><li><em>AppDelegate.applicationProtectedDataDidBecomeAvailable(_:)</em> — Success</li><li><em>AppDelegate.application(_:didFinishLaunchingWithOptions:)</em> — Success</li><li><em>ViewController.viewDidAppear(_:)</em> — Success</li></ul><p>So that (half) explains it. To avoid holding some implicitly unwrapped optional properties on our <em>AppDelegate</em>, we perform some setup in the <em>init()</em> method and part of this involved reading the access token from the Keychain. This was why the read would fail and ultimately why some users would find themselves logged out.</p><p>I learned an important lesson here that I shouldn’t assume that protected data is available when the <em>AppDelegate</em> is initialised, but to be honest, I still wasn’t happy because I couldn’t understand <em>why</em> it wasn’t available. After all, we hadn’t changed this particular bit of code for years and it had been working fine in iOS 12, 13 and 14, so what gives?</p><h3>Finding the root cause</h3><p>My debugging interface was useful, but it was missing a vital bit of information that would help to answer everything: The time.</p><p>I knew that ‘protected data’ wasn’t available up until <em>AppDelegate.application(_:didFinishLaunchingWithOptions:)</em>, but it still didn’t really make sense because to reproduce the issue, I was doing the following:</p><ol><li>Launch the app</li><li>Use it briefly</li><li>Force quit the app</li><li>Lock my device and leave it for ~30 minutes</li><li>Unlock the device</li><li>Launch the app again</li></ol><p>I was 100% sure that the device was unlocked whenever I launched the app again in step 6 and therefore I was adamant that I <em>should </em>be able to read from the Keychain in <em>AppDelegate.init()</em>.</p><p>It wasn’t until I looked at the timing of all of these steps that things started making a bit more sense:</p><figure><img alt="A screenshot of a debugging interface in the app showing log messages that indicate failure or success when attempting to read keychain data at different points in time. This time, the logs include timestamps." src="https://cdn-images-1.medium.com/max/404/1*E6evjJooV_xdkzUirJWHWA@2x.png" /></figure><p>Looking carefully again at the timestamps:</p><ul><li><em>main.swift </em>— 11:38:47</li><li><em>AppDelegate.init()</em> — 11:38:47</li><li><em>AppDelegate.application(_:didFinishLaunchingWithOptions:)</em> — 12:03:04</li><li><em>ViewController.viewDidAppear(_:)</em> — 12:03:04</li></ul><p>The app process itself is being launched 25 minutes before I was actually unlocking the phone and tapping on the app icon!</p><p>Now I would have never have actually thought that there was this big of a delay, it was actually <a href="https://twitter.com/_saagarjha">@_saagarjha</a> that suggested I check the timestamps and after, he pointed me to this tweet:</p><h3>Saagar Jha on Twitter: &quot;Interesting iOS 15 optimization: Duet now tries to preemptively &quot;prewarm&quot; third-party apps by running them through dyld and pre-main static initializers several minutes before you even tap on an app icon. The app is then suspended and the subsequent &quot;launch&quot; appears to be faster. / Twitter&quot;</h3><p>Interesting iOS 15 optimization: Duet now tries to preemptively &quot;prewarm&quot; third-party apps by running them through dyld and pre-main static initializers several minutes before you even tap on an app icon. The app is then suspended and the subsequent &quot;launch&quot; appears to be faster.</p><p>Now everything makes sense. We never detected it initially because we likely didn’t give the iOS 15 betas enough time to ‘learn’ our patterns so the issue was only reproduced in real-world scenarios where the device thought I was going to launch the app soon. I still have no idea how this prediction is formed but I’m just going to put it down to “Siri Intelligence” and call it a day.</p><h3>Conclusion</h3><p>Starting with iOS 15, the system might decide to “pre-warm” your app before the user actually tries to open it and this can increase the chances of protected data not being available when you think it should be.</p><p>Protect yourself from this by waiting for the <em>application(_:didFinishLaunchingWithOptions:) </em>delegate callback, and if possible, keep an eye on <a href="https://developer.apple.com/documentation/uikit/uiapplication/1622925-isprotecteddataavailable"><em>UIApplication.isProtectedDataAvailable</em></a><em> (</em>or the delegate callbacks/notifications) and handle the situation accordingly.</p><p>We still get a <strong>very small</strong> number of non-fatals that report <em>isProtectedDataAvailable </em>as <strong>false </strong>inside of <em>application(_:didFinishLaunchingWithOptions:) </em>but getting the app to a point where we can defer reading the access token from the Keychain beyond this point would be a massive task and right now it’s not worth the time to investigate further.</p><p>This was a pretty tough bug to debug, and the fact that the change in behaviour seems to be entirely undocumented really did not help me. If you were also caught out by this, please consider duplicating <a href="https://openradar.appspot.com/FB9780579">FB9780579</a>.</p><p>I learned a lot from this and I hope you do too! If you find yourself interested in working on issues like this and more, we’re hiring iOS Engineers for both our <a href="https://apply.workable.com/cookpad/j/B4DC959424/?utm_source=BlogPost&amp;utm_medium=conclusion">product</a> and <a href="https://apply.workable.com/cookpad/j/D68FA3EC4B/?utm_source=BlogPost&amp;utm_medium=conclusion">platform</a> teams at Cookpad so why don’t you apply?</p><p><strong>Update: </strong>Since publishing this article, numerous people have in fact pointed me to relatively <a href="https://developer.apple.com/documentation/uikit/app_and_environment/responding_to_the_launch_of_your_app/about_the_app_launch_sequence#3894431">well written documentation from Apple</a> regarding the pre-warming behaviour. Other people however have also told me that they have still observed different behaviour to what is documented in some scenarios so do proceed with caution.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8b818c089466" width="1" height="1" alt=""><hr><p><a href="https://sourcediving.com/solving-mysterious-logout-issues-on-ios-15-8b818c089466">Solving Mysterious Logout Issues on iOS 15</a> was originally published in <a href="https://sourcediving.com">Source Diving</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Fight to Deliver Apps to the Globe Faster]]></title>
            <link>https://sourcediving.com/a-fight-to-deliver-apps-to-the-globe-faster-97e5760956ce?source=rss----217db81c7a01--ios</link>
            <guid isPermaLink="false">https://medium.com/p/97e5760956ce</guid>
            <category><![CDATA[continuous-delivery]]></category>
            <category><![CDATA[ruby]]></category>
            <category><![CDATA[app-store-localization]]></category>
            <category><![CDATA[fastlane]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[ainame]]></dc:creator>
            <pubDate>Wed, 18 Nov 2020 14:32:29 GMT</pubDate>
            <atom:updated>2020-11-18T14:32:29.470Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*DvbvfreGqPTo9q4Na368Zg.jpeg" /><figcaption><a href="https://pixabay.com/photos/courier-night-panning-warsaw-1214227/">https://pixabay.com/photos/courier-night-panning-warsaw-1214227/</a></figcaption></figure><p>At Cookpad, we build a global community to make everyday cooking fun. We deliver an iOS app to the worldwide market that supports 26 languages and about 74 regions as of today. It is always challenging to develop and ship apps on that scale. Release management is vital to be able to deliver new features or bug fixes to our customers quickly and continuously. In order to do this, we automate <a href="https://sourcediving.com/continuous-ios-app-delivery-1a158f1f3d33">our release flow with using </a><a href="https://sourcediving.com/continuous-ios-app-delivery-1a158f1f3d33">fastlane, </a><a href="https://sourcediving.com/continuous-ios-app-delivery-1a158f1f3d33">Jenkins, and </a><a href="https://sourcediving.com/continuous-ios-app-delivery-1a158f1f3d33">Slack</a>. Thanks to this automation, we have been able to do weekly release cycles for a while.</p><p>However, we have recently faced an issue where submission by fastlane&#39;s deliver was getting unacceptably slow during a couple of releases, which made it take about 4 hours and sometimes ended up failing altogether. After spending some days to investigate it, I was able to improve the performance of deliver to complete submission within a reasonable time. <a href="https://github.com/fastlane/fastlane/pull/16972">fastlane/fastlane#16972</a></p><p>Let’s have a look at what happened and how I overcame the issue.</p><h4>Big Update Comes with a Pain</h4><p>Just a week before WWDC 2020, on 16th June, Apple renewed its App Store Connect portal, and it appeared to be causing errors in fastlane&#39;s deliver. This meant that we couldn&#39;t use fastlane to submit apps anymore for the time being. (Technically we could upload the binary .ipa file but couldn&#39;t update metadata and screenshots.) <a href="https://github.com/fastlane/fastlane/issues/16621">fastlane/fastlane#16621</a></p><p>This issue impacted us a lot and we had to figure out what we can or can’t do with the existing fastlane lanes in our Fastfile and then follow necessary steps to manually release in the first week. At that moment, <a href="https://twitter.com/joshdholtz">@joshdholtz</a> the lead maintainer of fastlane was the hero that eagerly migrated old APIs to the latest APIs. As we waited for the migration to be done we tested RC versions locally for a few weeks.</p><p>On 2nd July, fastlane v2.150.0 was finally released followed by some patch version bumps. Although we saw errors while uploading screenshots, that turned out to be our fault as some errors were not caught by old APIs; e.g. filename extensions didn&#39;t match the expected file format. All seemed to work when we first tried the new version.</p><h4>Weekends didn’t come free</h4><p>v2.150.0 resolved most of our submission issues, however some additional work was still required to support the new APIs for asset uploading. This was eventually resolved in <a href="https://github.com/fastlane/fastlane/pull/16842">fastlane/fastlane#16842</a>.</p><p>In our weekly release cycle, we submit the app every Friday afternoon, and as the release manger I was trying to submit the app with CI and waiting for CI jobs to complete for hours on Friday afternoon. When it failed, I tried it again. Each trial took up around 3–4 hours and my working hours were over, but I kept repeating that from my iPhone. This continued until midnight on Saturday. I wasn’t that exhausted doing it but felt like my work extended forever.</p><p>I decided to fight against this to get back my Fridays and weekends.</p><h4>Test it, Measure it</h4><p>For our case it was apparent that a specific part of the app submission was incredibly slow and problematic. We always run fastlane&#39;s command with --verbose option on Jenkins so that we could check those logs quickly.</p><p>We saw two issues:</p><ol><li>Uploading screenshots was too slow</li><li>The results when making bad entries were shown as errors in the screenshots section in the App Store Console</li></ol><p>Although they were different issues, both were around screenshots uploading. To test and measure screenshot uploads, firstly, I set up a dummy app and a lane to run deliver like this. In general, you need to be able to measure how fast or slow it is if you want to improve performance, otherwise you can&#39;t even tell if the performance improved or not by your change.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4f36a071e8e072ad068314c7fc142132/href">https://medium.com/media/4f36a071e8e072ad068314c7fc142132/href</a></iframe><p>That didn’t finish within a reasonable time initially as we had about 380 images for production.</p><blockquote>[03:36:31]: fastlane.tools just saved you 132 minutes! 🎉</blockquote><p>So I left one region’s screenshots and removed the rest locally for testing. That made it easy to do trial and error until it finally finished in 10 minutes.</p><h4>Ruby working with IO</h4><p>I had a hunch that parallelisation would help to improve the performance, and I had already made similar performance improvements for other parts of our workflow; i.e. importing and exporting translations from and to an external service. The more languages or regions we support, the more data we need to handle, and if it comes over the Internet, IO — HTTP requests would be likely to be the bottlenecks.</p><p>The Ruby language that fastlane uses can make multiple Thread objects easily. In Ruby, you can&#39;t let multiple threads run at a time due to GIL (Global Interpreter Lock), which secures Ruby&#39;s thread safety, you can still multiplex IO with them.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c19b9295c0b8ca3c3d82e7ade191b2be/href">https://medium.com/media/c19b9295c0b8ca3c3d82e7ade191b2be/href</a></iframe><p>Let’s say downloading an image takes up to 10 seconds and we need to download 10 images sequentially. It is done by 10 secs * 10 images = 100 seconds. If Ruby multiplexes the network request to download images, that is done in nearly 10 secs with Ruby’s Thread in theory. It&#39;s ten times faster than without using threads.</p><p>This, of course, applies to uploads as well. So I used Thread to parallelise deliver. This change seemed to make it run about two times faster than before with the mini data set.</p><h4>Find Pattern, Solve Problem</h4><p>After reading through fastlane deliver and assessing logs, I noticed deletions of screenshots (driven by overwrite_screenshots flag) was already running on multi-threads but it was not as fast as I imagined. So I added a micro-benchmark additionally around the uploading and deleting code to investigate its elapsed time.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8b69da32525184ca51218795ece59e31/href">https://medium.com/media/8b69da32525184ca51218795ece59e31/href</a></iframe><p>This change appeared to be quite helpful for me to identify how the real problem was occurring. Most of the deletion requests completed quickly, but some of them could take longer than others. This became much more obvious when observing upload requests.</p><pre>Uploading ‘./fastlane/screenshots/ar-SA/5.5_1.jpg’...<br>Uploaded &#39;./fastlane/screenshots/ar-SA/5.5_1.jpg&#39;... (2.637683 secs)<br>...<br>Uploading ‘./fastlane/screenshots/ar-SA/iPad Pro (12.9-inch) (3rd generation)_3.jpg’...<br>Uploaded &#39;./fastlane/screenshots/ar-SA/iPad Pro (12.9-inch) (3rd generation)_3.jpg’... (125.733078 secs)</pre><p>This slows down the most straightforward approach to use multi-threads. For example, deliver&#39;s upload operation was like this semi-pseudo code.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5d8e3d163a06b21e586fa996cca1cdcd/href">https://medium.com/media/5d8e3d163a06b21e586fa996cca1cdcd/href</a></iframe><p>Look at this picture below, and imagine you have three threads and three images to upload on each region; ar-SA and en-US. The above pseudo-code reluctantly has to wait for the longest response time to move on to the next locale. Let’s sort it out.</p><figure><img alt="The simplest approach to upload images in parallel is to parallelise uploading for each region" src="https://cdn-images-1.medium.com/max/596/1*Cp978oENGvWhDaW-5d1Obg.png" /><figcaption>The simplest approach to upload images in parallel is to parallelise uploading for each region</figcaption></figure><p>In a nutshell, this can be solved with the Queue-Worker pattern, which is commonly known and used in a variety of places where scalability matters but often its name varies; Thread Pool, Job Queue, or Work Queue. iOS developers will recognise it as DispatchQueue or OperationQueue.</p><p>We can expect more speed in uploading generally as it won’t be blocked by slow responses in each iteration. In the following picture the graph shows when the Queue-Worker pattern is used with three threads. Even if each response time is the same, the latter can use a free thread efficiently.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/619/1*rrgAdxaQWdRtwjbygCNZ1A.png" /><figcaption>How much “Queue-Worker” pattern can minimise the overall execution time in the case</figcaption></figure><p>From this point of view, Ruby is powerful. It has a class Thread::Queue (or just Queue) cooperating with Thread.</p><blockquote>The Queue class implements multi-producer, multi-consumer queues. It is especially useful in threaded programming when information must be exchanged safely between multiple threads. The Queue class implements all the required locking semantics. The class implements FIFO type of queue. In a FIFO queue, the first tasks added are the first retrieved.</blockquote><p><a href="https://www.rubydoc.info/stdlib/core/Queue">Class: Queue</a></p><p>So thanks to the power of Queue, I was able to implement &quot;Queue-Worker&quot; pattern with a piece of code in Ruby.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c10a73603e769ad08ece76f1559094d0/href">https://medium.com/media/c10a73603e769ad08ece76f1559094d0/href</a></iframe><p>This QueueWorker can be used just like this.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4bb17b334c2ca0185f5faee36ddcada0/href">https://medium.com/media/4bb17b334c2ca0185f5faee36ddcada0/href</a></iframe><p>So I applied this in deliver, and as a result it completed about six times faster locally once the App Store Connect API was responding better. (Remember it used to take over 2 hours initially.)</p><ul><li>With fastlane 2.154.0 19m25.106s</li><li>With my working branch 3m20.148s</li></ul><p>Test environment</p><p>We had 385 images to be uploaded (but four skipped due to exceeding the limit of 10 in each screenshot set 🙈)</p><pre>% du -sh fastlane/screenshots<br>90M  fastlane/screenshots<br>% ls fastlane/screenshots/**/*.{png,jpg} | wc -l<br> 385</pre><p>I used these option to run deliver to upload screenshots.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c0a975d9938f452821210d2a97c3040f/href">https://medium.com/media/c0a975d9938f452821210d2a97c3040f/href</a></iframe><h4>Not exactly saving your time if it’s slow</h4><blockquote>[03:36:31]: fastlane.tools just saved you 132 minutes! 🎉</blockquote><p>This message that fastlane outputs at the end of the command always reminds me that I would be exhausted every week if I were to submit apps manually. I love how fastlane saves our precious time in day-to-day work. However, if there is some bottleneck in it, it may involve your time in the end when you need to check the result of your fastlane action. In other words, automated workflows should run fast enough that you’re still able to retry within your working hours. Failures are a reality, nothing is perfect!</p><p>Fight to save your time and enjoy your weekend.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=97e5760956ce" width="1" height="1" alt=""><hr><p><a href="https://sourcediving.com/a-fight-to-deliver-apps-to-the-globe-faster-97e5760956ce">A Fight to Deliver Apps to the Globe Faster</a> was originally published in <a href="https://sourcediving.com">Source Diving</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Continuous iOS App Delivery]]></title>
            <link>https://sourcediving.com/continuous-ios-app-delivery-1a158f1f3d33?source=rss----217db81c7a01--ios</link>
            <guid isPermaLink="false">https://medium.com/p/1a158f1f3d33</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[jenkins]]></category>
            <category><![CDATA[fastlane]]></category>
            <category><![CDATA[chatops]]></category>
            <category><![CDATA[continuous-delivery]]></category>
            <dc:creator><![CDATA[Yusei Nishiyama]]></dc:creator>
            <pubDate>Fri, 18 Jan 2019 11:44:04 GMT</pubDate>
            <atom:updated>2019-01-18T11:44:03.894Z</atom:updated>
            <content:encoded><![CDATA[<h4>Tips for delivering your app more frequently with fewer problems</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hdZ7LcAsslorJWpkiyqwqw.jpeg" /></figure><p>At Cookpad we now have more than 10 iOS developers working on our core application and we’re looking to have more people join us.</p><p>With this number of engineers, it is, of course, challenging to keep the code well-organised to have a solid, yet flexible architecture and enforce the preferred architecture throughout the project. However, having good quality code itself doesn’t achieve anything.</p><p>Yes, we need to deliver it to users to make a change to the world!</p><p>Though it’s a common topic to discuss, releasing an app is not always easy. For each release, you need to ensure that the following are present and correct:</p><ul><li>App functionality</li><li>Translations</li><li>App Store metadata</li></ul><p>These things usually get harder and harder to manage as the project continues because the specification gets increasingly complicated. For example, it’s likely that the business will expand to different countries, once it’s been confirmed that the product works in one country which means you need to manage more languages.</p><p>Moreover, the target of the app release is not always your customers. It also can be:</p><ul><li>A developer, so that they can debug a feature on a real device</li><li>A product manager, so that they can try out a work-in-progress feature to confirm that what’s being built is actually what is wanted</li><li>QA team to test a release candidate</li><li>Community managers, who want to make sure that the translations are correct and check what’s new so they can write a user-friendly description for the App Store</li><li>Beta testers, who are a set of voluntary users, to help us get more insights on experimental features</li></ul><p>You often end up having many different kinds of apps with different audiences, which can make release management even harder.</p><p>In this article, I’m going to lay out how we tackle the complexity of release management.</p><h3>Have a foreseeable schedule</h3><p>First thing first, let’s talk about release schedule. Having a regular release cycle is the most important factor in enabling app releases, constantly and <strong>at ease</strong>.</p><p>It’s a development team who should control the release schedule. Do not let others do it. I’m not suggesting that you should make it non-negotiable, instead, you should listen carefully to what your product team needs but it’s only developers who actually can make it happen in a realistic way.</p><p>Here at Cookpad, we release a new version every week on the same day regardless of the progress of the product development. If we fail to finish something up by release date. That’s that. We’ll wait for the next version for it to be released. It’s not the end of the world since we release weekly.</p><p>The key point here is to detach app release cycle from product development cycle. Even if a version doesn’t have much to show to users, there may also be internal improvements such as refactoring and updating 3rd party libraries. It’s always good to make any kind of change incrementally to minimize the risk.</p><p>Having an agreed schedule between developers and product team ahead of time also significantly reduces unnecessary communication overhead. Have you ever been asked to rush to finish your work and release unsatisfactory code, and spent a huge amount of time just to explain why it’s impossible? This is less likely to happen if you have a fixed release date.</p><p>A flexible schedule might work at the beginning but it’s soon going to be chaotic as the team grows. It’s very hard to build good automation and a QA process around a haphazard schedule. If you stick to a fixed schedule, in the end, you can release more often with fewer problems, trust me.</p><h3>Let remote machine do the job</h3><p>Just having automation is not enough. This should be run on a remote machine, not on your local machine. Here’s why:</p><ul><li>You don’t need to worry about how others’ machines are set up. It’s hard to write a script that works on any kind of environment. Some may have a different version of Xcode toolchain. Some may not have defined required environment variables. You should spend time on provisioning remote build machines rather on making a lot of effort to make it work everywhere.</li><li>You can free up your local machine resource. Building a release version of app and uploading it to TestFlight usually takes very long and meanwhile, you really can’t code since your Xcode is busy and you can’t switch to a different branch (Well, you technically can but you know how troublesome it will be if you do it!). What you could do while waiting for the build is, at best, to review others code on GitHub but you’ll likely to find yourself on Twitter 😇</li></ul><p>I can’t tell you which service you should use to have a remote build server but whatever service you choose, <a href="https://codesigning.guide/">Fastlane match</a> makes server provisioning lot easier. At a glance, it looks quite complicated to understand but what it does, simply put, is just managing certificates and provisioning profiles encrypted in a separate private GitHub repository and Fastlane downloads them as needed so that you don’t need to install them in your remote servers manually.</p><h3>Have a flexible and easy-to-use interface</h3><p>Here I’m going into a bit more advanced topic which I rarely see discussed.</p><p>As I mentioned earlier, what to build and who will be the audience can vary. You may now have just two apps, one for users and another for internal testers, but you should not expect this to remain constant… as I did 🙈</p><p>We now have 3 different internal apps and 3 external apps <strong>for the same service</strong> but targeting different audiences, which I would never dream of. I’m not going to explain what those apps are exactly for as it’s too specific to our project but here are a couple different scenarios that help you understand why we need these:</p><ul><li>Build master branch and ask QA team to test it</li><li>Build a release candidate branch and upload it to App Store Connect</li><li>Build an arbitrary branch and upload it to TestFlight to make it accessible to beta testers</li></ul><p>These are different scenarios but one of my colleagues discovered what is common. The variables are which branch to build and where to upload it. We implemented a Slack bot that takes those conditions as arguments, the command format is:</p><pre>ios deploy {branch} to {destination}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q0K33r10YQ7LItYcyHwN1Q.png" /><figcaption>Me asking the bot to build master branch and publish it as an internal app</figcaption></figure><p>The chatbot passes the arguments to Jenkins and Jenkins determines which branch to check out and which Fastlane command to run. Roughly, the entire system looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cEJ6-s6dC4UcdSI6zo1NFQ.png" /></figure><p>What’s good about this is that it’s extensible. We may need another app in the feature with different reasons but even if it will be the case, we don’t need to change the format of the command. We instead just need to extend the command to support the new destination.</p><p>Another upside of doing this in Slack is that we can make progress of these admin tasks visible to everyone and then the team can be aware of exactly what’s happening.</p><h3>Get everything together</h3><p>I’m intending to make this post as general and tool-agnostic as possible but I, at the same time, feel it can be too abstract to imagine how you can do it by yourself. To avoid the article being too general to be useful, I’m going to show how our typical release looks like.</p><p>Firstly, we branch off a release candidate from master, against which we run integration tests. We call this code freeze. It always happens on Wednesday and every week. On Wednesday evening, one of us runs the following command:</p><pre>ios code_freeze {version}</pre><p>This is going to:</p><ul><li>Create a release candidate branch off from master</li><li>Update the version number with the specified value</li><li>Post a list of changes to Slack</li><li>Ask translators to translate new phrases</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T_7emjYF8pY8QjQefhYxLw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wYwPXz650Hi23sK_jH3Zhw.png" /></figure><p>Who’s going to do this doesn’t really matter since it’s just about posting a single message to Slack.</p><p>We then run the following command right after the code freeze:</p><pre>ios deploy RC-x.x.x to rc</pre><p>This command builds the release candidate branch, distributes it as a new version of an internal app and ask the QA team to test it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zt2brE6uupsTz82jqcveRA.png" /></figure><p>On Friday evening, if we are confident about the quality of the version, we make a release build by running:</p><pre>ios deploy RC-x.x.x to release</pre><p>This command builds the release candidate with the release configurations and uploads it to App Store Connect. Note that the only difference from the previous command is just the destination.</p><p>Then, once the binary has been processed, we run:</p><pre>ios submit release {build_number}</pre><p>This command submits the app whose build number is the one specified and posts a slack message.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7omMKgN5FWxyY4xfEOM4zQ.png" /></figure><p>Our app usually is approved over the weekend and if this is the case, we release the version with:</p><pre>ios release release</pre><p>This may be a little confusing as the first release means we are going to “release” the app while the second release specifies the app kind. This command releases the app, whose status, at this point, should be “Pending Developer Release”, posts a Slack message and create the next version on App Store Connect.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*u5VCrrgl1MImwMKjcwTfAw.png" /></figure><p>That’s pretty much it!</p><p>I hope you can get some idea of how to improve your release process from this or if you do it better please share how you do it with us as a response to this article.</p><p>This is not the end of the journey. Things will never be perfect. We are constantly looking for people who help us improve our development process and products. If interested, please consider joining us checking out <a href="https://www.cookpadteam.com/">our corporate site</a> and <a href="https://cookpad.workable.com/j/1C52A99E1C">iOS job description</a>.</p><p>Finally, please forgive me for advertising myself a bit using this space. I’m giving a talk at <a href="http://romobos.com/">MobOS conference in Cluj-Napoca, Romania</a> this February and I’m going to talk about a similar topic but in more detail, and hopefully, with more advanced techniques. Hope I can meet some of you there.</p><p>Thanks for reading. I wish you a peaceful app delivery.</p><p><em>Acknowledgement: Thank you </em><a href="https://medium.com/u/769ba1b0faf1"><em>Lewis Buckley</em></a><em> for your detailed review on this post and </em><a href="https://medium.com/u/db68a81ff31"><em>Liam Nichols</em></a><em> for always inspiring me with your smashing ideas on this topic.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1a158f1f3d33" width="1" height="1" alt=""><hr><p><a href="https://sourcediving.com/continuous-ios-app-delivery-1a158f1f3d33">Continuous iOS App Delivery</a> was originally published in <a href="https://sourcediving.com">Source Diving</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Agency to Product — An iOS developers reflection]]></title>
            <link>https://sourcediving.com/from-agency-to-product-an-ios-developers-reflection-65e303f0f384?source=rss----217db81c7a01--ios</link>
            <guid isPermaLink="false">https://medium.com/p/65e303f0f384</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[cookpad]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Thomas Guy]]></dc:creator>
            <pubDate>Wed, 20 Jun 2018 15:47:42 GMT</pubDate>
            <atom:updated>2018-06-20T15:47:41.639Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/842/1*NbzvfNuvOcd51xCshhh8Ow.png" /></figure><p>Now that I have been working at Cookpad for over a year as an iOS developer, I wanted to reflect on the differences in working product side having come from an agency environment.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/477/1*DuMibj0_iN2n4B3fe68F4Q.png" /></figure><h3>Agency Life</h3><p>I started working in a mobile agency nine years ago, which might not seem very long but back then the mobile development world was very different from what it is now. The company where I landed my first programming job primarily made web sites and were just starting to test the water in mobile development and decided it would be a good idea to get someone young (read cheap) to learn on the job and make a couple of apps — Me! The apps I built back then were as simple as a branded torch app to “Shine the light on bad cooking” :palm face: and an inspiring quote of the day generator. From there I moved into a seven year journey of building apps for a much more serious mobile agency.</p><p>Looking back I believe an agency was a great place for me to gain a lot of experience, have a lot of fun and “earn my stripes”. Agencies in my experience are often happy to have a junior developer dive straight into building a production ready application for a client with little to no code review process in place. In this environment you tend to learn very quickly how not to do things when you are the sole developer on a three month project with a hard deadline. I make this sound stressful (which it was at times) but for me it was fun, exciting and i picked up a whole bunch of skills.</p><p>Working on a great variety of applications from weather apps to financial apps and everything in between it was rarely boring and if you did start to get fed up with a project something new was always around the corner with apps taking between 3–6 months to build. There was also lots of opportunity to get involved in all aspects of the process — Initial client meetings, feasibility studies, nailing down a functional specification, collaborating with the design team and a skill I have found most useful (after many late nights), the ability to push back on unrealistic deadlines.</p><p>The agency environment had turned me from a meek “yes sir, no sir, I will definitely work late to add shake to dismiss functionality” developer into an experienced “Are you sure you want it to work like this, have you thought about that, this could be a good possibility instead” developer and I was having a very enjoyable time.</p><h3>Lots of variety, always learning and having fun — So why think about moving to product?</h3><p>Apps came and went, along with them fresh challenges, new API’s and technologies to learn. The new challenges and learning opportunities were welcome, it was letting go of the apps after they were built that started to become a problem for me.</p><p>I wanted answer to these questions: Did the users find the application intuitive? Did they notice the power user feature that took two weeks to build? How was retention? Could we have done more? What was the key feature, the key user journey that would have nailed the experience? Where did we fail the user? All of these questions and more were often left hanging after a projects retrospective meeting. Projects I had lived and breathed for more than 3 months felt unfinished. These questions were soon pushed aside each time I stepped into the project kick off for the next application.</p><p>It was these unanswered questions that ultimately led me to take the leap and work for a product company.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/519/1*HCSnvOMRVNDV00xKLcAjig.png" /></figure><h3>Product life</h3><p>Reflecting on my time working for a product I can now answer a few of the questions and worries I scribbled down when I was considering making the move from an agency:</p><h4>Will I get bored working on the same app? Will I miss the variety?</h4><p>Apps don’t come and apps don’t go — It is still the same app but this does not stop new challenges and learning opportunities presenting themselves daily. Perhaps it is the unique environment of Cookpad Global feeling like a start-up AND being an established company at the same time or perhaps it is like this with all product companies. Thanks to the product designers, UX designers, our users and our passionate team at Cookpad there is a constant stream of problems to solve providing variety with little chance of getting bored.</p><h4>Will it be slow? Will I have to fight through 4 levels of management to get a button placement approved?</h4><p>My experience so far has been anything but slow. Taking part in Google sprint weeks (where you come up with a problem, a solution, build a prototype and test it in one week) is anything but slow. Launching new features to meet unmovable deadlines (such as Ramadan) is not slow. Having a very ambitious product team wanting to test a million ideas is actually quite slow…. joke! This one is definitely not slow!</p><p>Where it does slow down perhaps is in getting your code to production. However this is for good reason as your code is reviewed by the other developers on your team to achieve maintainability and your features are reviewed by product to ensure quality — both of which are very important when you are all working on the same codebase.</p><h4>Will I still have the opportunity to grow as a developer?</h4><p>At Cookpad we currently have a team of 9 iOS developers working on the same application. It is inevitable that there will most likely be something to learn with each pull request submitted. In my opinion, with all of the technologies now available to an iOS developer the days of knowing all of the APIs and frameworks available are long gone. So having an 8 strong team behind you to let you know where you could have done something better, more efficient or with 1 less line of code is at times frustrating but ultimately an invaluable learning resource.</p><h4>Do the questions that led me from agency to product get answered?</h4><p>Going back to one of the main reasons i choose to move to product — Do those nagging user experience questions get answered? The answer is undoubtedly: yes, yes and yes.</p><p>I work in an environment where we have a constant stream of real users’ feedback from our offices around the world on a daily basis. We have usability studies, we have an experimental version of our application to trial new features, we have a team of data analysts and I am surrounded by passionate staff that want Cookpad and its mission to succeed. Those questions most definitely get answered, every day.</p><h3>Wrap up</h3><p>So if you have worked for an agency and are looking for something more fulfilling, more rewarding with the same mix of challenges and opportunities to learn come and work for a product company. Of course with product there is also the <a href="https://medium.com/cookpadteam/the-perks-of-working-at-cookpad-bristol-e9c2513fd328">pension and ever growing list of perks too</a> :P</p><p>If you can — work for a product that you believe in. Don’t work for a taxi app if you don’t believe in helping people get from A to B outside of public transport and don’t work for a social media app if you don’t agree with their ethics. If like me you believe in bringing family and friends together for meaningful face to face human connections and ultimately making everyday cooking fun — work for a product like Cookpad.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=65e303f0f384" width="1" height="1" alt=""><hr><p><a href="https://sourcediving.com/from-agency-to-product-an-ios-developers-reflection-65e303f0f384">From Agency to Product — An iOS developers reflection</a> was originally published in <a href="https://sourcediving.com">Source Diving</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rapid iOS UI Design and Prototyping With Swift Playgrounds]]></title>
            <link>https://sourcediving.com/rapid-ios-ui-design-and-prototyping-with-swift-playgrounds-d684dc2d27df?source=rss----217db81c7a01--ios</link>
            <guid isPermaLink="false">https://medium.com/p/d684dc2d27df</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[uikit]]></category>
            <category><![CDATA[swift-playgrounds]]></category>
            <category><![CDATA[autolayout]]></category>
            <dc:creator><![CDATA[David Fox]]></dc:creator>
            <pubDate>Mon, 04 Jun 2018 10:58:26 GMT</pubDate>
            <atom:updated>2018-06-05T07:38:49.917Z</atom:updated>
            <content:encoded><![CDATA[<h3>UI Design and Development At Cookpad</h3><p>At Cookpad, we work day-in-day-out with a rich library of UI components used across a large codebase. We also work on many prototype pieces of functionality and having a reusable, extensible library of these elements is essential in daily development. Recently, I’ve been looking into ways of improving and speeding up our UI workflow and improving the reusability of it’s components…</p><h3>Problems Faced During UI Development</h3><p>One issue with creating UI components in iOS applications is that it is often hard to visualise accurately what the current state of our view code is without building and running our project. Of course, storyboards go some way to alleviating this issue but populating our view elements with dynamic data (from view models, for example) is not really feasible in storyboards. This, coupled with the fact that there is a lot of pixel-pushing and small refinements during this process, we find ourselves building and running perhaps hundreds of times across the course of developing a new piece of functionality.</p><p>This results in a lot of time spent waiting and hoping that our autolayout constraints are set up correctly and our current styling values render as expected. Furthermore, if the view items we are creating are deeply buried within the application, we need to tap through to that area every single time.</p><p>In short, code which may have taken one minute to write could take just as long to preview! This really hinders progress and stops developers getting into a decent <em>flow</em>.</p><p>Take the following (slightly simplified) component from the Cookpad app…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/696/1*le9Dvtfk9VpFQ__BT-llPQ.png" /></figure><p>This is a standard table view cell for a Cookpad user. Iterating through the design for this cell can take many many iterations of tweaking and building, checking and debugging. This process takes away from the developer’s workflow.</p><p>So, I started looking into any available tooling to help improve the workflow for this kind of work. <em>UI design in Xcode Playgrounds</em> is where I ended up…</p><h3>What Are Playgrounds?</h3><p>Since the introduction of Playgrounds in Xcode 7, it is now possible to write and test code <em>outside</em> of our project in a simple and handy scratchpad-like IDE. This was very useful for me in learning Swift when it first arrived and playing around with its new syntax and features.</p><p>However, there are even more powerful features within Playgrounds that take it beyond just a coding scratchpad. We can use them to experiment not only with code, but also with <em>user interface elements</em>.</p><p>Imagine if we could rapidly iterate across the many steps required to create our user cell <em>without</em> having to build the project and navigate to our screen to verify every time. Imagine if we could see all it all updating in <em>real-time</em> in one place. This is what <em>LiveView</em> in Playgrounds allows us to do.</p><h3>Getting Started</h3><p>Let’s fire up Xcode and jump into our playground. If your project doesn’t already have one, go ahead and create one with File -&gt; New -&gt; Playground. In order to provide UI support, we’ll need to import the PlaygroundSupport framework. Additionally, for Playgrounds targeting iOS, we’ll also need to import UIKit.</p><p>Next, we need to reveal our live view within Xcode’s assistant editor. You should see assistant editor in the top-right of Xcode (it’s the one with two circles overlapping). Then select Live View from the explorer dropdown underneath like so:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1008/1*JqsQnYxylGIUHl9oqQosEg.png" /></figure><p>I’m going to create my cell in a UIView subclass for now to act as a basic stub for our cell. Paste the following into Xcode and save…</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1cee8708d16de8b92df3cba4258e20a6/href">https://medium.com/media/1cee8708d16de8b92df3cba4258e20a6/href</a></iframe><p>You should see a white rectangle for our view in the assistant editor. Nice! It doesn’t look like much but having a live-rendered view right there in Xcode is pretty powerful and a great starting point for us.</p><h3>Next Steps</h3><p>Now we’re all set up, let me take you through the steps to quickly create our user cell component. Here’s how we’ll work…</p><p>1. Get our base view heirarchy with no styling in place.<br>2. Start adding colours.<br>3. Tweak some positioning.</p><p>So, for step 1, replace the contents of your playground with <a href="https://gist.github.com/davefoxy/81670e55874f97b41def22f8dc8e3010">the contents of this gist</a>.</p><p>Now, you should see something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/698/1*zIoxVv1YDcVBY3l4SDTGpg.png" /></figure><p>Very ugly but probably the most important step of UI work in iOS is to define the view hierarchy and autolayout constraints <em>first</em>. Actual styling, font work etc is generally trivial work and should take a backseat to ensuring everything is where it should be. We’ll be using the layout anchor API introduced in iOS 9 as apposed to any 3rd party solutions from this point onwards but for now, step 1 represents our base <em>structure</em>, in much the same way we may define our <em>class</em> structure when designing an application’s <em>architecture</em>.</p><h3>Adding Some Style</h3><p>Now we’ve got our elements in place and our view rendering in Xcode, why not take a second to play around with some of our text values in the init method. Change one of the label’s text values, hit save and 🚀. Pretty cool, right?</p><p>Let’s push things a bit farther… I’m now going to apply some styling to our view’s elements. Watch the following flow to see how easy it now is for me to tweak, preview and iterate over my view’s contents and design…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*8zz7I5qPagPrD4FPzGiW7w.gif" /></figure><p>Without Playgrounds, we would have been bouncing in-between Xcode and the simulator, constantly tweaking values, re-building and running… For me, Playgrounds decreases development time for this kind of work by an order of magnitude and allows me to maintain a constant workflow without being disturbed by a slow and inefficient trial-and-error process.</p><p>Also, when driving our UI from view models and such, we can easily see what our views look like when there’s too much data, not enough or even drive our LiveView off mock data from our tests.</p><p>You can find the final code for this component <a href="https://gist.github.com/davefoxy/823158d168f5a2164caaf9bc7288581a">in this gist</a>.</p><h3>Reusability Of UI Components</h3><p>So we have a nice process going right now but with a fairly large codebase, we have quite a few helper classes and extensions for UI work here at Cookpad. Extensions on UIColor to define and make reuse of our core branding easier. Subclasses of UIButton to allow us to easily insert a particular style of button anywhere (the “Follow” button in our demo is a good example of this). We don’t want to have to type out all that styling code every time and maintain it if we decide to change our styling in either the main app or our playground.</p><p>The problem is that Playgrounds run in something of a sandbox and, even if they’re part of the same <em>workspace</em>, they don’t have access to that project’s assets and classes out of the box. So, how can we implement a reusable set of UI utilities that are shared and available across both our application and also, available for quick prototyping within UI Playgrounds? The answer, is via a framework.</p><h3>Creating A UI Components Framework</h3><p>The process required to create a shared framework can seem a little complex and there’s a number of different ways to go about this so let’s go through the most basic option, step-by-step together. The basic flow we’ll use is:</p><p>1. Move our reusable components out of Playgrounds and into their own classes.<br>2. Create a new framework just for our UI items and add the relevant classes.<br>3. Import our framework into our playground.</p><p>Let’s start off by moving our “Follow” button into it’s own class. Create a new file <em>in the main project</em>. In my case, I’m going to call it CookpadButtons.swift. Add <a href="https://gist.github.com/davefoxy/6486faaef8096c723004e0240ed2ff3b">the contents of this gist</a> and notice the usage of public. In order to be accessible to our playground, the default internal is insufficient.</p><p>Next, it’s time to create our framework.</p><p>1. Create a new Cocoa Touch Framework via the File -&gt; New -&gt; Target menu. I gave my framework the name CookpadUI.<br>2. Next, go to the project settings panel and click on our new framework under the side Targets panel.<br>3. Navigate to the Build Phases panel and, under the Compile Sources dropdown, click the + icon and select the new CookpadButtons.swift file. You project settings should now look something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*icV2upnu5cxow5A21bknjA.png" /></figure><p>Build your project and then, back in Playgrounds, simply import the framework with import CookpadUI and we can now instantiate and use our new FollowButton subclass from within both the main project and inside of our playground too 🚀.</p><p>The power here is that updating our new follow button class updates it <em>everywhere</em>. No need to keep Playgrounds’ UI element styling up-to-date with changes in our app and vice-versa 💪</p><p>We now have a seriously powerful UI workflow. Shared UI components which are reusable across our production app and also, easy to integrate into and prototype with in Playgrounds 🙌</p><blockquote>A quick note around updating code in your new framework: <br>You may find the need to re-build the framework target in order to see your changes reflected within Playgrounds.</blockquote><h3>Other Benefits — Collaborating With Designers</h3><p>This workflow also opens up other opportunities for improving cross-communication between developers and designers. It can be beneficial for designers to see their designs and layout as they will <em>really</em> appear and to freely tweak and rapidly re-iterate upon their work. Designing via Playgrounds offers a unique workflow whereby development and design can efficiently collaborate towards a consistent end result.</p><h3>In Summary</h3><p>The techniques shown here are just the beginning of improving the design-development-delivery cycle of the Cookpad application. The power really comes when our UI library grows and we get ease-of-reuse as this really opens up our toolset to allow for rapid prototyping.</p><p>Hopefully this post gives you some insight into how a UI-centric framework and shifting your UI workflow into Playgrounds can improve your team’s daily productivity. Any questions or insights, feel free to drop me a line in the comments 😉</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d684dc2d27df" width="1" height="1" alt=""><hr><p><a href="https://sourcediving.com/rapid-ios-ui-design-and-prototyping-with-swift-playgrounds-d684dc2d27df">Rapid iOS UI Design and Prototyping With Swift Playgrounds</a> was originally published in <a href="https://sourcediving.com">Source Diving</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>