<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://xavierlowmiller.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://xavierlowmiller.github.io/" rel="alternate" type="text/html" /><updated>2023-01-05T07:13:12+00:00</updated><id>https://xavierlowmiller.github.io/feed.xml</id><title type="html">Xavier Lowmiller’s Blog</title><subtitle>A site about iOS programming, and my life in general</subtitle><author><name>xavierLowmiller</name></author><entry><title type="html">Implementing the Slide to Unlock Animation in SwiftUI</title><link href="https://xavierlowmiller.github.io/blog/2020/10/14/Slide-to-unlock-in-SwiftUI" rel="alternate" type="text/html" title="Implementing the Slide to Unlock Animation in SwiftUI" /><published>2020-10-14T00:00:00+00:00</published><updated>2020-10-14T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2020/10/14/Slide-to-unlock-in-SwiftUI</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2020/10/14/Slide-to-unlock-in-SwiftUI">&lt;p&gt;Everybody remembers the original iPhone’s solution to avoid pocket calling people:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2020-10-11-ios-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This venerable UI pattern has disappeared a while ago from our lock screens, but many apps implement it, perhaps most prominently in Amazon’s 1-click buy.&lt;/p&gt;

&lt;p&gt;I recently implemented such an animation at work in UIKit. The &lt;a href=&quot;https://stackoverflow.com/questions/438046/iphone-slide-to-unlock-animation/64308214#64308214&quot;&gt;StackOverflow answers&lt;/a&gt; typically reach for CoreAnimation on layers, and I was wondering how this would go in SwiftUI. &lt;a href=&quot;https://swiftui-lab.com/category/animations/&quot;&gt;Animations&lt;/a&gt; are one of the things in SwiftUI that don’t come easy to me, so it’s the perfect exercise!&lt;/p&gt;

&lt;h2 id=&quot;animatablemodifier&quot;&gt;AnimatableModifier&lt;/h2&gt;

&lt;p&gt;I chose to implement the effect as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnimatableModifier&lt;/code&gt;. This protocol inherits from both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Animatable&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ViewModifier&lt;/code&gt;, so we implement both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animatableData&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func body(content: Content) -&amp;gt; some View&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My idea here is to take whatever view we are given inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ViewModifier&lt;/code&gt;’s body function, overlay it with a gradient, and constrain it to the contents using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mask()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;LinearGradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The animation then is done via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animatableData&lt;/code&gt;: It exposes data that should be animated over. Since our animation is an endless one, I chose a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CGFloat&lt;/code&gt; that grows from 0 to 1 over and over again. This float represents the horizontal percentage-based position of the gradient that is moved from left to right.&lt;/p&gt;

&lt;h2 id=&quot;getting-the-layout-right&quot;&gt;Getting the layout right&lt;/h2&gt;

&lt;p&gt;Let’s start by implementing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shimmer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnimatableModifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Gradient&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sideColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
        &lt;span class=&quot;nv&quot;&gt;middleColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gradient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sideColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;middleColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sideColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;overlay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;LinearGradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;nv&quot;&gt;gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;nv&quot;&gt;startPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;nv&quot;&gt;endPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trailing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s an overlay over the content, that is again masked by the content. This way, we don’t need a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GeometryReader&lt;/code&gt; because the overlay will exactly match the frame of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt;. We use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LinearGradient&lt;/code&gt; that currently spans the entire available space. This gradient is then masked by the content again, which stamps out the alpha channel of the content. This works nicely with text:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2020-10-11-full-width-gradient.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;moving-the-gradient&quot;&gt;Moving the gradient&lt;/h2&gt;

&lt;p&gt;Currently, the gradient goes from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.leading&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.trailing&lt;/code&gt;. Let’s fix that with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt; variable that we can later animate:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shimmer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnimatableModifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.25&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;overlay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;LinearGradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;nv&quot;&gt;gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;nv&quot;&gt;startPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                        &lt;span class=&quot;nv&quot;&gt;endPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This now displays the animation at 25% of its run:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2020-10-11-25-gradient.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Pretty nice! The gradient now covers 40% of the area (20% to the left side and 20% to the right side of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt;).
Unfortunately, there are some artefacts at 0% and a 100%, where half of the gradient is still shown:
&lt;img src=&quot;/assets/2020-10-11-0-gradient.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;/assets/2020-10-11-100-gradient.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can fix this by removing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;±0.2&lt;/code&gt; at the edges of the animation:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;At 0%, we want to hide the right side of the gradient&lt;/li&gt;
  &lt;li&gt;At 100%, we want to hide the right side of the gradient
This is a simple linear equation:
    &lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;startPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;endPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;animating-the-position&quot;&gt;Animating the position&lt;/h2&gt;

&lt;p&gt;To conform to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Animatable&lt;/code&gt; part of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnimatableModifier&lt;/code&gt;, we need to expose the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt; as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animatableData&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Shimmer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AnimatableModifier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animatableData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we animate from 0 to 1, SwiftUI will be call this with values ranging from 0 to 1 for every frame. It’s like a stream of numbers: During the animation it will assume the values 0.1, 0.2, 0.3, …&lt;/p&gt;

&lt;p&gt;The actual animation needs to be triggered in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.onAppear&lt;/code&gt; closure:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;overlay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onAppear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;withAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Animation&lt;/span&gt;
                            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;repeatForever&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;autoreverses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s dissect this:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.onAppear&lt;/code&gt;, we set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position&lt;/code&gt; to 1&lt;/li&gt;
  &lt;li&gt;This is wrapped in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.withAnimation&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;We have a 2-second linear animation that is delayed by 1 second&lt;/li&gt;
  &lt;li&gt;This 3-second animation is repeated forever&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s important to use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.withAnimation&lt;/code&gt; here because this only animates the changes that happen inside the closure. If we opted for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.animation&lt;/code&gt; modifier on the view, all other changes (like frame change on device rotation) will be animated as well.&lt;/p&gt;

&lt;p&gt;This is what the result looks like:
&lt;img src=&quot;/assets/2020-10-11-slide-to-unlock.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;It’s really nice to animate things in SwiftUI. The previews help a lot: while developing this, I had multiple previews showing the animation’s progress at various stages.&lt;/p&gt;

&lt;p&gt;But the main reason I liked it is that SwiftUI doesn’t mix metaphors: In UIKit, you could always reach down to the layer API when something (such as masking) isn’t exposed. Nothing stops you from doing that from a UIView, or a UIViewController, or another UIViewController. SwiftUI severely limits these interactions: It’s a fresh start with consistent APIs that are properly contained.&lt;/p&gt;

&lt;p&gt;You can find the final code in &lt;a href=&quot;https://gist.github.com/xavierLowmiller/76625243deed678171e9a25de66fffd4&quot;&gt;this gist&lt;/a&gt;.&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">Everybody remembers the original iPhone’s solution to avoid pocket calling people:</summary></entry><entry><title type="html">Porting @AppStorage to iOS 13</title><link href="https://xavierlowmiller.github.io/blog/2020/09/04/iOS-13-AppStorage" rel="alternate" type="text/html" title="Porting @AppStorage to iOS 13" /><published>2020-09-04T00:00:00+00:00</published><updated>2020-09-04T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2020/09/04/iOS-13-AppStorage</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2020/09/04/iOS-13-AppStorage">&lt;p&gt;A couple of weeks ago, a few friends and I were out having beers. We told the barman to make sure our glasses are filled, and we’d figure the tap out later.
After a while, somebody said “you know, I’d love an app for my watch to keep track of how many I’ve had”. My answer was that for watchOS 7, that app would probably fit in a tweet, including persistence and all (&lt;a href=&quot;https://gist.github.com/xavierLowmiller/378e63c22aee6a07a650fe01305e2f6e&quot;&gt;it didn’t&lt;/a&gt;, but it’s not so far off). When porting it back to watchOS 6, I noticed that the thing I missed most was the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AppStorage&lt;/code&gt; property wrapper.&lt;/p&gt;

&lt;p&gt;While there’s a lot of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt; wrappers out there, I couldn’t find one that had a 100% identical API to the “real” SwiftUI thing. So of course, &lt;a href=&quot;https://github.com/xavierLowmiller/AppStorage&quot;&gt;I had to write one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post collects some of the obstacles and surprises I faced implementing it. While it’s specifically about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AppStorage&lt;/code&gt;, any property wrapper that implements a simple side effect can probably be implemented in a similar manner.&lt;/p&gt;

&lt;h2 id=&quot;starting-out&quot;&gt;Starting out&lt;/h2&gt;

&lt;p&gt;Since I had a specific target in mind, I started by copying the exact signature:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@frozen&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@propertyWrapper&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DynamicProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;wrappedValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nonmutating&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;projectedValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The thing that immediately caught my eye was the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonmutating set&lt;/code&gt;. It’s a modifier that is used very rarely in day-to-day Swift, but makes total sense: In SwiftUI, views are value types, and mutating them doesn’t make much sense. They just get copied on mutation, and nothing happens in the SwiftUI update engine since the original is just the same.
First I’ve tried wrapping an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@State&lt;/code&gt; property, but that had some issues later. The trick here is to borrow from Swift’s Copy-on-Write types and make the backing storage a reference type:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DynamicProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;wrappedValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;nonmutating&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This way, the compiler is happy since mutations to the reference type don’t cause the struct itself to mutate, and SwiftUI will know to update views because of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ObservedObject&lt;/code&gt;. I image that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@State&lt;/code&gt; is implemented similarly.&lt;/p&gt;

&lt;h2 id=&quot;actually-saving-values&quot;&gt;Actually saving values&lt;/h2&gt;

&lt;p&gt;SwiftUI’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AppStorage&lt;/code&gt; takes two arguments: A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt; (a simple String), and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;store&lt;/code&gt; (an optional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt; instance).&lt;/p&gt;

&lt;p&gt;There are multiple initializers for all the types that are supported, such as:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;wrappedValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wrappedValue&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can forward this store to the property wrapper so we can persistently store values as a side effect when setting new values:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DynamicProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefaults&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;wrappedValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;nonmutating&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;forKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works quite nicely for simple types like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bool&lt;/code&gt;, that can be stored and retrieved from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt; directly. In addition to those types, SwiftUI supports &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RawRepresentable&lt;/code&gt;s, such as enums and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;URL&lt;/code&gt;s. Since I was going for a 100% compatible API, I wanted to do this as well.&lt;/p&gt;

&lt;h2 id=&quot;a-little-type-erasure&quot;&gt;A little type erasure&lt;/h2&gt;

&lt;p&gt;I was stuck at this point for a couple of days. There are two failures:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;In the initializer, a cast from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int&lt;/code&gt; to an enum with RawValue &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int&lt;/code&gt; fails: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;store.value(forKey: key) as? Value&lt;/code&gt; just never works.&lt;/li&gt;
  &lt;li&gt;There’s a crash when trying to set an enum in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After some research, I found that this is a case for type erasure: Instead of directly casting to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt; and writing that to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt;, the property wrapper gets two closures: One for saving to and one for reading from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt;. This way, there’s no need to know how to (de-)serialize arbitrary types: this information can be captured in the initializer, where we know which concrete type we’re dealing with:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;RawRepresentable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;RawValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;wrappedValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;rawValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;initialValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wrappedValue&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;saveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;forKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This way, the property wrapper itself is becoming really simple, since it doesn’t even have to know about stores and keys anymore: All it needs is a way to somehow save new values:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DynamicProperty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;saveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Void&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;wrappedValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;nonmutating&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;saveValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newValue&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It would be easy to extend this idea to more types, such as all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Codable&lt;/code&gt; types. Since the SwiftUI version doesn’t do this, I decided to leave it out (for now).&lt;/p&gt;

&lt;p&gt;At this point I thought I was finished: I wrote some tests that ensured I have an identical API, and thought of wrapping up. Then I thought of the possibility of somebody changing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt; without letting the property wrapper know (cruel, I know). SwiftUI’s property wrapper still works in this case, so I had to address it, too.&lt;/p&gt;

&lt;h2 id=&quot;key-value-observing-the-store&quot;&gt;Key-Value Observing the Store&lt;/h2&gt;

&lt;p&gt;Luckily, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt; is compatible with KVO. It’s easy enough to work with in this case, but requires some things:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The observer must subclass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSObject&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The observer must override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;observeValue(forKeyPath:of:change:context)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;We must be careful to add and remove the observer correctly.
Since we’re already dealing with a reference type as our private backing storage, it can be used to support KVO here:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefaults&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;keyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;@escaping&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addObserver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;forKeyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;deinit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;removeObserver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;forKeyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;observeValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forKeyPath&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;keyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt;
                             &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt;
                             &lt;span class=&quot;nv&quot;&gt;change&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;NSKeyValueChangeKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]?,&lt;/span&gt;
                             &lt;span class=&quot;nv&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UnsafeMutableRawPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;change&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultValue&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, we have to pass the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt; instance which we want to observe, the key we wish to observe on, and the type eraser closures to the backing storage, ruining much of the simplicity of before. I omitted the logistics here, but you can take a look in the &lt;a href=&quot;https://github.com/xavierLowmiller/AppStorage/blob/main/Sources/AppStorage/AppStorage.swift&quot;&gt;final implementation&lt;/a&gt;: Most of the information that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Storage&lt;/code&gt; requires is handed through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppStorage&lt;/code&gt;’s private initializer.&lt;/p&gt;

&lt;p&gt;The upside is that the property wrapper now instantly updates SwiftUI views that depend on it, even if somebody sneakily changes (or deletes) things in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserDefaults&lt;/code&gt; instance.&lt;/p&gt;

&lt;h2 id=&quot;naming&quot;&gt;Naming&lt;/h2&gt;

&lt;p&gt;Now, since the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AppStorage&lt;/code&gt; clashes with SwiftUI’s own property wrapper, I had to change this property wrapper’s name: It’s called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Persistence&lt;/code&gt;, and I’m not really happy about that.&lt;/p&gt;

&lt;p&gt;What I’d really like is some way to make the custom property wrapper go away in iOS 14, watchOS 7, and the likes:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@unavailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iOS&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;watchOS&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;macOS&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tvOS&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;@propertyWrapper&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…but this only works the other way around. You can make more types available for newer operating systems, but not remove them.&lt;/p&gt;

&lt;p&gt;Another thing that would be cool would be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typealias&lt;/code&gt; based on operating system availability:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;#available(iOS 14, watchOS 7, macOS 11, tvOS 14, *)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;typealias&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SwiftUI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;typealias&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;AppStorage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Persistence&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…but Swift doesn’t allow top-level statements like this.&lt;/p&gt;

&lt;p&gt;If you have any idea about this, please &lt;a href=&quot;https://github.com/xavierLowmiller/AppStorage/issues/4&quot;&gt;reach out to me&lt;/a&gt;!&lt;/p&gt;

&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;/h2&gt;

&lt;p&gt;I learned a couple of things while building my own version of SwiftUI’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AppStorage&lt;/code&gt; property wrapper:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A simple struct wrapper around a reference type is a pattern Swift really likes, even beyond Copy-on-Write types.&lt;/li&gt;
  &lt;li&gt;Initializer overloads in combination with type erasure enables powerful generic patterns.&lt;/li&gt;
  &lt;li&gt;KVO is very useful, especially when contained in a private type.&lt;/li&gt;
  &lt;li&gt;Swift’s support for type-level programming can be improved.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it for today! Please check out the &lt;a href=&quot;https://github.com/xavierLowmiller/AppStorage&quot;&gt;final implementation&lt;/a&gt;, especially when you can’t drop iOS 13 yet but want the goodness of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AppStorage&lt;/code&gt;.&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">A couple of weeks ago, a few friends and I were out having beers. We told the barman to make sure our glasses are filled, and we’d figure the tap out later. After a while, somebody said “you know, I’d love an app for my watch to keep track of how many I’ve had”. My answer was that for watchOS 7, that app would probably fit in a tweet, including persistence and all (it didn’t, but it’s not so far off). When porting it back to watchOS 6, I noticed that the thing I missed most was the @AppStorage property wrapper.</summary></entry><entry><title type="html">What Adding Dependencies Will Do To Your App in 2020</title><link href="https://xavierlowmiller.github.io/blog/2020/06/04/Swift-Dependencies" rel="alternate" type="text/html" title="What Adding Dependencies Will Do To Your App in 2020" /><published>2020-06-04T00:00:00+00:00</published><updated>2020-06-04T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2020/06/04/Swift-Dependencies</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2020/06/04/Swift-Dependencies">&lt;p&gt;With Xcode 11’s support for Swift Package Manager and the coming changes in Swift 5.3, there’s a lot of discussion on what dependency manager to use. I’d like to take you on a tour to see what happens to an app when dependencies are managed using the various tools.&lt;/p&gt;

&lt;p&gt;To do this, I measured various metrics around dependencies to see what adding dependencies looks like in the various options in 2020.&lt;/p&gt;

&lt;p&gt;Dependency Management has come a long way on iOS. In the early days, it was expected to manually add libraries and frameworks, and update them by hand. Git Submodules can be of some help here, but integrating them into Xcode projects is still tedious.&lt;/p&gt;

&lt;h2 id=&quot;the-managers&quot;&gt;The managers&lt;/h2&gt;

&lt;h3 id=&quot;cocoapods&quot;&gt;CocoaPods&lt;/h3&gt;

&lt;p&gt;As the community grew and more libraries were written, discoverability was another concern. Enter CocoaPods, which fixed all these problems: There’s a central index where pods can be discovered, and the integration into projects is now a breeze.
The value of CocoaPods to the iOS community cannot be described by words. It’s such a great example of what a community can do, and has been the de facto default package manager for iOS projects for years.
Its impact on the whole iOS ecosystem can’t be overstated. The &lt;a href=&quot;https://CocoaPods.org&quot;&gt;website&lt;/a&gt; is the go-to place for discovering iOS dependencies. Publishing a library or framework for iOS without CocoaPods support is barely imaginable.&lt;/p&gt;

&lt;p&gt;This is the Podfile used for the experiments can be found &lt;a href=&quot;https://github.com/xavierLowmiller/Swift-Dependency-Analysis/blob/main/Cocoapods/Podfile&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;carthage&quot;&gt;Carthage&lt;/h3&gt;

&lt;p&gt;Shortly after Swift came around, Carthage was born. It’s written in Swift itself, and has some &lt;a href=&quot;https://www.quora.com/CocoaPods-is-opinionated-whereas-Carthage-is-not-Why-does-that-make-it-better-What-specific-problems-did-the-Carthage-authors-experience-with-CocoaPods?share=1&quot;&gt;different opinions&lt;/a&gt; than CocoaPods. It focuses on simplicity and ease of understanding how it works. This means there’s less magic, both in the good and in the bad sense.
It’s the simple (&lt;a href=&quot;https://www.infoq.com/presentations/Simple-Made-Easy/&quot;&gt;not easy&lt;/a&gt;) dependency manager for Swift projects. There’s some beautiful ideas behind Carthage, like being decentralized and imposing as few changes to your Xcode project as possible.
There’s a &lt;a href=&quot;https://github.com/Carthage/Carthage#differences-between-carthage-and-CocoaPods&quot;&gt;great writeup&lt;/a&gt; on their GitHub page that I don’t need to repeat here. The bottom line is that it’s simpler than CocoaPods but doesn’t have (and won’t ever have) as many features. This makes the setup a little more work, but once it’s done it’s unlikely to need adjustments in the future.&lt;/p&gt;

&lt;p&gt;This is the Cartfile used for the experiments can be found &lt;a href=&quot;https://github.com/xavierLowmiller/Swift-Dependency-Analysis/blob/main/Carthage/Cartfile&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;swift-package-manager&quot;&gt;Swift Package Manager&lt;/h3&gt;

&lt;p&gt;The new kid on the block for dependency management on iOS. 
At WWDC 2019, Apple announced that the Swift Package Manager (which has been around since late 2015) would be fully integrated in Xcode and could manage iOS projects. This is big news for a platform that never had a truly official package manager!
A lot of popular libraries have since adopted SPM and it’s only going to grow bigger as important features land in SPM in Swift 5.3, such as &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md&quot;&gt;support for assets&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;alternatives-considered&quot;&gt;Alternatives Considered&lt;/h2&gt;

&lt;p&gt;In this post, I drilled down the most popular ways to manage dependencies using their most basic configurations. But of course, there’s many more ways to manage dependencies.&lt;/p&gt;

&lt;h3 id=&quot;manual-dependency-management&quot;&gt;Manual Dependency Management&lt;/h3&gt;

&lt;p&gt;A.k.a. just adding files to your project. It would have been nice to do this so I could have a baseline without any kind of module/framework/Swift Package for the measurements.
This probably works nicely with single-file dependencies (&lt;a href=&quot;https://github.com/RNCryptor/RNCryptor#installing-manually&quot;&gt;RNCryptor even recommends this technique&lt;/a&gt;), but managing more complex dependencies is tedious, especially since there’s not way to easily update things.
I tried this, but it’s just not feasible with so many dependencies, I had too many name clashes and other issues, like libraries expecting to be embedded in modules.&lt;/p&gt;

&lt;h3 id=&quot;git-submodules&quot;&gt;Git Submodules&lt;/h3&gt;

&lt;p&gt;The dependency manager when there is no dependency manager. The idea here is to add projects as git submodules and integrate their .xcodeproj files into your project’s project file or workspace.
This is a lot of setup work, and should have similar results as CocoaPods.&lt;/p&gt;

&lt;h3 id=&quot;cocoapods-with-static-libraries&quot;&gt;CocoaPods with static libraries&lt;/h3&gt;

&lt;p&gt;In the early days of Swift, it was only possible to package Swift Modules as dynamic frameworks. This is still the default for most CocoaPods, so I used that in my measurements.&lt;/p&gt;

&lt;h2 id=&quot;the-dependencies&quot;&gt;The dependencies&lt;/h2&gt;

&lt;p&gt;To do my measurements, I chose 10 dependencies that should be a somewhat realistic mix of popular libraries. They must be compatible with all dependency managers and for simplicity’s sake should be on GitHub. Some of them have dependencies themselves. They range from small (Reachability.swift, 1 file, 275 LoC) to large (RxSwift, 141 files, 10330 LoC).&lt;/p&gt;

&lt;p&gt;Here’s the list:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Alamofire/Alamofire&quot;&gt;Alamofire&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/xmartlabs/Eureka&quot;&gt;Eureka&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Moya/Moya&quot;&gt;Moya&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mxcl/PromiseKit&quot;&gt;PromiseKit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ashleymills/Reachability.swift&quot;&gt;Reachability&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ReSwift/ReSwift&quot;&gt;ReSwift&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RNCryptor/RNCryptor&quot;&gt;RNCryptor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.github.com/ReactiveX/RxSwift&quot;&gt;RxSwift&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/daltoniam/Starscream&quot;&gt;Starscream&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jpsim/Yams&quot;&gt;Yams&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All measurements were done on my developer machine, a MacBook Pro (15-inch, 2017), using Xcode 11.5 and iOS 13.5. If you’d like to follow along, all of my experiments are &lt;a href=&quot;https://github.com/xavierLowmiller/Swift-Dependency-Analysis&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;round-1-app-launch-times&quot;&gt;Round 1: App Launch Times&lt;/h2&gt;

&lt;p&gt;Historically, app launch speed has been negatively impacted, especially if you &lt;a href=&quot;https://github.com/artsy/eigen/issues/586&quot;&gt;have a lot of libraries&lt;/a&gt;. With the arrival of &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2017/413/&quot;&gt;dyld 3&lt;/a&gt;, a lot has changed here. Let’s see if launch times are still an issue for todays iOS dependency setups.&lt;/p&gt;

&lt;p&gt;I tested this using the new default launch test on both a physical iPhone 11 Pro as well as the iOS Simulator.&lt;/p&gt;

&lt;p&gt;This is the test case:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;testLaunchPerformance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;XCTOSSignpostMetric&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationLaunch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The hypothesis is that an app with 10 dependencies launches slower than an app that has no dependencies at all.
The results, however, show that there is no longer much of a measurable impact:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;No dependencies&lt;/th&gt;
      &lt;th&gt;CocoaPods&lt;/th&gt;
      &lt;th&gt;Carthage&lt;/th&gt;
      &lt;th&gt;SPM&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;iPhone 11 Pro&lt;/td&gt;
      &lt;td&gt;1.009s&lt;/td&gt;
      &lt;td&gt;1.021s&lt;/td&gt;
      &lt;td&gt;1.105s&lt;/td&gt;
      &lt;td&gt;1.032s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Simulator&lt;/td&gt;
      &lt;td&gt;1.056s&lt;/td&gt;
      &lt;td&gt;1.105s&lt;/td&gt;
      &lt;td&gt;1.118s&lt;/td&gt;
      &lt;td&gt;1.080s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;There’s still a little difference between the different methods, but that can very well be a measurement error. All setups that use dependencies launch a little slower than the “No dependencies” setup, but it’s not any serious amount of time. At 10 dependencies, it’s probably not worth optimizing for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Tie!&lt;/p&gt;

&lt;h2 id=&quot;round-2-app-size&quot;&gt;Round 2: App Size&lt;/h2&gt;

&lt;p&gt;App download size has always been an important metric, especially before Apple effectively dropped the mobile app download size limit.&lt;/p&gt;

&lt;p&gt;Let’s find out how integrating dependencies will affect your app’s size!&lt;/p&gt;

&lt;h3 id=&quot;round-2a-xcodebuild-build&quot;&gt;Round 2a: xcodebuild build&lt;/h3&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcodebuild&lt;/code&gt; will create a build product in the DerivedData directory that basically is a runnable app. We use this command to generate it:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;xcodebuild build &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-quiet&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-scheme&lt;/span&gt; Dependencies &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-configuration&lt;/span&gt; Release &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-derivedDataPath&lt;/span&gt; DerivedData
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can measure the file size of the artifact using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;du&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-sh&lt;/span&gt; DerivedData/Build/Products/Release-iphoneos/Dependencies.app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here are the results:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;No dependencies&lt;/th&gt;
      &lt;th&gt;CocoaPods&lt;/th&gt;
      &lt;th&gt;Carthage&lt;/th&gt;
      &lt;th&gt;SPM&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Total Size&lt;/td&gt;
      &lt;td&gt;140 KB&lt;/td&gt;
      &lt;td&gt;14 MB&lt;/td&gt;
      &lt;td&gt;84 MB&lt;/td&gt;
      &lt;td&gt;12 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Executable Size&lt;/td&gt;
      &lt;td&gt;104 KB&lt;/td&gt;
      &lt;td&gt;104 KB&lt;/td&gt;
      &lt;td&gt;104 KB&lt;/td&gt;
      &lt;td&gt;12 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Frameworks Folder Size&lt;/td&gt;
      &lt;td&gt;No Frameworks&lt;/td&gt;
      &lt;td&gt;14 MB&lt;/td&gt;
      &lt;td&gt;84 MB&lt;/td&gt;
      &lt;td&gt;No Frameworks&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;84 Megabytes! Yikes!&lt;/p&gt;

&lt;p&gt;I measured the binary executable size separately from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Frameworks&lt;/code&gt; folder, which are the two places where compiled code lives.
It looks like SPM wins this round, but apps are normally not distributed in a universal, uncompressed way.
Let’s see what a thinned app looks like!&lt;/p&gt;

&lt;h3 id=&quot;round-2b-app-thinning&quot;&gt;Round 2b: App Thinning&lt;/h3&gt;

&lt;p&gt;iOS 9 introduced &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2015/404/&quot;&gt;App Thinning&lt;/a&gt;, which is a technique to reduce .ipa file sizes: By default, the App Store will generate specialized versions for each kind of device, stripping away unneeded assets and executable code. Let’s see how using App Thinning changes the numbers!&lt;/p&gt;

&lt;p&gt;First, we build and export an archive so we can export thinned apps locally:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;xcodebuild archive &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-quiet&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-scheme&lt;/span&gt; Dependencies &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-configuration&lt;/span&gt; Release &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-archivePath&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'DerivedData/archive.xcarchive'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-derivedDataPath&lt;/span&gt; DerivedData
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, we can build thinned versions for specific devices:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;xcodebuild build &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-quiet&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-configuration&lt;/span&gt; Release &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-exportArchive&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-archivePath&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'DerivedData/archive.xcarchive'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-exportPath&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'DerivedData/thinned-ipa'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-exportOptionsPlist&lt;/span&gt; ../exportOptions.plist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exportOptions.plist&lt;/code&gt; contains the &lt;a href=&quot;https://www.theiphonewiki.com/wiki/Models#iPhone&quot;&gt;device identifier&lt;/a&gt; of the model we’d like to thin for, in this case it’s an iPhone 11 Pro:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;thinning&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;iPhone12,3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will create a thinned, zipped .ipa file that we can measure again:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;No dependencies&lt;/th&gt;
      &lt;th&gt;CocoaPods&lt;/th&gt;
      &lt;th&gt;Carthage&lt;/th&gt;
      &lt;th&gt;SPM&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Total Size&lt;/td&gt;
      &lt;td&gt;24 KB&lt;/td&gt;
      &lt;td&gt;2.1 MB&lt;/td&gt;
      &lt;td&gt;2.2 MB&lt;/td&gt;
      &lt;td&gt;1.7 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Total Size (unzipped)&lt;/td&gt;
      &lt;td&gt;136 KB&lt;/td&gt;
      &lt;td&gt;6.2 MB&lt;/td&gt;
      &lt;td&gt;6.5 MB&lt;/td&gt;
      &lt;td&gt;5.0 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Executable Size&lt;/td&gt;
      &lt;td&gt;88 KB&lt;/td&gt;
      &lt;td&gt;88 KB&lt;/td&gt;
      &lt;td&gt;88 KB&lt;/td&gt;
      &lt;td&gt;5.0 MB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Frameworks Folder Size&lt;/td&gt;
      &lt;td&gt;No Frameworks&lt;/td&gt;
      &lt;td&gt;6.4 MB&lt;/td&gt;
      &lt;td&gt;6.0 MB&lt;/td&gt;
      &lt;td&gt;No Frameworks&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This is a pretty neat result! Carthage still produces the largest artifacts, but it’s nowhere near the difference as in the un-thinned test.
SPM is definitely a good option when in comes to size, as it produces the smallest files. The ipa produced here is around 20% smaller than the others.
This might be due to embedding dependencies statically, so a similar result can might be achieved using CocoaPods in the static library mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Swift Package Manager!&lt;/p&gt;

&lt;h2 id=&quot;round-3-build-times&quot;&gt;Round 3: Build Times&lt;/h2&gt;

&lt;p&gt;Having fast feedback from CI and quick builds on developer machines is always a goal worth spending time on. Let’s see how the different package managers perform!&lt;/p&gt;

&lt;p&gt;I measured the following things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Dependency Resolution&lt;/li&gt;
  &lt;li&gt;Clean Build&lt;/li&gt;
  &lt;li&gt;Incremental Build&lt;/li&gt;
  &lt;li&gt;Dependency Resolution + Build (a.k.a. What you’d do in CI)&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;No dependencies&lt;/th&gt;
      &lt;th&gt;CocoaPods&lt;/th&gt;
      &lt;th&gt;Carthage&lt;/th&gt;
      &lt;th&gt;SPM&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Dependency Resolution&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;1m 38s&lt;/td&gt;
      &lt;td&gt;13m 25s&lt;/td&gt;
      &lt;td&gt;2m 23s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Clean Build&lt;/td&gt;
      &lt;td&gt;3s&lt;/td&gt;
      &lt;td&gt;1m 10s&lt;/td&gt;
      &lt;td&gt;5s&lt;/td&gt;
      &lt;td&gt;1m 20s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Incremental Build&lt;/td&gt;
      &lt;td&gt;2s&lt;/td&gt;
      &lt;td&gt;3s&lt;/td&gt;
      &lt;td&gt;2s&lt;/td&gt;
      &lt;td&gt;3s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;DR + Build&lt;/td&gt;
      &lt;td&gt;3s&lt;/td&gt;
      &lt;td&gt;2m 32s&lt;/td&gt;
      &lt;td&gt;12m 39s&lt;/td&gt;
      &lt;td&gt;2m 31s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The clear outlier here is Carthage. With it building all the frameworks during the dependency resolution phase (which is far slower than the others), it wins at clean builds.&lt;/p&gt;

&lt;p&gt;During day-to-day development (where you’re mostly doing incremental builds), there’s almost no difference, at least in this rather artificial project.&lt;/p&gt;

&lt;p&gt;Please take these results with a grain of salt. There’s no caching involved, so especially the CI use case is probably not a realistic scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Hard to say, but probably Cocoapods with Swift Package Manager as a close second.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Things are looking pretty good for Swift Package Manager. It produces small binaries and builds as fast as Cocoapods. Once all your dependencies are available on SPM, there’s no reason not to switch to it.
That said, all three dependency managers I compared are definitely viable in 2020, so there’s also no compelling reason to change dependency managers.&lt;/p&gt;

&lt;p&gt;I think it ultimately comes down to team preference or some nuances from the random tidbits section which one they should use, with Swift Package Manager being my recommendation if you can get away with it.&lt;/p&gt;

&lt;h2 id=&quot;random-tidbits&quot;&gt;Random Tidbits&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/yonaskolb/XcodeGen&quot;&gt;XcodeGen&lt;/a&gt; can make the initial Carthage setup a breeze&lt;/li&gt;
  &lt;li&gt;When running on CI such as GitHub Actions, strongly consider &lt;a href=&quot;https://github.com/actions/cache/blob/master/examples.md#swift-objective-c---carthage&quot;&gt;caching your dependencies&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Many people have been bitten by CocoaPods when merging branches or updating pod versions&lt;/li&gt;
  &lt;li&gt;Both Carthage and SPM don’t require any ruby setup on the build machines&lt;/li&gt;
  &lt;li&gt;In fact, SPM works with Xcode out of the box! If CI simplicity is a concern, consider using SPM&lt;/li&gt;
  &lt;li&gt;SPM doesn’t give you the option to check in dependencies out of the box&lt;/li&gt;
  &lt;li&gt;Some “important” dependencies like &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk&quot;&gt;Firebase&lt;/a&gt; don’t support all package managers (yet)&lt;/li&gt;
  &lt;li&gt;SPM is pretty GUI-heavy, there’s no straightforward way to use CLI to manage dependencies&lt;/li&gt;
&lt;/ul&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">With Xcode 11’s support for Swift Package Manager and the coming changes in Swift 5.3, there’s a lot of discussion on what dependency manager to use. I’d like to take you on a tour to see what happens to an app when dependencies are managed using the various tools.</summary></entry><entry><title type="html">365 Days of Exercise</title><link href="https://xavierlowmiller.github.io/blog/2020/03/08/365-Days-of-Exercise" rel="alternate" type="text/html" title="365 Days of Exercise" /><published>2020-03-08T00:00:00+00:00</published><updated>2020-03-08T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2020/03/08/365-Days-of-Exercise</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2020/03/08/365-Days-of-Exercise">&lt;p&gt;20 minutes. Every day.&lt;/p&gt;

&lt;p&gt;I don’t remember exactly when or how I decided this, but according to my records it must have been some time in December of 2018. I read about habit-forming and decided that it would be healthy to devote some time to working out every day.&lt;/p&gt;

&lt;p&gt;In my first attempt, I hit a streak of about 50 days but illness required me to take antibiotics in late February 2019, so I paused until March and got started again.&lt;/p&gt;

&lt;p&gt;My current streak just hit 365 days.&lt;/p&gt;

&lt;h2 id=&quot;benefits&quot;&gt;Benefits&lt;/h2&gt;

&lt;p&gt;There’s some obvious upside to exercising your body. Working in an office, I started having some pain in my back, shoulder and neck, but this is completely gone. I also used to suffer from pretty tough headaches that completely shut me down about every month or two. This isn’t completely gone, but it’s getting very infrequent: I can remember only a single incident in the last year.
Also, it’s not like I’m totally ripped or anything. I’m happy about my looks, but the same was true a year ago. My flexibility and posture have improved considerably, though.&lt;/p&gt;

&lt;p&gt;But there’s also some unexpected effects on my life.&lt;/p&gt;

&lt;h3 id=&quot;improved-planning&quot;&gt;Improved Planning&lt;/h3&gt;

&lt;p&gt;Usually, I don’t plan ahead a lot. This lead me to some missed appointments and a bunch of stress to make up for lost time. Now, since I schedule my exercises, I have a pretty good idea what my next 3-5 days will look like. Since I’m serious about doing sports everyday, bad planning just has to go. This also makes most of my other scheduling more realistic.&lt;/p&gt;

&lt;h3 id=&quot;overall-health&quot;&gt;Overall Health&lt;/h3&gt;

&lt;p&gt;Being ready to do sports everyday requires more than just having time to do it. Sickness that keeps me from doing exercise has to be avoided at all costs. Obviously, nobody wants to be sick, but I used to not care as much. Being ill was just a fact of life sometimes. Now, I do more for my health.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sleep is important.&lt;/li&gt;
  &lt;li&gt;I drink far less alcohol.&lt;/li&gt;
  &lt;li&gt;I eat healthier food.
Thinking about this, I should find a way to better check my nutrition and cook more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;willpower&quot;&gt;Willpower&lt;/h3&gt;

&lt;p&gt;There’s little that makes me get up from the couch at 11pm and exercise, but apparently that annoying little red badge on iPhone apps is one of those things. The app I use, &lt;a href=&quot;https://apps.apple.com/de/app/streaks/id963034692&quot;&gt;Streaks&lt;/a&gt;, has this badge and won’t stop displaying it until I check off the exercise.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2020-03-08-exercise-streaks.png&quot; alt=&quot;This is now the horror of my nightmares&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After about 20 days, losing my streak became something I dreaded. This feeling becomes stronger after 68 days. And after 125. There’s just no way you’re going to stop at 197 days.
I use this sensation for more things now (like flossing, or making photos for &lt;a href=&quot;https://1se.co&quot;&gt;1SE&lt;/a&gt;).
Perhaps surprisingly, I haven’t worked up the courage to “cheat”, and check off the day even if I haven’t exercised yet (Yes, my girlfriend did this once for me, but I did two sets the next day, I swear!).&lt;/p&gt;

&lt;p&gt;A lot of people ask me if it’s not unhealthy to work out every day. I certainly agree that not all exercise is good when done every day, I don’t notice any issues after one year. Also, I’m doing 20+ minutes, it’s not like I’m working on the same muscle group for hours each day.&lt;/p&gt;

&lt;h2 id=&quot;what-i-do&quot;&gt;What I do&lt;/h2&gt;

&lt;h3 id=&quot;the-happy-body&quot;&gt;&lt;a href=&quot;https://thehappybody.com&quot;&gt;The Happy Body&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The Happy Body is the bread and butter of this project. The exercises were designed to be done every day in 30 minutes to improve flexibility, strength, speed, posture, leanness, and ideal body weight.
After seeing the author &lt;a href=&quot;https://www.youtube.com/watch?v=IGFXJXg2Ins&quot;&gt;doing this&lt;/a&gt; at 59 years old, I bought the book. The exercises are easily learned and can be remembered well. There’s no baseline fitness required, and you ease into doing them perfectly over time.&lt;/p&gt;

&lt;p&gt;This program goes beyond just exercise and includes a comprehensive diet plan. I believe that it works and used to eat accordingly for a while, but I never really had an issue with my weight, so I don’t follow this as religiously anymore.&lt;/p&gt;

&lt;h4 id=&quot;on-the-road&quot;&gt;On the road&lt;/h4&gt;
&lt;p&gt;It’s also quite portable. The book suggests using water bottles as weights when on the road, but I usually do it without any weights and go for speed instead. Many hotels have a fitness lounge, but I’ve also done it just on the floor when there’s a carpet. Wooden floors are usually too tough for me.&lt;/p&gt;

&lt;h4 id=&quot;how-much-is-it&quot;&gt;How much is it?&lt;/h4&gt;
&lt;p&gt;The book and the weights cost me some money, but it’s a one time thing.&lt;/p&gt;

&lt;h3 id=&quot;bouldering&quot;&gt;Bouldering&lt;/h3&gt;

&lt;p&gt;Now, this is something I really enjoy. I can hang around with friends, and it’s giving me a nice sense of achievement every time I conquer a new route. Most of the time, I go for an hour or two with friends, but it can also be fun to go alone for just 30-45 minutes and really hit the limits.
I’d love to do more rope climbing as well, but it’s not nearly as convenient as bouldering. It requires a partner and a lot more equipment, and gyms aren’t as widespread as Bouldering. It’s very nice to do this outside, but this typically is an all-day affair which is easily ruined by bad weather.&lt;/p&gt;

&lt;h4 id=&quot;on-the-road-1&quot;&gt;On the road&lt;/h4&gt;
&lt;p&gt;I went to bouldering gyms while traveling to Berlin, Vienna and Mainz. They always have shoes for rent, so there’s no need to plan for taking your equipment. The chalk bag is another thing, since many places don’t have bags for rent, but it’s not as essential as one might think.&lt;/p&gt;

&lt;h4 id=&quot;how-much-is-it-1&quot;&gt;How much is it?&lt;/h4&gt;
&lt;p&gt;For equipment, I need new shoes about once a year (50-100€), some chalk (I use liquid chalk, which costs 10-15€ and lasts about half a year.
The gym costs around 10€ per visit, but I’m with Urban Sports Club, where I pay around 50-60€ each month and can enter most places for free.&lt;/p&gt;

&lt;h3 id=&quot;running&quot;&gt;Running&lt;/h3&gt;

&lt;p&gt;If all else fails, I go out for a run. I used to be more serious about running (in 2008, I ran five days a week), but nowadays I find it quite boring. There’s some situations where it’s just perfect: It’s nice when on holiday and you want to get to know the surroundings a bit. Running with colleagues after work or with conference attendees is also very good.&lt;/p&gt;

&lt;p&gt;What’s cool is that I’m pretty fast even if it’s what I do the least. I couldn’t find records of my pace ten years ago, but if I remember correctly, I’m pretty much as fast. Having great flexibility really pays off. I can run a pace of 5 minutes per Kilometer pretty easily. The only thing that isn’t as great is my stamina: Usually I want to stop after 40-45 minutes, when I used to run for 90 minutes without an issue a couple of years ago.&lt;/p&gt;

&lt;h4 id=&quot;on-the-road-2&quot;&gt;On the road&lt;/h4&gt;
&lt;p&gt;Running is the ultimate portable sport. I usually pack my 2012 Nike Airs when I spend the weekend at my girlfriend’s, and on vacation it’s usually not a problem to take the running shoes with me. I think I could run in my regular shoes pretty well, too.&lt;/p&gt;

&lt;h4 id=&quot;how-much-is-it-2&quot;&gt;How much is it?&lt;/h4&gt;
&lt;p&gt;Aside from the shoes, which I’ve had for a really long time, it’s completely free! I’m thinking of getting an Apple Watch to monitor my vitals better, but this wouldn’t just be for running.&lt;/p&gt;

&lt;h3 id=&quot;other-stuff&quot;&gt;Other stuff&lt;/h3&gt;

&lt;p&gt;When I’m on vacation, I often make it about some sport. Surfing counts. Hiking in the mountains counts.&lt;/p&gt;

&lt;p&gt;Today, I’m snowboarding in the mountains with my family.&lt;/p&gt;

&lt;h2 id=&quot;the-future&quot;&gt;The future&lt;/h2&gt;

&lt;p&gt;Exercising is never something I liked. To this day, the hardest minute is the first (after that, my body goes into autopilot mode and just does the routine). After a year, I still wouldn’t say that I enjoy most physical activity. But I deeply enjoy having done it. The shower after is great. Checking off the exercise in Streaks is great.&lt;/p&gt;

&lt;p&gt;Having done at least 20 minutes of exercise every day for the past year is a tremendous achievement to me. I’m really proud.
Next, I want to reevaluate the motivations behind it. Maybe I’ll make it more about nutrition and health. Maybe I’ll get more serious about bouldering. Maybe I’ll find another sport I really like.&lt;/p&gt;

&lt;p&gt;I don’t know when I’m stopping my streak. Like I said, the dread of breaking it is now far greater than my laziness. I’ve shown myself I can do it for a year straight, and now I can focus on something else.&lt;/p&gt;

&lt;p&gt;Maybe I’ll make it to 500.
Maybe 1000.
Maybe I won’t stop ever.&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">20 minutes. Every day.</summary></entry><entry><title type="html">Deploying a Vapor App Using Docker: The Missing Manual</title><link href="https://xavierlowmiller.github.io/blog/2020/02/06/Swift-Docker" rel="alternate" type="text/html" title="Deploying a Vapor App Using Docker: The Missing Manual" /><published>2020-02-06T00:00:00+00:00</published><updated>2020-02-06T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2020/02/06/Swift-Docker</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2020/02/06/Swift-Docker">&lt;p&gt;One of the the things that made Vapor awesome was the ability to easily deploy to Vapor Cloud in a couple of seconds. The service took care of certificate generation and subdomains, which made it perfect for prototyping backends used in iOS apps, which require TLS. Bringing you backend online was always just a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vapor cloud deploy&lt;/code&gt; away.
Unfortunately, &lt;a href=&quot;https://docs.vapor.cloud/shutdown/&quot;&gt;it’s shutting down February 29th, 2020&lt;/a&gt;, so we have to use another way to deploy our Vapor apps. I recently migrated my &lt;a href=&quot;https://github.com/xavierLowmiller/business-cards&quot;&gt;PassKit business cards&lt;/a&gt; to my company’s OpenShift platform, so I want to share my experiences working with Swift and Docker.&lt;/p&gt;

&lt;p&gt;This article is aimed at developers who aren’t experts with Docker, such as iOS developers playing around with Swift on the server (like me). If you have never used Docker before, this guide should help you!&lt;/p&gt;

&lt;h2 id=&quot;required-software&quot;&gt;Required software&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/docker-for-mac/install/&quot;&gt;Docker for Mac&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; CLI, which is available on homebrew:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  brew install docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-dockerfile&quot;&gt;The Dockerfile&lt;/h2&gt;

&lt;p&gt;Every Vapor application that is created using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vapor new&lt;/code&gt; comes with a fully configured Dockerfile (called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.Dockerfile&lt;/code&gt;) that works out of the box.&lt;/p&gt;

&lt;p&gt;In it, two containers are created:&lt;/p&gt;

&lt;h3 id=&quot;the-builder-image&quot;&gt;The builder image:&lt;/h3&gt;
&lt;p&gt;This uses the official Swift image and contains all the tools necessary to build (and test) your app.&lt;/p&gt;

&lt;h3 id=&quot;the-production-image&quot;&gt;The production image:&lt;/h3&gt;
&lt;p&gt;This is standard Ubuntu LTS image that copies the build artifacts from the builder, but has none of the build tools, so it will be lighter.&lt;/p&gt;

&lt;h2 id=&quot;building-an-image&quot;&gt;Building an image&lt;/h2&gt;

&lt;p&gt;To create the container, run the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker build &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; app:latest &lt;span class=&quot;nt&quot;&gt;--build-arg&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;production &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; web.Dockerfile &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is what the arguments do:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t app:latest&lt;/code&gt;: It’s customary to name a container, and this is the command to do so. Usually, the scheme &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;name&amp;gt;:&amp;lt;version&amp;gt;&lt;/code&gt; is used, for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app:1.0&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app:latest&lt;/code&gt;, etc. If this option is omitted, a hash for the container is generated.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--build-arg env=production&lt;/code&gt;: Vapor apps have a few default environments (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;production&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;development&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing&lt;/code&gt;) which can be checked for. It’s important to set this value, otherwise your app will crash on startup. You could also hardcode this value in the Dockerfile, or set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENVIRONMENT&lt;/code&gt; env value when you run the image later.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f web.Dockerfile&lt;/code&gt;: By default, the Dockerfile is just called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;. If that’s the case, this argument can be omitted, so you could rename &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.Dockerfile&lt;/code&gt; and not pass the argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything works as planned, the terminal will report success:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Successfully built 4cb90e95b339
Successfully tagged ledger:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s take the image for a spin!&lt;/p&gt;

&lt;h2 id=&quot;running-the-image-locally&quot;&gt;Running the image locally&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:80 app:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can now hit &lt;a href=&quot;http://localhost:8080&quot;&gt;localhost:8080&lt;/a&gt; and see your app!&lt;/p&gt;

&lt;p&gt;In the Dockerfile, the Vapor app is started on port 80 by default, but that only concerns the container. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p 8080:80&lt;/code&gt; argument is necessary to access the port from the outside.&lt;/p&gt;

&lt;p&gt;You’ll notice that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd+.&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl+C&lt;/code&gt; won’t stop the Docker process. You can list all containers and use a separate command to stop it again.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container &lt;span class=&quot;nb&quot;&gt;ls
&lt;/span&gt;docker stop 00ec27eaafa0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that this doesn’t remove the container, but only stops it. Remember to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker system prune&lt;/code&gt; from time to time to clean up dead containers.&lt;/p&gt;

&lt;h2 id=&quot;deploying&quot;&gt;Deploying&lt;/h2&gt;

&lt;p&gt;After your image is built, you can publish it to a cloud provider using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker push &amp;lt;url&amp;gt;&lt;/code&gt;. You probably need to follow some sort of authentication, but that process typically is well documented (at least &lt;a href=&quot;https://blog.openshift.com/getting-started-docker-registry/&quot;&gt;OpenShift&lt;/a&gt; is).&lt;/p&gt;

&lt;p&gt;Choosing a provider is really up to you, and most (all?) of them support Docker in this day and age. Many of them have a free tier available.&lt;/p&gt;

&lt;h2 id=&quot;more-thoughts&quot;&gt;More thoughts&lt;/h2&gt;

&lt;p&gt;Here’s some things that helped me get started:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The Docker Dashboard and Kitematic are great GUI tools to see what Docker is up to. It’s also good for setting environment variables and exposing ports.&lt;/li&gt;
  &lt;li&gt;Restarting a container wipes data unless storage is configured. This, however, is another blog post.&lt;/li&gt;
  &lt;li&gt;Docker is a power tool. It might seem hard to get started, but people who are more experienced are usually willing to help with the basics. Almost none of this is Swift-specific after all.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Having Swift and Docker play nicely is an important step on the road to ubiquitous Swift. Having an environment like Docker that is completely agnostic of the language and processes running in them enables developers to use their favorite language anywhere.
This article is meant as a Getting Started guide that takes you from knowing nothing about Docker to successfully deploying to a cloud provider.&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">One of the the things that made Vapor awesome was the ability to easily deploy to Vapor Cloud in a couple of seconds. The service took care of certificate generation and subdomains, which made it perfect for prototyping backends used in iOS apps, which require TLS. Bringing you backend online was always just a vapor cloud deploy away. Unfortunately, it’s shutting down February 29th, 2020, so we have to use another way to deploy our Vapor apps. I recently migrated my PassKit business cards to my company’s OpenShift platform, so I want to share my experiences working with Swift and Docker.</summary></entry><entry><title type="html">iOS 13 Modal Presentation Styles</title><link href="https://xavierlowmiller.github.io/blog/2019/09/12/iOS-13-Modal-Presentation" rel="alternate" type="text/html" title="iOS 13 Modal Presentation Styles" /><published>2019-09-12T00:00:00+00:00</published><updated>2019-09-12T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2019/09/12/iOS-13-Modal-Presentation</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2019/09/12/iOS-13-Modal-Presentation">&lt;p&gt;As the release of iOS 13 draws near, it’s high time to update your apps to the latest SDK. One thing you will notice immediately (besides the new dark mode) is that all the modals are different.&lt;/p&gt;

&lt;p&gt;There’s a great &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/224&quot;&gt;WWDC video&lt;/a&gt; that covers the design update. This post is meant to be a TLDR overview of the new modal styles.&lt;/p&gt;

&lt;p&gt;By default, a UIViewController will have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modalPresentationStyle&lt;/code&gt; of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.automatic&lt;/code&gt;. On iOS and iPadOS, this will mostly behave like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pageSheet&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;whats-actually-happening-here&quot;&gt;What’s actually happening here?&lt;/h2&gt;

&lt;p&gt;iOS 13 brings the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pageSheet&lt;/code&gt; style to modals on all iPhones in portrait orientation and makes it the new default. On earlier versions, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pageSheet&lt;/code&gt; only worked on some iPhones in landscape, such as on an iPhone 8 Plus:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2019-09-12-ios-12-page-sheet.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are four common use cases for modals in iOS 13:&lt;/p&gt;

&lt;h2 id=&quot;ios-12-style&quot;&gt;iOS 12 Style:&lt;/h2&gt;

&lt;p&gt;To get back the iOS 12 style, just set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modalPresentationStyle&lt;/code&gt; back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.fullScreen&lt;/code&gt;, which used to be the default.&lt;/p&gt;

&lt;p&gt;You can do this in a storyboard:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2019-09-12-storyboard-full-screen.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Or programmatically:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;viewController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modalPresentationStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullScreen&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;viewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are using segues, it’s also possible to set this property in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prepare(for:sender)&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;segue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIStoryboardSegue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;segue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modalPresentationStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullScreen&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using this style is a great idea for views that should take up the whole screen, for example when showing an image or reading modes.&lt;/p&gt;

&lt;h2 id=&quot;ios-13-page-sheet-styles&quot;&gt;iOS 13 Page Sheet styles&lt;/h2&gt;

&lt;p&gt;The new iOS 13 only styles are more interesting, as they can be dismissed by pull down out of the box. There are different use cases that you might want to support:&lt;/p&gt;

&lt;h3 id=&quot;pull-to-dismiss&quot;&gt;Pull To Dismiss&lt;/h3&gt;

&lt;p&gt;Great news: Since this is the new default (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.automatic&lt;/code&gt;), you don’t have to implement anything!
The same effect can be achieved when you set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modalPresentationStyle&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pageSheet&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;prevent-pull-to-dismiss&quot;&gt;Prevent Pull To Dismiss&lt;/h3&gt;

&lt;p&gt;In cases where the user should not be able to dismiss the modal just by swiping, but you still want the new page sheet style, dismissal can be prevented.&lt;/p&gt;

&lt;p&gt;The simplest way to implement this style is to set the UIViewController’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isModalInPresentation&lt;/code&gt; flag to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NonDismissableViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;isModalInPresentation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s also possible to set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate&lt;/code&gt; of the UIViewController’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;presentationController&lt;/code&gt; and forbid the dismissal there:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NonDismissableViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIAdaptivePresentationControllerDelegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;presentationControllerShouldDismiss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPresentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;pull-to-dismiss-with-action-sheet-confirmation&quot;&gt;Pull To Dismiss with Action Sheet Confirmation&lt;/h3&gt;

&lt;p&gt;If you want to support the user pulling to dismiss, but need to perform an action (such as ask for confirmation), you can hook into the dismissal attempt.&lt;/p&gt;

&lt;p&gt;There is a new method in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UIAdaptivePresentationControllerDelegate&lt;/code&gt; that will inform you that the user attempted to dismiss the modal by pulling down:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ActionDismissableViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIAdaptivePresentationControllerDelegate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;presentationControllerShouldDismiss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPresentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;presentationControllerDidAttemptToDismiss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;presentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIPresentationController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionSheet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;actionSheet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIAlertController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;actionSheet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIAlertController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Discard Changes?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;preferredStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionSheet&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionSheet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Yes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;nv&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destructive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;nv&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actionSheet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;No&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;nv&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionSheet&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;presentationControllerDidAttemptToDismiss&lt;/code&gt; will only be called if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;presentationControllerShouldDismiss&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;working-with-uinavigationcontroller&quot;&gt;Working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UINavigationController&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;A common use case is to wrap view controllers inside of a navigation controller and present the navigation controller modally.&lt;/p&gt;

&lt;p&gt;If you do that, be careful to set the &lt;em&gt;navigation controller’s&lt;/em&gt; delegate (and not the delegate of the view controller contained inside.&lt;/p&gt;

&lt;p&gt;Note that this only applies to the delegate, setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isModalInPresentation&lt;/code&gt; property to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; on the contained works 🤷‍♀️.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It’s really simple to adopt the new modal styles once you know where to look. At first, it might seem confusing what properties to set, but this overview should help you.&lt;/p&gt;

&lt;p&gt;As a user, I’m looking forward to having these in many apps!&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">As the release of iOS 13 draws near, it’s high time to update your apps to the latest SDK. One thing you will notice immediately (besides the new dark mode) is that all the modals are different.</summary></entry><entry><title type="html">Five Years of Swift: A Love Letter</title><link href="https://xavierlowmiller.github.io/blog/2019/06/02/Five-Years-of-Swift" rel="alternate" type="text/html" title="Five Years of Swift: A Love Letter" /><published>2019-06-02T00:00:00+00:00</published><updated>2019-06-02T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2019/06/02/Five-Years-of-Swift</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2019/06/02/Five-Years-of-Swift">&lt;p&gt;“The language is called Swift, and it totally rules.”&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Craig Federighi on June 2, 2014&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Swift’s announcement came as a total surprise to me and everybody watching the WWDC keynote. Somehow, everyone at Apple managed to keep this language that Chris Lattner and others worked on &lt;a href=&quot;https://github.com/apple/swift/commit/18844bc65229786b96b89a9fc7739c0fc897905e&quot;&gt;since 2010&lt;/a&gt; a total secret (to me, it’s almost an even greater achievement to patiently go through &lt;a href=&quot;https://youtu.be/w87fOAG8fjk?t=6234&quot;&gt;100 minutes of keynote speech&lt;/a&gt; before telling the world about it).&lt;/p&gt;

&lt;p&gt;It has been quite a ride, and I have some words for its fifth birthday.&lt;/p&gt;

&lt;h2 id=&quot;the-first-taste&quot;&gt;The first taste&lt;/h2&gt;
&lt;p&gt;In the winter semester of 2013, there wasn’t a lot of programming in the curriculum of my computer science studies, so I decided to take the &lt;a href=&quot;https://web.stanford.edu/class/cs193p/cgi-bin/drupal/&quot;&gt;Stanford iOS course&lt;/a&gt;. Before learning about iOS, I had done some C# and Java during the first two semesters, so Objective-C was a bit foreign to me. It was, however, the perfect language for me to learn at the time. I had never really worked with pointers before, Foundation and UIKit have a lot of nice examples of software architecture that came in handy in later classes, and there was a plethora of material for me to read and learn from. At the end of the semester, I finished the course and decided to apply for an internship in iOS at a local company.&lt;/p&gt;

&lt;p&gt;The phone interview was to take place on June 3rd. By this time, I was down with nil messaging. I started to grasp the big ideas behind the distinction of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSString&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSMutableString&lt;/code&gt;. I had even typed out my first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valueForKey:&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;And then came Swift.&lt;/p&gt;

&lt;p&gt;After the keynote, the idea of learning a new language in preparation of the interview seemed a little … distressing. Immediately after the download of the Xcode 6 beta (which took hours on my home internet connection), I opened a Playground and started playing: There’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt;. Xcode wants me to put exclamation marks everywhere. All the method names are strange all of a sudden. SourceKitService Crashed. Before I got the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; function working, I went to bed.&lt;/p&gt;

&lt;p&gt;(Of course, everyone on the interview had yet to try Swift. I got the job.)&lt;/p&gt;

&lt;p&gt;During the internship, I got the chance to build a whole app in Swift 1.0. We never shipped this one before rewriting it in Objective-C, because of some code signing issues that I don’t quite remember. But by then, it was already a burden to go back to the old ways. I replaced &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;* const&lt;/code&gt;, tuples became &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSDictionary&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt; on arrays turned into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valueForKey:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We didn’t really have a Swift project for a while, but on the side we played around with it a lot. Also in my studies, the Programming Languages class featured Swift: At the beginning of the course, the professor unveiled the languages we’d be working with: C/C++ (to learn about pointers and low-level stuff), JavaScript (to learn about interpretation and scripting languages), C# and Java (mainly for the JIT compilation), and Swift: To learn how C# and Java should be implemented, were they designed in 2015, and to see what the future will look like.&lt;/p&gt;

&lt;p&gt;Naturally, I liked this class quite a lot, even though a lot of the Swift features we learned about (like tuples and value semantics on arrays) were controversial with my peers. I smiled when tuples, local functions, and pattern matching were touted as a big feature of &lt;a href=&quot;https://devblogs.microsoft.com/dotnet/whats-new-in-csharp-7-0/&quot;&gt;C# 7.0&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;swift-2-and-opening-up&quot;&gt;Swift 2 and opening up&lt;/h2&gt;
&lt;p&gt;At WWDC 2015, I was surprised to see the announcement of Swift 2.0. Nobody I knew really used Swift in production code, so seeing a major version bump on the language that still felt a little beta was strange to me.&lt;/p&gt;

&lt;p&gt;The big features were protocol extensions (which I soon loved), and try/catch based error handling, which I had mixed feelings about. I had previously learned about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt; type and the error handling it enables, and discussed with many of my peers why Swift doesn’t need try/catch and why it’s a stupid idea in other languages.&lt;/p&gt;

&lt;p&gt;Still the biggest announcement for Swift at WWDC was that it’s going to be open source. This enabled me to write my &lt;a href=&quot;https://github.com/xavierLowmiller/Bachelorarbeit&quot;&gt;bachelor’s thesis&lt;/a&gt; on Swift (which is in German, sorry). It focuses on Swift’s memory management (ARC) and the Copy-on-Write implementations, which heavily use the reference counting mechanism to do its tricks. Writing it was one of the best times in my life, and I got through it pretty well. It was a lot of fun to dig through the source files and find out how everything works under the hood.&lt;/p&gt;

&lt;p&gt;While I got an A on the thesis (yay!), the best thing about open source Swift is the discussion about new features. Imagine the incredible amount of change that Swift 3 brought without understanding the reasoning! I can now fully support many decisions (like the removal of C-style for loops), and accept others (like the removal of currying) because I see what made the team arrive at these decisions. This transparency is an invaluable asset that open source and especially the open discussion has brought to the Swift community.&lt;/p&gt;

&lt;p&gt;An example of this is the try/catch error handling: After learning how it’s implemented in Swift, I now love it. I still despise the stack-unwinding madness and ‘anything can throw’ mentality of Java (much like I dislike the ‘anything can be null’ mentality).&lt;/p&gt;

&lt;h2 id=&quot;swift-3-and-becoming-mainstream&quot;&gt;Swift 3 and becoming mainstream&lt;/h2&gt;
&lt;p&gt;During the Swift 3 era, we fully switched to using it over Objective-C. New projects were already started in Swift 2, but we had some legacy projects that we inherited at the agency I’m working at. In early 2017, we made the decision to port over all of them, which works quite well due to the incredible interoperability between Objective-C and Swift, especially when going from the former to the latter.&lt;/p&gt;

&lt;p&gt;The impact this decision had on our work was profound. I don’t have a lot of data on bugs, issues, performance, etc., but a big plus was that our internal discussions shifted: Before, we were talking about peculiarities of Objective-C, and all its little gotchas. After making the switch to Swift, it was more about features and architecture.&lt;/p&gt;

&lt;p&gt;This seems to also reflect in the broader Swift community: Swift was more and more adopted as the darling of the iOS community and posts that featured Objective-C were getting rarer as time went on. I’m not sure if books like &lt;a href=&quot;https://www.objc.io/books/advanced-swift/&quot;&gt;Advanced Objective-C&lt;/a&gt; would ever have happened. Many &lt;a href=&quot;https://machinethink.net/blog/mixins-and-traits-in-swift-2.0/&quot;&gt;blog posts&lt;/a&gt; talk about features that plainly weren’t available in the old world. Most of the issues that the Clang Analyzer is great at finding are simply impossible to write in Swift.&lt;/p&gt;

&lt;p&gt;I sometimes jest that I learned everything I need to know about my job on Twitter. While I can’t really say how the community changed with the advent of Swift (because I wasn’t around to see much of it in the Objective-C days), I have never seen such a plethora of high-quality content to learn from. We are truly blessed to have such cool people publishing awesome stuff.&lt;/p&gt;

&lt;p&gt;Another big thing that happened in the Swift 3 era is the release of &lt;a href=&quot;https://vapor.codes&quot;&gt;Vapor&lt;/a&gt;. I love using Swift for iOS, and hope it has a place other realms in the future, like on the server. Using Vapor 1.0, we deployed a couple of smallish &lt;a href=&quot;https://github.com/xavierLowmiller/business-cards&quot;&gt;web apps&lt;/a&gt; to try it out a bit.&lt;/p&gt;

&lt;p&gt;True to Swift’s nature, Vapor has had a lot of API and conceptual changes between its major versions, especially going from 2 to 3. Now that it’s based on SwiftNIO and lots of cool Swift 4 features like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Codable&lt;/code&gt; and Key Paths, I think they have found their rhythm and the changes are going to be smoother in the future.&lt;/p&gt;

&lt;h2 id=&quot;swift-4-5-and-the-crazy-and-brilliant-future&quot;&gt;Swift 4, 5, and the crazy and brilliant future&lt;/h2&gt;
&lt;p&gt;Speaking of smoother migrations, the Swift 4 migration across all our projects took less than an afternoon (yes, without migrating JSON parsing to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Codable&lt;/code&gt;). Lots of really cool stuff was introduced that we now take for granted (hello conditional conformance 👋), but it was still a much smoother upgrade than going from 2 to 3. By now I feel like Swift is the mainstream language for iOS apps, and it is stable enough to explore other realms.&lt;/p&gt;

&lt;p&gt;I’m not quite sure it’s ready for &lt;a href=&quot;https://oleb.net/blog/2017/06/chris-lattner-wwdc-swift-panel/&quot;&gt;world domination&lt;/a&gt;, but we’ve come a long way. Now that there’s support for Linux, Android, Windows, and many more, it’s time to spread the wings and set for new horizons.&lt;/p&gt;

&lt;p&gt;In the future, I’m looking forward to an improved &lt;a href=&quot;https://nshipster.com/language-server-protocol/&quot;&gt;editing experience outside of Xcode&lt;/a&gt;, and the shiny proposal for &lt;a href=&quot;https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9f782&quot;&gt;asynchronous/concurrent programming&lt;/a&gt; 🤩.&lt;/p&gt;

&lt;h2 id=&quot;to-five-years&quot;&gt;To five years&lt;/h2&gt;
&lt;p&gt;Nothing seems to have had a bigger impact on my career so far (and my whole life, really) than Swift and its community and ecosystem. I couldn’t have imagined a better tool to work with these past years.&lt;/p&gt;

&lt;p&gt;So cheers, cheers to five years! Thank you for making it great.&lt;/p&gt;

&lt;p&gt;On to the next five!&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">“The language is called Swift, and it totally rules.” Craig Federighi on June 2, 2014</summary></entry><entry><title type="html">Configure Apps at Build Time</title><link href="https://xavierlowmiller.github.io/blog/2018/11/18/Configure-Apps" rel="alternate" type="text/html" title="Configure Apps at Build Time" /><published>2018-11-18T00:00:00+00:00</published><updated>2018-11-18T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2018/11/18/Configure-Apps</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2018/11/18/Configure-Apps">&lt;p&gt;In many modern, networked apps, dealing with multiple configurations is a fact
of life. Many backends in development have multiple stages with different
versions of the code or feature flags that all need to be supported by clients.
All of these stages have different URLs that need to be accsible by the iOS app.
This extends to API keys, private oauth keys, etc.&lt;/p&gt;

&lt;p&gt;Xcode’s Configurations seem like a natural fit for this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-11-18-Xcode-Configurations.png&quot; alt=&quot;Xcode Configurations&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are, however, a couple of problems with this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Private information is now part of the repo.
You might not want to share all secrets with everybody who has access to 
the code base (especially if it is open source)&lt;/li&gt;
  &lt;li&gt;Changing data in a config is a nontrivial task for developers 
not familiar with Xcode&lt;/li&gt;
  &lt;li&gt;Mixing of orthogonal concepts:
What stages should be addressed with apps that have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#DEBUG&lt;/code&gt; flag on?&lt;/li&gt;
  &lt;li&gt;It gets confusing: What is the difference between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Release&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Production&lt;/code&gt;?&lt;/li&gt;
  &lt;li&gt;You have to re-run cocoapods each time you add, remove, or rename a config&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To avoid these issues, sites like &lt;a href=&quot;https://ios-factor.com/config&quot;&gt;iOS-factor&lt;/a&gt; recommend separating the config
from the code. Unfortunately, they don’t provide an example on how to inject
configs at build time. This blog post does just that.&lt;/p&gt;

&lt;p&gt;There will be multiple parts to this. Part one (this post) will detail how to
factor out configs from code and inject it during CI. Part two will show how
configurations can be changed at run time. In the third and final part, we will
implement a solution for over-the-air (OTA) updates.&lt;/p&gt;

&lt;h2 id=&quot;step-one-extract-all-configs-to-a-separate-file&quot;&gt;Step one: Extract all configs to a separate file&lt;/h2&gt;

&lt;p&gt;Extract all the things that can change between stages to a single file.
I chose JSON here because pretty much any build system can manipulate it easily,
(and it’s much easier to feature in a blog post) but a property list can do the
job as well.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;backendUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://dev.example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;analyticsKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;s3cr3t-k3y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;redesignedLoginEnabled&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-two-load-the-file-and-use-the-configs-in-the-app&quot;&gt;Step two: Load the file and use the configs in the app:&lt;/h2&gt;

&lt;p&gt;Load the file and parse its data. I chose to include it using &lt;a href=&quot;https://nshipster.com/nsdataasset/&quot;&gt;NSDataAsset&lt;/a&gt;
because it loads trivially and applies a minimal layer of obfuscation to the
contents:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSDictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSDataAsset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Config&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;JSONSerialization&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jsonObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSDictionary&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Malformed config.json file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that the actual dictionary is marked &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;private&lt;/code&gt;. Since JSONs and property
lists don’t have strong typing, I like to write accessors for the keys expected
in the config files:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;backendUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;urlString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;backendUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urlString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Invalid/missing backend URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;analyticsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;analyticsKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;analyticsKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Invalid/missing analytics key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;analyticsKey&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;redesignedLoginEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;redesignedLoginEnabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;redesignedLoginEnabled&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Bool&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redesignedLoginEnabled&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s a lot of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fatalError&lt;/code&gt; going on here. That’s because I consider errors
in the config file programmer errors and there’s no sensible way an app can
recover from a missing URL or key.
The exception here is the feature toggle, where you can easily provide a good
fallback value.&lt;/p&gt;

&lt;p&gt;Once these accessors are in place, using the data from the config file is a
piece of cake:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backendUrl&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// https://dev.example.com&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;analyticsKey&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// s3cr3t-k3y&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;redesignedLoginEnabled&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-three-modify-the-config-file-from-ci&quot;&gt;Step three: Modify the config file from CI&lt;/h2&gt;

&lt;p&gt;The idea here is to modify the JSON file before building the app.
How you do this really depends on what CI system is in place. We use fastlane,
so here’s how you modify JSON in Ruby:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/path/to/app/Assets.xcassets/Config.dataset/config.json'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'backendURL'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'https://test.example.com'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'analyticsKey'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'k3y-for-t3st1ng-3nv'&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'w'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pretty_generate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This example sets the values in code, but you might as well read them from an
ENV or pass it to the build script some other way.&lt;/p&gt;

&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;/h2&gt;

&lt;p&gt;In today’s article, we learned how to extract app configurations from the code.
Once all the configurations are isolated to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.json&lt;/code&gt; file, there’s no
more need to include URLs or secrets in the repository.&lt;/p&gt;

&lt;p&gt;If you’re worried that you might forget to set a required value in the file
when running the code locally, you can generate &lt;a href=&quot;https://github.com/spacepandas/cineaste-ios/pull/42&quot;&gt;Xcode warnings&lt;/a&gt; in a build
script to inform you about the missing keys.&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">In many modern, networked apps, dealing with multiple configurations is a fact of life. Many backends in development have multiple stages with different versions of the code or feature flags that all need to be supported by clients. All of these stages have different URLs that need to be accsible by the iOS app. This extends to API keys, private oauth keys, etc.</summary></entry><entry><title type="html">Just Do Blocks</title><link href="https://xavierlowmiller.github.io/blog/2018/10/24/Do-Blocks" rel="alternate" type="text/html" title="Just Do Blocks" /><published>2018-10-24T00:00:00+00:00</published><updated>2018-10-24T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2018/10/24/Do-Blocks</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2018/10/24/Do-Blocks">&lt;p&gt;Swift’s error handling system relies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do&lt;/code&gt; blocks to wrap throwing statements
and handle those errors:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FileManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;An error has occurred trying to write the file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While this is obviously the most common use case, there’s nothing stopping you
from writing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do&lt;/code&gt; block without any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;try&lt;/code&gt; statements&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;I won't throw an error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Why would you do this, you might ask?&lt;/p&gt;

&lt;h3 id=&quot;variable-names&quot;&gt;Variable Names&lt;/h3&gt;

&lt;p&gt;Well, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do&lt;/code&gt; blocks have their own variable scope. Consider the following code:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;named&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Save full size image&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fullSizeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jpegData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compressionQuality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fullSizeUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fullImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fullSizeData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fullSizeUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Save thumbnail&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;thumbnailData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resizedToThumbnailSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jpegData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compressionQuality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;thumbnailUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thumbnailUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thumbnailData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thumbnailUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All that awkward naming can be avoided using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do&lt;/code&gt; blocks:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;named&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Save full size image&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jpegData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compressionQuality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fullImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Save Thumbnail&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resizedToThumbnailSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jpegData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compressionQuality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thumbnailUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s not a problem to use the same name in different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do&lt;/code&gt; blocks.&lt;/p&gt;

&lt;p&gt;I find this particularly useful with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XCTestExpectations&lt;/code&gt; in unit tests:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;XCTestExpectation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Wait for async dispatch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;loadUserData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fulfill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;XCTestExpectation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Wait for async dispatch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;loadPhotosMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fulfill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;exp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;XCTestExpectation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Wait for async dispatch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;loadPhotos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fulfill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;XCTAssert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp3&lt;/code&gt;, …!&lt;/p&gt;

&lt;h3 id=&quot;memory-management&quot;&gt;Memory Management&lt;/h3&gt;

&lt;p&gt;Furthermore, as variables go out of scope their memory can be reclaimed.
You can use this like the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using&quot;&gt;using statement&lt;/a&gt; in C#.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;postProcessFrameOfMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scrubbingPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TimeInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIImage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIImage&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Load the movie, which fills up a lot of memory&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moview&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scrubbingPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// The movie is out of scope now, its memory can be reclaimed&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;postProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is also useful for nasty stuff like locks, which should be kept around as
short as possible.&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">Swift’s error handling system relies on do blocks to wrap throwing statements and handle those errors:</summary></entry><entry><title type="html">SwiftLint For Teams</title><link href="https://xavierlowmiller.github.io/blog/2018/07/09/SwiftLint-For-Teams" rel="alternate" type="text/html" title="SwiftLint For Teams" /><published>2018-07-09T00:00:00+00:00</published><updated>2018-07-09T00:00:00+00:00</updated><id>https://xavierlowmiller.github.io/blog/2018/07/09/SwiftLint-For-Teams</id><content type="html" xml:base="https://xavierlowmiller.github.io/blog/2018/07/09/SwiftLint-For-Teams">&lt;p&gt;&lt;a href=&quot;https://github.com/realm/SwiftLint&quot;&gt;SwiftLint&lt;/a&gt; is an indispensible tool in the arsenal of the modern iOS
developer. If you have more than a single iOS developer on the team, chances
are you are already using it to enforce style guides and perform linting.
Our iOS Team discusses &lt;a href=&quot;https://github.com/realm/SwiftLint/blob/master/Rules.md&quot;&gt;new rules&lt;/a&gt; on a regular basis and decides which ones
make sense for our style guide.&lt;/p&gt;

&lt;p&gt;However, if you are working on multiple projects at the same time, updating the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; files can become a hassle. Luckily, there’s a way out of this
repetitive task.&lt;/p&gt;

&lt;h2 id=&quot;hosting-and-loading-the-shared-config&quot;&gt;Hosting and loading the shared config&lt;/h2&gt;

&lt;p&gt;We use a dedicated repository to host our coding guidelines and best practices.
Part of that repo is our default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; file that we keep updated.&lt;/p&gt;

&lt;p&gt;Luckily, most git providers make it easy to download files via an API: Here’s
how to get a file &lt;a href=&quot;https://developer.github.com/v3/repos/contents/&quot;&gt;from GitHub&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;SWIFTLINT_CONFIG_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/tmp/.swiftlint.yml&quot;&lt;/span&gt;

curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; https://raw.github.com/realm/SwiftLint/.swiftlint.yml &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SWIFTLINT_CONFIG_PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Notice the &lt;em&gt;raw&lt;/em&gt;.github.com)&lt;/p&gt;

&lt;p&gt;GitLab provides &lt;a href=&quot;https://docs.gitlab.com/ee/api/repository_files.html&quot;&gt;a similar API&lt;/a&gt;. If your provider does not have an API,
you can use the &lt;a href=&quot;https://git-scm.com/docs/git-archive&quot;&gt;git-archive&lt;/a&gt; command:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git archive &lt;span class=&quot;nt&quot;&gt;--remote&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;git://yourGitRepo.com/&amp;lt;user&amp;gt;/&amp;lt;repo&amp;gt; .swiftlint.yml | &lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; /tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-the-shared-file-for-linting&quot;&gt;Using the shared file for linting&lt;/h2&gt;

&lt;p&gt;Using the file is a trivial matter:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/usr/local/bin/swiftlint autocorrect &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SWIFTLINT_CONFIG_PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
/usr/local/bin/swiftlint lint &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SWIFTLINT_CONFIG_PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can extract all of this into a script (say, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run-swiftlint.sh&lt;/code&gt;) and replace
your SwiftLint build phase with an invocation of this script (or just paste it
in the Run Script phase).&lt;/p&gt;

&lt;p&gt;Be aware that this will download the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; on every single build.
Since this will slow local builds and rate limiting is a thing with most
git providers, this is a bad idea.&lt;/p&gt;

&lt;p&gt;In production, we use a more sophisticated script.
It will try to download the file at most every 24 hours, or if it was deleted.
Furthermore, it employs caching because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;If-Modified-Since&lt;/code&gt; requests
&lt;a href=&quot;https://developer.github.com/v3/?#staying-within-the-rate-limit&quot;&gt;do not count&lt;/a&gt; against the rate limit.&lt;/p&gt;

&lt;h2 id=&quot;overriding-rules-locally&quot;&gt;Overriding rules locally&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-07-09-first-run-after-lint.png&quot; alt=&quot;SnapshotHelper project setup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So what do you do when the new shared configuration introduces a lot of
errors to a project so that it isn’t feasible to fix them right now? Since the
new config is shared, you can’t just disable the rules temporarily.
Luckily, SwiftLint has a feature that makes this easy: &lt;br /&gt;
&lt;em&gt;nested configurations&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Consider the following project setup:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-07-09-Nested-Config.png&quot; alt=&quot;SnapshotHelper project setup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s say your style guide forbids force unwrapping because you never ever want
the app to crash in production. Your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; file then might contain a
rule that forbids them and produces errors:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;opt_in_rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;force_unwrapping&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, how do you allow force unwrapping in tests? You could use a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;//swiftlint:disable force_unwrapping&lt;/code&gt; every time you want to use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt;, 
but this gets boring quickly. Leveraging SwiftLint’s nested configurations,
this is as easy as adding another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; file to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tests&lt;/code&gt; folder
that disables the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;force_unwrapping&lt;/code&gt; rule again for this subfolder:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;disabled_rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;force_unwrapping&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can now force unwrap in your test files like there’s no tomorrow!&lt;/p&gt;

&lt;p&gt;This feature can be used to disable rules that are new to the shared config but
break the local project. Unfortunately, I haven’t found a way to put a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; file in the root folder of the project since that one is
overridden by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--config&lt;/code&gt; parameter in the script, so you have to create
separate ones for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sources&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tests&lt;/code&gt; directories.&lt;/p&gt;

&lt;h4 id=&quot;️-limitations&quot;&gt;⚠️ Limitations&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exclude&lt;/code&gt; will be ignored in nested configurations, so you
probably should have folders like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pods&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Carthage&lt;/code&gt; in the shared
configuration.&lt;/p&gt;

&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Use a shared &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.swiftlint.yml&lt;/code&gt; that is hosted somewhere on your git&lt;/li&gt;
  &lt;li&gt;Download the file before linting using a script&lt;/li&gt;
  &lt;li&gt;Replace your SwiftLint build phase with the script&lt;/li&gt;
  &lt;li&gt;Override shared rules with specific ones using Nested Configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the script we currently use across our team:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;SWIFTLINT_CONFIG_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.swiftlint.yml&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;TEMP_FILE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.temp.yml&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# If the file does not exist or is older than 1 day, download it&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SWIFTLINT_CONFIG_PATH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SWIFTLINT_CONFIG_PATH&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-mtime&lt;/span&gt; +1 &lt;span class=&quot;nt&quot;&gt;-print&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;last_modified&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SWIFTLINT_CONFIG_PATH&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;+%a, %d %b %Y %H:%M:%S GMT&quot;&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;http_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Accept: application/vnd.github.v4.raw'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;If-Modified-Since: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$last_modified&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%{http_code}&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TEMP_FILE&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        https://api.github.com/repos/adorsys/csi-coding-guidelines/contents/iOS/.swiftlint.default.yml&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; 200 &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$http_code&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TEMP_FILE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SWIFTLINT_CONFIG_PATH&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TEMP_FILE&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi
fi

if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SWIFTLINT_CONFIG_PATH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PODS_ROOT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/SwiftLint/swiftlint&quot;&lt;/span&gt; autocorrect &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SWIFTLINT_CONFIG_PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PODS_ROOT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/SwiftLint/swiftlint&quot;&lt;/span&gt; lint &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SWIFTLINT_CONFIG_PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also find the current version &lt;a href=&quot;https://github.com/adorsys/csi-coding-guidelines/blob/master/iOS/SwiftLint.md&quot;&gt;on our GitHub&lt;/a&gt;.&lt;/p&gt;</content><author><name>xavierLowmiller</name></author><summary type="html">SwiftLint is an indispensible tool in the arsenal of the modern iOS developer. If you have more than a single iOS developer on the team, chances are you are already using it to enforce style guides and perform linting. Our iOS Team discusses new rules on a regular basis and decides which ones make sense for our style guide.</summary></entry></feed>