<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[William Boles]]></title><description><![CDATA[A blog about iOS development in Swift and Objective-C]]></description><link>https://williamboles.com/</link><image><url>https://williamboles.com/favicon.png</url><title>William Boles</title><link>https://williamboles.com/</link></image><generator>Ghost 5.79</generator><lastBuildDate>Tue, 20 Feb 2024 10:12:29 GMT</lastBuildDate><atom:link href="https://williamboles.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Ambiguous Decoding]]></title><description><![CDATA[Dealing with a JSON network response in iOS projects used to be a pain - you would have to manually parse the response, extract the required values, ignore those that weren't needed, and build your model instances 🤮. Then came along `Codable` and all that changed.]]></description><link>https://williamboles.com/ambiguous-decoding/</link><guid isPermaLink="false">63c1f9321d3785003d054e08</guid><category><![CDATA[networking]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Mon, 23 Jan 2023 22:33:02 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Dealing with a JSON network response in iOS projects used to be a pain - you would have to manually parse the response, extract the required values, ignore those that weren&apos;t needed, and build your model instances &#x1F92E;. At best, it was tedious work; at worst, it was a source of bugs. It wasn&apos;t long before a whole host of 3rd party solutions were developed to automate away the process of matching a JSON response to a Swift type. As sometimes happens when a 3rd party solution gains traction in the developer community, this functionality was pulled into Swift itself. In Swift 4, we got native support for encoding and decoding JSON in the form of <a href="https://developer.apple.com/documentation/foundation/jsonencoder?ref=williamboles.com"><code>JSONEncoder</code></a> and <a href="https://developer.apple.com/documentation/foundation/jsondecoder?ref=williamboles.com"><code>JSONDecoder</code></a> that worked side-by-side with two protocols <a href="https://developer.apple.com/documentation/swift/encodable?ref=williamboles.com"><code>Encodable</code></a> and <a href="https://developer.apple.com/documentation/swift/decodable?ref=williamboles.com"><code>Decodable</code></a> to make converting between Swift types and JSON <a href="https://dictionary.cambridge.org/dictionary/english/easy-peasy?ref=williamboles.com">easy-peasy</a>.</p>
<blockquote>
<p><code>Encodable</code> and <code>Decodable</code> protocols are often combined as the <code>Codable</code> protocol.</p>
</blockquote>
<p>For encoding, take the type you want to encode, conform it to <code>Encodable</code> and pass it to an instance of <code>JSONEncoder</code>:</p>
<pre><code class="swift">struct Example: Encodable {
    let title: String
}

let example = Example(title: &quot;Stay wonderful!&quot;)

let encoded = try JSONEncoder().encode(example)</code></pre>
<p><code>encoded</code> holds a JSON representation of an instance of <code>Example</code>.</p>
<p>Decoding is equally as simple. Take the below JSON response:</p>
<pre><code class="json">{
    &quot;title&quot;:&quot;Will do!&quot;
}</code></pre>
<p>The above JSON can be <em>decoded</em> into an <code>Example</code> instance by conforming <code>Example</code> to <code>Decodable</code> and passing it, along with the JSON response in <code>Data</code> form, to an instance of <code>JSONDecoder</code>:</p>
<pre><code class="swift">struct Example: Encodable, Decodable {
    let title: String
}

let decoded = try JSONDecoder().decode(Example.self, from: data)</code></pre>
<p><code>decoded</code> now holds an <code>Example</code> instance with a <code>title</code> value of: <code>Will do!</code>.</p>
<p>We get a lot of functionality for the amount of code written above. We get that functionality because <code>Encodable</code> and <code>Decodable</code> are more than just protocols. Conforming a type to either protocol triggers the compiler to synthesise conformance to those protocols (notice how <code>Example</code> doesn&apos;t implement any of the methods defined in either <code>Encodable</code> or <code>Decodable</code>). In order to synthesise conformance, the compiler makes several assumptions about how our Swift type matches its JSON counterpart.</p>
<p><img src="https://williamboles.com/content/images/2023/01/bridging-the-gap-between-the-sides.jpg" alt="A photo of a bridge spanning two shores and so making it possible to travel between them" loading="lazy"></p>
<p>In this article, I want to explore what happens when one of those assumptions proves false. When automatic decoding/encoding isn&apos;t possible because the structure of the JSON representation can&apos;t be directly converted into a Swift representation due to differences between how JSON treats data and how Swift treats data.</p>
<h4 id="overcoming-differences-%F0%9F%A4%9D">Overcoming differences &#x1F91D;</h4>
<p>The <a href="https://developer.apple.com/documentation/swift/array?ref=williamboles.com">array</a> type in Swift is homogeneous, i.e. each element is of the same type; the <a href="https://www.w3schools.com/js/js_json_arrays.asp?ref=williamboles.com">array</a> type in JSON is heterogeneous, i.e. elements can be of different types. This can present a tricky issue for us as consumers of a JSON endpoint that returns different types in the same array.</p>
<p>Let&apos;s take the below JSON response as an example:</p>
<pre><code class="json">{
   &quot;media&quot;:[
      {
         &quot;media_type&quot;:&quot;text&quot;,
         &quot;id&quot;:12,
         &quot;text&quot;:&quot;This is an example of text media&quot;
      },
      {
         &quot;media_type&quot;:&quot;image&quot;,
         &quot;id&quot;:2785,
         &quot;caption&quot;:&quot;This is an example of image media&quot;,
         &quot;url&quot;:&quot;https://example.com/images/2785.jpg&quot;
      }
   ]
}</code></pre>
<p>Here the array, <code>media</code>, is heterogeneous as it contains 2 different JSON objects: <code>text</code> and <code>image</code>. Directly converting the JSON <code>media</code> array into a Swift array isn&apos;t possible as there is no way to declare a Swift array that holds multiple types.</p>
<p>However, it is possible to indirectly hold Swift representations of <code>text</code> and <code>image</code> in an array if those two types are grouped under a common type. In Swift, an <code>enum</code> is the perfect data structure for grouping a suite of distinct but related types.</p>
<p>Using an <code>enum</code>, it is possible to customise the decoding process to extract elements from the above <code>JSON</code> response as distinct objects and still keep them grouped in the same array.</p>
<p>Let&apos;s start by looking at how we determine what type each element in the <code>media</code> array is:</p>
<pre><code class="swift">//1
struct Content: Decodable {
    let media: [Media]
}

//2
enum Media: Decodable {
    case text
    case image

    //3
    enum CodingKeys: String, CodingKey {
        case mediaType = &quot;media_type&quot;
    }

    // MARK: - Init

    //4
    init(from decoder: Decoder) throws {
        //5
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(String.self, forKey: .mediaType)

        //6
        switch type {
        case &quot;text&quot;:
            self = .text
        case &quot;image&quot;:
            self = .image
        default:
            fatalError(&quot;Unexpected media type encountered&quot;)
        }
    }
}</code></pre>
<p>Let&apos;s walk through the above code:</p>
<ol>
<li><code>Content</code>, which conforms to <code>Decodable</code>, holds the array of all <code>Media</code> instances and is used to mirror the JSON structure.</li>
<li><code>Media</code>, which conforms to <code>Decodable</code>, is an artificial type that expresses each known media type as a case.</li>
<li>As <code>Media</code> doesn&apos;t have any properties, no coding keys are synthesised. So <code>Media</code> has to define its own - <code>CodingKeys</code>. <code>CodingKeys</code> conforms to <code>CodingKey</code>, which the <code>JSONDecoder</code> instance expects its keys to be. The <code>CodingKeys</code> enum only contains one case as the only information from the JSON that <code>Media</code> needs to know about to determine what case to be is - <code>media_type</code>.</li>
<li>In order to customise the decoding process, <code>Media</code> needs to implement it&apos;s own <code>init(from decoder: Decoder) throws</code> method rather than depend on the synthesised version.</li>
<li>A container is created using the keys declared in the <code>CodingKeys</code> enum, with the <code>media_type</code> value extracted as a <code>String</code> instance.</li>
<li><code>type</code> is switched over to compare against the 2 supported media types. If <code>type</code> is a match for the string representation, <code>self</code> is set to that case; if there is no match, a fatal error is thrown.</li>
</ol>
<blockquote>
<p>The <code>fatalError</code> could be replaced with an <code>unknown</code>/<code>unsupported</code> case if crashing the app here is undesired.</p>
</blockquote>
<p>While <code>Media</code> can determine which type each element in <code>media</code> is, it&apos;s not that useful on its own. Let&apos;s extend <code>Media</code> to capture the details of each element in <code>media</code>:</p>
<pre><code class="swift">struct Text: Decodable {
    let id: Int
    let text: String
}

struct Image: Decodable {
    let id: Int
    let caption: String
    let url: URL
}</code></pre>
<p><code>Text</code> and <code>Image</code> each conform to <code>Decodable</code> and mirror their respective JSON object. <code>Text</code> and <code>Image</code> will be used as associated values to the cases in <code>Media</code>.</p>
<blockquote>
<p>Note that we didn&apos;t need to implement <code>init(from decoder: Decoder) throws</code> here as the synthesised implementation is perfect for our needs.</p>
</blockquote>
<p>Let&apos;s alter <code>Media</code> to make use of <code>Text</code> and <code>Image</code>:</p>
<pre><code class="swift">enum Media: Decodable {
    //1
    case text(Text)
    case image(Image)

    //Omitting unchanged code

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(String.self, forKey: .mediaType)

        //2
        switch type {
        case &quot;text&quot;:
            let text = try Text(from: decoder)
            self = .text(text)
        case &quot;image&quot;:
            let image = try Image(from: decoder)
            self = .image(image)
        default:
            fatalError(&quot;Unexpected media type encountered&quot;)
        }
    }
}</code></pre>
<ol>
<li>Each <code>case</code> now has a dedicated type for that media type as an associated value.</li>
<li>For each media type, the <code>Decoder</code> instance is passed into <code>Text</code> or <code>Image</code> as needed to continue the decoding process at the next level.</li>
</ol>
<p>We can test our decoding implementation by:</p>
<pre><code class="swift">let json = &quot;&quot;&quot;
{
   &quot;media&quot;:[
      {
         &quot;media_type&quot;:&quot;text&quot;,
         &quot;id&quot;:12,
         &quot;text&quot;:&quot;This is an example of text media&quot;
      },
      {
         &quot;media_type&quot;:&quot;image&quot;,
         &quot;id&quot;:2785,
         &quot;caption&quot;:&quot;This is an example of image media&quot;,
         &quot;url&quot;:&quot;https://example.com/images/2785.jpg&quot;
      }
   ]
}
&quot;&quot;&quot;

let content = try JSONDecoder().decode(Content.self, from: json.data(using: .utf8)!)</code></pre>
<p>If everything went well, <code>Content</code> should contain the same data as <code>media</code> does in the JSON representations.</p>
<blockquote>
<p>You can see this in action by running the <code>ContentTests</code> in the linked <a href="https://github.com/wibosco/AmbiguousCodable-Example?ref=williamboles.com">project</a>.</p>
</blockquote>
<p>Now that the decoding side has been explored let&apos;s look at how to encode <code>Content</code>:</p>
<pre><code class="swift">//1
struct Content: Decodable, Encodable {
    let media: [Media]
}

//2
enum Media: Decodable, Encodable {
    //Omitted unchanged properties and methods

    // MARK: - Encode

    //3
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        let object: Codable
        let type: String

        switch self {
        case .text(let text):
            type = &quot;text&quot;
            object = text
        case .image(let image):
            type = &quot;image&quot;
            object = image
        }

        try container.encode(type, forKey: .mediaType)
        try object.encode(to: encoder)
    }
}

//4
struct Text: Decodable, Encodable {
    //Omitted unchanged properties
}

//5
struct Image: Decodable, Encodable {
    //Omitted unchanged properties
}</code></pre>
<blockquote>
<p>I could have used <code>Codable</code>, which combines <code>Decodable</code> and <code>Encodable</code>, but I kept them separate in this article for clarity.</p>
</blockquote>
<ol>
<li>To support encoding, <code>Content</code> now needs to conform to <code>Encodable</code>.</li>
<li>To support encoding, <code>Media</code> now needs to conform to <code>Encodable</code>.</li>
<li>As the Swift implementation of a JSON <code>media</code> object is two types, a custom <code>func encode(to encoder: Encoder) throws</code> needs to be implemented to combine those two types back into one. Here a container is made from <code>CodingKeys</code> so that the media type can be encoded before the encoder is passed to the enum cases associated value type instance to add the stored properties of that instance to the data that&apos;s already been encoded.</li>
<li>To support encoding, <code>Text</code> now needs to conform to <code>Encodable</code>.</li>
<li>To support encoding, <code>Image</code> now needs to conform to <code>Encodable</code>.</li>
</ol>
<blockquote>
<p>Using an <code>enum</code> to bridge the gap between JSON and Swift isn&apos;t only limited to elements in an <code>array</code>. Another common use case is where the structure of the JSON object is the same, but the type of a field changes, e.g. sometimes an <code>Int</code>, sometimes a <code>String</code>, etc. - in this case, we use an enum to represent the type in pretty much the same way as shown above.</p>
</blockquote>
<p>And that&apos;s it! &#x1F973;</p>
<h4 id="looking-back">Looking back</h4>
<p>As we have seen, <code>Decodable</code> and <code>Encodable</code> are easy-to-use, powerful tools in the Swift toolkit. While on the surface, <code>Decodable</code> and <code>Encodable</code> are just protocols, when combined with the synthesised functionality we get from the compiler, we get the ability in most cases to convert from JSON into Swift types and vice versa with either no customisation or at least very little. Even in our tricky JSON example that proved too complex for the synthesised functionality to automatically convert to Swift, the amount of code we had to write wasn&apos;t much.</p>
<p>To see the above code snippets in a working example alongside unit tests, head over to the <a href="https://github.com/wibosco/AmbiguousCodable-Example?ref=williamboles.com">repo</a> and clone the project.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Finding hope in custom alerts]]></title><description><![CDATA[UIAlertController alerts form the backbone of a lot of the interactions between our users and our apps. While there have been some changes to alerts over the years, very little has changed about their appearance or our ability to customise that appearance - this causes for app designers pain 😱]]></description><link>https://williamboles.com/finding-hope-with-custom-alerts/</link><guid isPermaLink="false">5cfd59e7b015b70037135a14</guid><category><![CDATA[ui]]></category><category><![CDATA[alerts]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Tue, 26 May 2020 08:18:59 GMT</pubDate><media:content url="https://williamboles.com/content/images/2020/05/photo-of-designer-sitting-on-cliff-edge-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2020/05/photo-of-designer-sitting-on-cliff-edge-2.jpg" alt="Finding hope in custom alerts"><p><a href="https://developer.apple.com/documentation/uikit/uialertcontroller?ref=williamboles.com"><code>UIAlertController</code></a> alerts form the backbone of a lot of the interactions between our users and our apps. Alerts are often shown at those critical points in our apps where we are asking to confirm an action or allow access to a resource. While there have been some changes to alerts over the years, very little has changed about their appearance. This lack of customisation presents <em>significant</em> difficulties for app designers &#x1F631;. Having an <code>UIAlertController</code> alert pop up at the most critical points of our apps with its semi-transparent rectangular layout and standard fonts, breaking the app&apos;s theme is enough to make any app designer spill their <a href="https://en.wikipedia.org/wiki/Flat_white?ref=williamboles.com">flat white coffee</a> down their checkered shirt and throw their <a href="https://en.wikipedia.org/wiki/Caran_d%27Ache_(company)?ref=williamboles.com">Caran d&apos;Ache</a> coloured pencils across the table with an angst that few could ever comprehend, never mind experience. At this point, as you gaze into their tear-filled eyes, you offer a few comforting words:</p>
<p><strong>&quot;We can always write our own custom alerts instead&quot;</strong></p>
<p>Slowly as your words start to cut through their anguish, they start to nod, and with strength returning to their voice, they say:</p>
<p><strong>&quot;Sounds good man, I&apos;ll get started on those right away&quot;</strong></p>
<p>And with that, you turn away and start thinking about how you can present those custom alerts.</p>
<p><img src="https://williamboles.com/content/images/2020/05/photo-of-designer-sitting-on-cliff-edge.jpg" alt="Finding hope in custom alerts" loading="lazy"></p>
<p>This article will look at how to build a custom alert presenter that will try and follow most of the conventions used by <code>UIAlertController</code> alerts. Along the way, we will even overcome a particularly tricky and challenging to reproduce navigation bug &#x1F4AA;.</p>
<blockquote>
<p>This post will gradually build up to a working example however if you are too excited and want to jump ahead then head on over to the <a href="https://github.com/wibosco/CustomAlert-Example?ref=williamboles.com">completed example</a> and take a look at <code>AlertPresenter</code> and <code>AlertWindow</code> to see how things end up.</p>
</blockquote>
<h3 id="thinkingaboutbeingstandard">Thinking about being standard</h3>
<p>A standard <code>UIAlertController</code> alert has 4 parts:</p>
<ol>
<li>Foreground view.</li>
<li>Background view.</li>
<li>Presentation animation.</li>
<li>Dismissal animation.</li>
</ol>
<p>The first thing to decide is:</p>
<p>&quot;What is part of the custom alert, and what is part of the mechanism to show an alert?&quot;</p>
<p>Let&apos;s look at how <code>UIAlertController</code> handles the 4 parts of an alert:</p>
<ol>
<li>Foreground view - <code>UIAlertController</code> allows some customisation.</li>
<li>Background view - <code>UIAlertController</code> handles this for us.</li>
<li>Presentation animation - <code>UIAlertController</code> handles this for us.</li>
<li>Dismissal animation - <code>UIAlertController</code> handles this for us.</li>
</ol>
<p>The only customisable part of an <code>UIAlertController</code> alert is the foreground view. This lack of control with the other parts may at first feel limiting, but by preventing customisation of 3-of-the-4 parts, iOS forces us to focus the most critical part - <strong>the message</strong>. The message is contained in the foreground view.</p>
<p>Just like <code>UIAlertController</code>, the alert presentation layer we will build below will only allow the foreground view will be customisable. This limitation will ensure that presenting and dismissing alerts will happen consistently across the app. Instead of the foreground view being a <code>UIView</code> instance, it will be a <code>UIViewController</code> instance to provide greater control to our custom alerts. This <code>UIViewController</code> instance will be added to the view hierarchy as a child view-controller. This functionality will come together in the following class structure:</p>
<p><img src="https://williamboles.com/content/images/2020/05/custom-alert-class-diagram-3.jpg" alt="Finding hope in custom alerts" loading="lazy"></p>
<ul>
<li><code>AlertPresenter</code> is the entry point for presenting and dismissing alerts.</li>
<li><code>AlertWindow</code>, as we will see shortly, each alert is presented in its own <code>UIWindow</code> instance. Used to overcome that tricky navigation issue we spoke about above.</li>
<li><code>HoldingViewController</code>, the windows root view-controller that is responsible for presenting the <code>AlertContainerViewController</code> that will hold the alert view-controller as a child view-controller.</li>
<li><code>AlertContainerViewController</code>, the parent view-controller that the alert view-controller is embedded in as a child view-controller.</li>
<li><code>CustomAlertPresentAnimationController</code> is responsible for presenting the <code>AlertContainerViewController</code> instance with the same animation as a standard <code>UIAlertController</code>.</li>
<li><code>CustomAlertDismissAnimationController</code> is responsible for dismissing the <code>AlertContainerViewController</code> instance with the same animation as a standard <code>UIAlertController</code>.</li>
</ul>
<p><code>HoldingViewController</code>, <code>AlertContainerViewController</code>, <code>CustomAlertPresentAnimationController</code> and <code>CustomAlertDismissAnimationController</code> are private and only known to <code>AlertWindow</code>.</p>
<blockquote>
<p>Don&apos;t worry if that doesn&apos;t all make sense yet, we will look into each class in greater depth below.</p>
</blockquote>
<p>Let&apos;s start with <code>AlertPresenter</code>:</p>
<pre><code class="swift">class AlertPresenter {
    static let shared = AlertPresenter()

    // MARK: - Present

    func presentAlert(_ viewController: UIViewController) {
        os_log(.info, &quot;Alert being presented&quot;)

        //TODO: Present
    }
}</code></pre>
<p><code>AlertPresenter</code> is a singleton, so the same instance will be used to present (and dismiss) all alerts. As <code>AlertPresenter</code> isn&apos;t a <code>UIViewController</code> subclass, it&apos;s not possible to directly present the alert. Instead, we are going to use a dedicated <code>UIWindow</code> instance to present alerts from. Using a dedicated <code>UIWindow</code> instance should avoid the situation where multiple simultaneous navigation events (presentation/dismissal) occur at the same time, resulting in one of those events being cancelled and the following error is generated:</p>
<p><img src="https://williamboles.com/content/images/2020/05/screenshot_of_navigation_collision-1.jpg" alt="Finding hope in custom alerts" loading="lazy"></p>
<p>The navigation stack in one window is independent of the navigation stack of any other windows. An alert in the process of being presented on window A will not cause a navigation collision with a view-controller being pushed on window B &#x1F973;.</p>
<p>Before delving into how to use a dedicated window to present alerts, let&apos;s get to know windows better.</p>
<blockquote>
<p>If you are comfortable with how <code>UIWindow</code> works, feel free to <a href="#usingwindows">skip ahead</a>.</p>
</blockquote>
<h3 id="gettingtoknowwindows">Getting to know windows &#x1F4AD;</h3>
<p><code>UIWindow</code> is a subclass of <code>UIView</code> that acts as the container for an app&apos;s visible content - it is the top of the view hierarchy. All views that are displayed to the user need to be added to a window. An app can have multiple windows, but only windows that are <em>visible</em> can have their content displayed to the user - by default windows are not visible. Multiple windows can be visible at once. Each window&apos;s UI stack is independent of other window&apos;s UI stacks. Where a window is displayed in regards to other visible windows is controlled by setting that window&apos;s <code>windowLevel</code> property. The higher the <code>windowLevel</code> the <em>nearer</em> to the user that window is. <code>UIWindow</code> has three default levels:</p>
<ol>
<li><code>.normal</code></li>
<li><code>.statusBar</code></li>
<li><code>.alert</code></li>
</ol>
<p>With <code>.alert</code> &gt; <code>.statusBar</code> &gt; <code>.normal</code>. If a more fine-grain level of control is needed it&apos;s possible to use a custom level:</p>
<pre><code class="swift">window.windowLevel = .normal + 25</code></pre>
<blockquote>
<p>As of iOS 13: <code>.alert</code> has a raw value of 2000, <code>.statusBar</code> has a raw value of 1000 and <code>.normal</code> has a raw value of 0.</p>
</blockquote>
<p>Where two or more windows have the same level, their ordering is determined by the order they were made visible in - the last window made visible is <em>nearest</em> one to the user.</p>
<p>It&apos;s unusual to directly add subviews to a window instead each window should have a root view-controller who&apos;s view is used as the window&apos;s initial subview.</p>
<p>As well as displaying content, <code>UIWindow</code> is also responsible for forwarding any <a href="https://developer.apple.com/documentation/uikit/uievent/eventtype?ref=williamboles.com">events</a> (touch, motion, remote-control or press) to interested parties in it&apos;s <a href="https://swiftrocks.com/understanding-the-ios-responder-chain.html?ref=williamboles.com">responder chain</a>. While all touch events are forwarded to the responder chain of the window that the event occurred on, events that are outside of the app&apos;s UI such as motion events or keyboard entry, are forwarded to the <em>key</em> window. Only one window can be <em>key</em> at any one given time (which window is <em>key</em> can change throughout the lifetime of the app).</p>
<p>An iOS app needs at least one window, a reference to this window can be found in the app delegate:</p>
<pre><code class="swift">@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    //Omitted methods
}</code></pre>
<p>If you&apos;re using storyboards to layout your interface, then most of the work of setting up this window is happening under the hood. When the app is launched, a <code>UIWindow</code> instance is created that fills the screen. This window is then assigned to the <code>window</code> property (declared in the <code>UIApplicationDelegate</code> protocol), configured with the view-controller declared as the <em>storyboard entry point</em> from the project&apos;s main storyboard and made <strong>key and visible</strong>.</p>
<p>If you are not using storyboards you can better see this setup:</p>
<pre><code class="swift">@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    // MARK - AppLifecycle

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {
        window = UIWindow()
        window?.rootViewController = YourViewController()
        window?.makeKeyAndVisible()

        return true
    }
}</code></pre>
<h3 id="usingadedicatedwindow">Using a dedicated window</h3>
<p>As this new window will only display alerts, let&apos;s subclass <code>UIWindow</code> for this single purpose:</p>
<pre><code class="swift">class AlertWindow: UIWindow {

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        super.init(frame: UIScreen.main.bounds)

        // 1
        rootViewController = viewController

        // 2
        windowLevel = .alert
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError(&quot;Unavailable&quot;)
    }

    // MARK: - Present

    // 3
    func present() {
        makeKeyAndVisible()
    }
}</code></pre>
<p>In <code>AlertWindow</code> we:</p>
<ol>
<li>Set the alert as the window&apos;s <code>rootViewController</code>.</li>
<li>Set the window level value to <code>.alert</code>. The <code>.alert</code> level will put this window above the app&apos;s main window, ensuring that it can be seen.</li>
<li><code>present()</code> is a bit of syntactic sugar around making the window key and visible, it will act as a mirror to the soon-to-be-seen dismiss method.</li>
</ol>
<blockquote>
<p>An <code>AlertWindow</code> instance is only meant to show one alert and then be disposed of.</p>
</blockquote>
<p>Let&apos;s use <code>AlertWindow</code>to present alerts:</p>
<pre><code class="swift">class AlertPresenter {
    // 1
    private var alertWindows = Set<alertwindow>()

    //Omitted other properties

    // MARK: - Present

    // 2
    func presentAlert(_ viewController: UIViewController) {
        os_log(.info, &quot;Alert being presented&quot;)

        let alertWindow = AlertWindow(withViewController: viewController)
        alertWindow.present()

        alertWindows.insert(alertWindow)
    }
}</alertwindow></code></pre>
<p>With the above changes we:</p>
<ol>
<li>Store all windows that are being presented in a <a href="https://developer.apple.com/documentation/swift/set?ref=williamboles.com">Set</a>. Storing the window inside the set will keep that window alive by increasing its retain count (as making a window key-and-visible doesn&apos;t increase the retain count).</li>
<li>Create an <code>AlertWindow</code> instance using the alert to be presented and then instruct that window to present itself.</li>
</ol>
<p>If you were to create an instance of <code>AlertWindow</code> and make it visible, you would notice that the alert is presented without an animation - it just appears. A window&apos;s root view-controller cannot be animated on-screen so an intermediate view-controller is needed which can be the window&apos;s root view-controller and then the alert can be presented from that view-controller:</p>
<pre><code class="swift">class HoldingViewController: UIViewController {
    private let viewController: UIViewController

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        self.viewController = viewController
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError(&quot;init(coder:) has not been implemented&quot;)
    }

    // MARK: - ViewLifecycle

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        present(viewController, animated: true, completion: nil)
    }
}</code></pre>
<p><code>HoldingViewController</code> only has one responsibility - presenting an alert once <code>viewDidAppear(_:)</code> has been called.</p>
<blockquote>
<p>Trying to present an alert earlier will cause a navigation collision due to the <code>HoldingViewController</code> instance having not finished its own &quot;animation&quot; on screen. While (at the time of writing - iOS 13) this doesn&apos;t seem to affect the actual presentation of the alert, waiting for <code>HoldingViewController</code> to be presented before attempting another presentation ensures that the error isn&apos;t produced.</p>
</blockquote>
<p>Lets go back to <code>AlertWindow</code> and make use of <code>HoldingViewController</code>:</p>
<pre><code class="swift">class AlertWindow: UIWindow {
    private let holdingViewController: HoldingViewController

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        holdingViewController = HoldingViewController(withViewController: viewController)
        super.init(frame: UIScreen.main.bounds)

        rootViewController = holdingViewController

        windowLevel = .alert
    }


    // MARK: - Present

    func present() {
        makeKeyAndVisible()
    }
}</code></pre>
<p>If you run the project with the above changes and hook in your own alert, you would notice two issues:</p>
<ol>
<li>Sizing - the alert occupies the full-screen.</li>
<li>Presentation - the alert is animated from bottom to top.</li>
</ol>
<h3 id="sizingalerts">Sizing alerts</h3>
<p>An alert should be shown at the smallest size possible - we can&apos;t do this if that alert <em>is</em> the modal. Instead, we need to embed that alert into another view controller. <code>HoldingViewController</code> can&apos;t be used for this as it is being used as the window&apos;s root view-controller so can&apos;t be animated on-screen. We need to introduce a new view-controller that will act as a container so that that container can be animated on-screen from the <code>HoldingViewController</code> instance:</p>
<pre><code class="swift">class AlertContainerViewController: UIViewController {
    let childViewController: UIViewController

    // MARK: - Init

    // 1
    init(withChildViewController childViewController: UIViewController) {
        self.childViewController = childViewController
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError(&quot;init(coder:) has not been implemented&quot;)
    }

    // MARK: - ViewLifecycle

    override func viewDidLoad() {
        super.viewDidLoad()

        // 2
        addChild(childViewController)
        view.addSubview(childViewController.view)
        childViewController.didMove(toParent: self)

        // 3
        childViewController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            childViewController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            childViewController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            childViewController.view.widthAnchor.constraint(lessThanOrEqualTo: view.widthAnchor, multiplier: 1),
            childViewController.view.heightAnchor.constraint(lessThanOrEqualTo: view.heightAnchor, multiplier: 1)
        ])
    }
}</code></pre>
<p>In <code>AlertContainerViewController</code> we:</p>
<ol>
<li>Store the alert passed in via the init&apos;er as a property.</li>
<li>Embed the alert as a child view-controller.</li>
<li>Centre the alert and constrain it to (at maximum) the width of the view&apos;s width and height.</li>
</ol>
<blockquote>
<p>I don&apos;t view an alert that is too large for the container as being the presentation layers problem to solve - if an alert is too large for a single screen, I&apos;d question if it <em>really</em> is an alert.</p>
</blockquote>
<p>Let&apos;s update <code>HoldingViewController</code> to use <code>AlertContainerViewController</code>:</p>
<pre><code class="swift">class HoldingViewController: UIViewController {
    let containerViewController: AlertContainerViewController

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        // 1
        containerViewController = AlertContainerViewController(withChildViewController: UIViewController)
        super.init(nibName: nil, bundle: nil)
    }

    //Omitted other methods

    // MARK: - ViewLifecycle

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // 2
        present(containerViewController, animated: true, completion: nil)
    }
}</code></pre>
<p>With the changes to <code>HoldingViewController</code> we:</p>
<ol>
<li>Create an <code>AlertContainerViewController</code> instance.</li>
<li>Modally present that <code>AlertContainerViewController</code> instance rather than the alert.</li>
</ol>
<p>Now if you run the above code with your <code>UIViewController</code> subclass, you would notice that your alert is compressed to be as small as it can be and centred in the screen.</p>
<p>Time to address the second point - <strong>Presentation</strong>.</p>
<h3 id="presentingalerts">Presenting alerts</h3>
<p>Let&apos;s change the presentation animation to make it feel more like the <code>UIAlertController</code>.</p>
<p>As we move from one view-controller to another, we are used to seeing different types of animation, the most common being:</p>
<ul>
<li>Push: the new view slides in from the side on top of the current view.</li>
<li>Pop: the current view slides out to the side to reveal another view underneath.</li>
<li>Present: the new view slides up from the bottom on top of the current view.</li>
<li>Dismiss: the current view slides down to reveal another vie underneath.</li>
</ul>
<p>These view transactions are provided free by iOS. However it is possible to specify our own view transactions by using the <a href="https://developer.apple.com/documentation/uikit/animation_and_haptics/view_controller_transitions?ref=williamboles.com">Transitions API</a>. The Transitions API is a suite of protocols that determine how custom transitions should behave, there are quite a few protocols in the suite however only two of them are of interest to us:</p>
<ol>
<li><a href="https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate?ref=williamboles.com"><code>UIViewControllerTransitioningDelegate</code></a> - is a set of methods that vend objects used to manage a fixed-length or interactive transition between view controllers. Every view-controller has a <code>transitioningDelegate</code> which has a type of <code>UIViewControllerTransitioningDelegate</code>. When a transaction is about to happen, iOS asks the transitioning-delegate for an animator to use. If the <code>transitioningDelegate</code> is nil or the necessary <code>UIViewControllerTransitioningDelegate</code> method hasn&apos;t been implemented, then iOS will fall back to using the default animation for that type of transition.</li>
<li><a href="https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning?ref=williamboles.com"><code>UIViewControllerAnimatedTransitioning</code></a> - is a set of methods for implementing the animations for a custom view controller transition. A class that conforms to <code>UIViewControllerAnimatedTransitioning</code> is known as the animator. An animator controls the duration of the transition and allows for manipulating the <em>from-view-controller&apos;s view</em> and <em>to-view-controller&apos;s view</em> on the transition canvas.</li>
</ol>
<blockquote>
<p>I briefly covered the Transitions API above - if you want to read more, I&apos;d recommend this <a href="https://www.raywenderlich.com/322-custom-uiviewcontroller-transitions-getting-started?ref=williamboles.com">post</a>.</p>
</blockquote>
<p>Let&apos;s create an animator to handle presenting the alert:</p>
<pre><code class="swift">class CustomAlertPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning // 1 {

    // MARK: - UIViewControllerAnimatedTransitioning

    // 2
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -&gt; TimeInterval {
        return 0.2
    }

    // 3
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toViewController = transitionContext.viewController(forKey: .to),
            let snapshot = toViewController.view.snapshotView(afterScreenUpdates: true)
            else {
                return
        }

        let containerView = transitionContext.containerView
        let finalFrame = transitionContext.finalFrame(for: toViewController)

        snapshot.frame = finalFrame
        snapshot.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
        snapshot.alpha = 0.0

        containerView.addSubview(toViewController.view)
        containerView.addSubview(snapshot)
        toViewController.view.isHidden = true

        let duration = transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration, animations: {
            snapshot.alpha = 1.0
            snapshot.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
        }) { _ in
            toViewController.view.isHidden = false
            snapshot.removeFromSuperview()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}</code></pre>
<p><code>CustomAlertPresentAnimationController</code> can at first glance look a little intimidating so let&apos;s break it down:</p>
<ol>
<li><code>CustomAlertPresentAnimationController</code> conforms to <code>UIViewControllerAnimatedTransitioning</code> and needs to be a subclass of <code>NSObject</code> as <code>UIViewControllerAnimatedTransitioning</code> extends <code>NSObjectProtocol</code>.</li>
<li>In <code>transitionDuration(using:)</code>  the duration of the animation is specified as <code>0.2</code> seconds - we&apos;ve copied the timing of an <code>UIAlertController</code> alert here.</li>
<li><code>UIAlertController</code> instances when animated onto the screen, gradually fade in with a slight bounce effect before settling to its actual size. In <code>animateTransition(using:)</code> we use the <code>UIViewControllerContextTransitioning</code> instance (<code>transitionContext</code>) to get the offscreen view of the  <em>to-view-controller</em> (i.e. the <code>AlertContainerViewController</code> instance holding our alert). We take a snapshot (screenshot) of the <code>AlertContainerViewController</code> instances view, add that snapshot to the animator&apos;s container view (think of this as a temporary transaction view that is present during the animation) and animate the snapshot on the screen to mimic a <code>UIAlertController</code> animation. Taking a snapshot means we avoid having to deal with any constraint issues that may arise from manipulating the actual <code>AlertContainerViewController</code> instances view. Once the animation is finished, we remove the snapshot from the view hierarchy and reveal the <code>AlertContainerViewController</code> instance&apos;s view occupying the same position.</li>
</ol>
<p>As <code>HoldingViewController</code> is the view-controller that presents the <code>AlertContainerViewController</code> instance, it needs to  conform to <code>UIViewControllerTransitioningDelegate</code>:</p>
<pre><code class="swift">class HoldingViewController: UIViewController, UIViewControllerTransitioningDelegate {
    //Omitted properties

    init(withViewController viewController: UIViewController) {
        //Omitted start of method

        // 1
        containerViewController.modalPresentationStyle = .custom

        // 2
        containerViewController.transitioningDelegate = self
    }

    //Omitted other methods

    // MARK: - UIViewControllerTransitioningDelegate

    // 3
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -&gt; UIViewControllerAnimatedTransitioning? {
        return CustomAlertPresentAnimationController()
    }
}</code></pre>
<p>With the changes to <code>HoldingViewController</code> we:</p>
<ol>
<li>Set the <code>modalPresentationStyle</code> to <code>custom</code> as we will be providing the modal presentation animation.</li>
<li>Set this <code>HoldingViewController</code> instance as the <code>transitioningDelegate</code> of the <code>AlertContainerViewController</code> instance.</li>
<li>Return an instance of <code>CustomAlertPresentAnimationController</code> when presenting an <code>AlertContainerViewController</code> instance.</li>
</ol>
<p>If you add in the above code changes to your project and run it, you would now see your alert being animated onto the screen in the same way as an <code>UIAlertController</code> alert.</p>
<p>So we just need to add in a semi-transparent background, and our alert presentation will be complete:</p>
<pre><code class="swift">class AlertContainerViewController: UIViewController {
    //Omitted properties and other methods

    override func viewDidLoad() {
        super.viewDidLoad()

        let backgroundView = UIView()
        backgroundView.backgroundColor = UIColor.darkGray.withAlphaComponent(0.75)
        backgroundView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(backgroundView)

        //Omitted child view-controller setup

        NSLayoutConstraint.activate([
            //Omitted child view-controller layout setup

            backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            backgroundView.topAnchor.constraint(equalTo: view.topAnchor),
            backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}</code></pre>
<p>Now our alert presentation is complete; let&apos;s turn our attention to dismissing that alert.</p>
<h3 id="dismissingalerts">Dismissing alerts</h3>
<p>Just like presentation, dismissal will happen inside the <code>AlertPresenter</code>:</p>
<pre><code class="swift">class AlertPresenter {
    //Omitted properties and other methods

    func dismissAlert(_ viewController: UIViewController) {
        // 1
        guard let alertWindow = alertWindows.first(where: { $0.viewController == viewController } )  else {
            return
        }

        os_log(.info, &quot;Alert being dismissed&quot;)

        // 2
        alertWindow.dismiss { [weak self] in
            // 3
            self?.alertWindows.remove(alertWindow)
        }
    }
}</code></pre>
<p>With the changes to <code>AlertPresenter</code> we:</p>
<ol>
<li>Find the window that is showing the alert.</li>
<li>Call <code>dismiss(completion:)</code> on that window to begin the dismissal process.</li>
<li>Remove the window from <code>alertWindows</code> once the dismissal has completed.</li>
</ol>
<blockquote>
<p>There are a few ways we could have implemented the dismissal logic. In the end, I decided to require all custom alerts to call <code>dismissAlert(_:)</code> when they are ready to be dismissed. This direct calling approach has symmetry with how the alert is presented and is very simple.</p>
</blockquote>
<p>If you try to run the above method you will get an error because <code>AlertWindow</code> doesn&apos;t yet have an <code>viewController</code> property or <code>dismissAlert(_:)</code> method so let&apos;s add them in:</p>
<pre><code class="swift">class AlertWindow: UIWindow {
    // 1
    var viewController: UIViewController {
        return holdingViewController.containerViewController.childViewController
    }

    //Omitted other methods and properties

    // MARK: - Dismiss

    func dismiss(completion: @escaping (() -&gt; Void)) {
        // 2
        holdingViewController.dismissAlert { [weak self] in
            // 3
            self?.resignKeyAndHide()
            completion()
        }
    }

    // MARK: - Resign

    private func resignKeyAndHide() {
        resignKey()
        isHidden = true
    }
}</code></pre>
<p>With the above changes to <code>AlertWindow</code> we:</p>
<ol>
<li>Added a new <code>viewController</code> property that gets the alert being displayed and returns it.</li>
<li>Pass the dismissal command onto the <code>HoldingViewController</code> instance.</li>
<li>Resign the window and hide it once the dismissal has completed.</li>
</ol>
<blockquote>
<p>Normally I don&apos;t like the level of <a href="https://refactoring.guru/smells/message-chains?ref=williamboles.com">method chaining</a> shown inside the <code>viewController</code> property. However, I feel comfortable doing it here as <code>HoldingViewController</code> and <code>AlertContainerViewController</code> are private implementation details of <code>AlertWindow</code>.</p>
</blockquote>
<p>Let&apos;s add a <code>dismissAlert(_:)</code> method into <code>HoldingViewController</code>:</p>
<pre><code class="swift">class HoldingViewController: UIViewController, UIViewControllerTransitioningDelegate {
    //Omitted properties and other methods

    // MARK: - Dismiss

    func dismissAlert(completion: @escaping (() -&gt; Void)) {
        containerViewController.dismiss(animated: true, completion: {
            completion()
        })
    }
}</code></pre>
<p>With the above change, we call the standard UIKit <a href="https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss?ref=williamboles.com">dismiss(animation:completion:)</a> on the <code>containerViewController</code> and trigger the completion closure when that dismiss action completes.</p>
<p>If you add in the above code changes to your project and run it by calling <code>AlertPresenter.shared.dismiss(completion:)</code> when your alert&apos;s dismissal button is pressed then your alert should be dismissed. However, it will still be using the standard modal dismissal animation. Just like with presenting, dismissing will need an animator:</p>
<pre><code class="swift">class CustomAlertDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

    // MARK: - UIViewControllerAnimatedTransitioning

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -&gt; TimeInterval {
        return 0.2
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: .from),
            let snapshot = fromViewController.view.snapshotView(afterScreenUpdates: true)
            else {
                return
        }

        let containerView = transitionContext.containerView
        let finalFrame = transitionContext.finalFrame(for: fromViewController)

        snapshot.frame = finalFrame

        containerView.addSubview(snapshot)
        fromViewController.view.isHidden = true

        let duration = transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration, animations: {
            snapshot.alpha = 0.0
        }) { _ in
            snapshot.removeFromSuperview()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}</code></pre>
<p><code>CustomAlertDismissAnimationController</code> is mostly the opposite of <code>CustomAlertPresentAnimationController</code> but without the bounce effect.</p>
<p>Now <code>HoldingViewController</code> just has to return an <code>CustomAlertDismissAnimationController</code> instance at the appropriate moment:</p>
<pre><code class="swift">class HoldingViewController: UIViewController, UIViewControllerTransitioningDelegate {
    //Omitted properties and other methods

    func animationController(forDismissed dismissed: UIViewController) -&gt; UIViewControllerAnimatedTransitioning? {
        return CustomAlertDismissAnimationController()
    }
}</code></pre>
<p>And that completes our custom alert presenter.</p>
<h3 id>&#x1F37E;&#x1F483;&#x1F3FB;&#x1F57A;&#x1F3FB;</h3>
<h3 id="feelinghopeful">Feeling hopeful? &#x1F31E;</h3>
<p>To recap, we built a simple custom alert presentation system that takes an alert view-controller, places it in the centre of a semi-transparent container and presents it in a dedicated window, all the while doing so with the same feel as presenting a <code>UIAlertController</code> instance.</p>
<p>I spent quite a long time toying with the idea of having <code>AlertPresenter</code> accept view-models rather than view-controllers (with the view-model then being transformed into view-controllers inside the presenter). However, the view-model solution always ended up feeling very confused - was <code>AlertPresenter</code> part of the app&apos;s infrastructure or part of the app&apos;s UI. By using view-models <code>AlertPresenter</code> had to be both &#x1F627;. Each time someone created a new type of alert, <code>AlertPresenter</code> would need to be modified to know about the new view-model - breaking the <a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle?ref=williamboles.com">open/closed principle</a> and opening the door to unexpected consequences rippling through the app (you can see this approach on this <a href="https://github.com/wibosco/CustomAlert-Example/tree/using_viewmodels_and_enums_to_present_alerts?ref=williamboles.com">branch</a>) via the <code>AlertPresenter</code>. By moving the alert view-controller creation from <code>AlertPresenter</code> to the component that wanted to use <code>AlertPresenter</code>, I was able to give <code>AlertPresenter</code> a clear purpose: <code>AlertPresenter</code> takes a view-controller and presents it in the same manner as an <code>UIAlertController</code> alert. This clear division of responsibility between alert creator and alert presenter, I believe, has meant that <code>AlertPresenter</code> is a simple, easy-to-understand component that should very rarely need to be modified.</p>
<p>To see the above code snippets together in a working example, head over to the <a href="https://github.com/wibosco/CustomAlert-Example?ref=williamboles.com">repo</a> and clone the project.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Alert queuing with windows]]></title><description><![CDATA[Being British, queuing is essential to me. I take every opportunity I can get to queue: posting a package ✅, fleeing a burning building ✅, etc. So every time I need to present a sequence of alerts, I come up against the uncomfortable truth that UIAlertController doesn't care about queuing 😔]]></description><link>https://williamboles.com/alert-queuing-with-windows/</link><guid isPermaLink="false">5d9b1708b8979800387ddeff</guid><category><![CDATA[ui]]></category><category><![CDATA[alerts]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Wed, 06 Nov 2019 14:25:57 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/10/Queuing-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2019/10/Queuing-1.png" alt="Alert queuing with windows"><p>Being <a href="https://en.wikipedia.org/wiki/British_people?ref=williamboles.com">British</a>, queuing is essential to me. I take every opportunity I can get to queue: posting a package &#x2705;, paying for my groceries &#x2705;, fleeing a burning building &#x2705;, etc. So every time I need to present a sequence of alerts, I come up against the uncomfortable truth that <a href="https://developer.apple.com/documentation/uikit/uialertcontroller?ref=williamboles.com"><code>UIAlertController</code></a> doesn&apos;t care as much about queuing as I do &#x1F614;.</p>
<p><img src="https://williamboles.com/content/images/2020/05/people_queuing.jpg" alt="Alert queuing with windows" loading="lazy"></p>
<p>This article is a look at how <code>UIAlertController</code> can be made a little more British through embracing a strong queuing etiquette.</p>
<h3 id="letsgetqueuing">Let&apos;s get queuing &#x1F1EC;&#x1F1E7;</h3>
<p>A <a href="https://en.wikipedia.org/wiki/Queue_(abstract_data_type)?ref=williamboles.com">queue</a> is a <em>first-in, first-out</em> data structure so that the oldest element is the first to be removed. Swift doesn&apos;t come with a built in queue type so lets build one:</p>
<pre><code class="swift">struct Queue&lt;Element&gt; {
    private var elements = [Element]()

    // MARK: - Operations

    mutating func enqueue(_ element: Element) {
        elements.append(element)
    }

    mutating func dequeue() -&gt; Element? {
        guard !elements.isEmpty else {
            return nil
        }

        return elements.removeFirst()
    }
}</code></pre>
<p>The above class is a relatively trivial generic queue implementation that allows only two operations - enqueue and dequeue.</p>
<p>When queuing alerts, it&apos;s important to ensure that all alerts are queued on the same instance. To achieve this, we need a specialised singleton class that will control access to the queue and be the central component for all alert presentation logic:</p>
<pre><code class="swift">class AlertPresenter {
    private var alertQueue = Queue&lt;UIAlertController&gt;()

    static let shared = AlertPresenter()

    // MARK: - Present

    func enqueueAlertForPresentation(_ alertController: UIAlertController) {
        alertQueue.enqueue(alertController)

        //TODO: Present
    }
}</code></pre>
<p>In the above class, <code>AlertPresenter</code> holds a private queue specialised for <code>UIAlertController</code> elements and a method for enqueuing alerts. But as of yet, no way to present the queued alert. Before we can implement a presentation method, let&apos;s look at how alerts are normally presented:</p>
<pre><code class="swift">let alertController = ...

present(alertController, animated: true, completion: nil)</code></pre>
<p><code>UIAlertController</code> (which is a subclass of <code>UIViewController</code>) is presented modally like any other view-controller by calling <code>present(_:animated:completion:)</code> on the presenting view-controller. A consequence of queuing alerts in <code>AlertPresenter</code> is that it brokes the usual alert presentation flow. As <code>AlertPresenter</code> isn&apos;t a subclass of <code>UIViewController</code> we can&apos;t use it directly for presenting. Instead, we need to get a view-controller to present from. As well as requiring a view-controller to be injected into <code>AlertPresenter</code>, our indirect alert presentation also exacerbates an existing subtle issue with presenting alerts - simultaneous navigation events (presentation/dismissal) occurring at the same time resulting in one of those events being cancelled and the following error being generated:</p>
<p><img src="https://williamboles.com/content/images/2019/11/screenshot_of_navigation_collision.png" alt="Alert queuing with windows" loading="lazy"></p>
<blockquote>
<p>Without <code>AlertPresenter</code> being involved, the alert would be presented directly from the view-controller, allowing that view-controller to prevent other navigation events occurring until after the alert is dismissed. However, by queueing the alert, the view-controller has no way of knowing when it will be shown so no way of knowing when it should or shouldn&apos;t prevent other navigation events.</p>
</blockquote>
<p>While it&apos;s possible for <code>AlertPresenter</code> to query the topmost view-controller and determine if it is in the process of presenting or being dismissed, doing so requires logic that gets messy quickly &#x1F922;. Instead of having to accommodate events happening on the navigation stack that <code>AlertPresenter</code> doesn&apos;t control, we can raise <code>AlertPresenter</code> above all navigation concerns by using a dedicated <a href="https://developer.apple.com/documentation/uikit/uiwindow?ref=williamboles.com"><code>UIWindow</code></a> instance (with a dedicated view-controller) to present the queued alerts from. As the navigation stack in one window is independent of any other window&apos;s navigation stack, an alert can be presented on its window at the same time as a view controller is being pushed on its window without navigation collisions &#x1F973;.</p>
<p>Before delving into how to use a dedicated window to present alerts, let&apos;s get to know windows better.</p>
<blockquote>
<p>If you are comfortable with how <code>UIWindow</code> works, feel free to <a href="#usingwindows">skip ahead</a>.</p>
</blockquote>
<h3 id="gettingtoknowwindows">Getting to know windows &#x1F4AD;</h3>
<p><code>UIWindow</code> is a subclass of <code>UIView</code> that acts as the container for an app&apos;s visible content - it is the top of the view hierarchy. All views that are displayed to the user need to be added to a window. An app can have multiple windows, but only windows that are <em>visible</em> can have their content displayed to the user - by default windows are not visible. Multiple windows can be visible at once. Each window&apos;s UI stack is independent of other window&apos;s UI stacks. Where a window is displayed in regards to other visible windows is controlled by setting that window&apos;s <code>windowLevel</code> property. The higher the <code>windowLevel</code> the <em>nearer</em> to the user that window is. <code>UIWindow</code> has three default levels:</p>
<ol>
<li><code>.normal</code></li>
<li><code>.statusBar</code></li>
<li><code>.alert</code></li>
</ol>
<p>With <code>.alert</code> &gt; <code>.statusBar</code> &gt; <code>.normal</code>. If a more fine-grain level of control is needed it&apos;s possible to use a custom level:</p>
<pre><code class="swift">window.windowLevel = .normal + 25</code></pre>
<blockquote>
<p>As of iOS 13: <code>.alert</code> has a raw value of 2000, <code>.statusBar</code> has a raw value of 1000 and <code>.normal</code> has a raw value of 0.</p>
</blockquote>
<p>Where two or more windows have the same level, their ordering is determined by the order they were made visible in - the last window made visible is <em>nearest</em> one to the user.</p>
<p>It&apos;s unusual to directly add subviews to a window instead each window should have a root view-controller who&apos;s view is used as the window&apos;s initial subview.</p>
<p>As well as displaying content, <code>UIWindow</code> is also responsible for forwarding any <a href="https://developer.apple.com/documentation/uikit/uievent/eventtype?ref=williamboles.com">events</a> (touch, motion, remote-control or press) to interested parties in it&apos;s <a href="https://swiftrocks.com/understanding-the-ios-responder-chain.html?ref=williamboles.com">responder chain</a>. While all touch events are forwarded to the responder chain of the window that the event occurred on, events that are outside of the app&apos;s UI such as motion events or keyboard entry, are forwarded to the <em>key</em> window. Only one window can be <em>key</em> at any one given time (which window is <em>key</em> can change throughout the lifetime of the app).</p>
<p>An iOS app needs at least one window, a reference to this window can be found in the app delegate:</p>
<pre><code class="swift">@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    //Omitted methods
}</code></pre>
<p>If you&apos;re using storyboards to layout your interface, then most of the work of setting up this window is happening under the hood. When the app is launched a <code>UIWindow</code> instance is created that fills the screen. This window is then assigned to the <code>window</code> property (declared in the <code>UIApplicationDelegate</code> protocol), configured with the view-controller declared as the <em>storyboard entry point</em> from the project&apos;s main storyboard as it&apos;s <code>rootViewController</code> and finally the window is made <strong>key and visible</strong>.</p>
<p>If you are not using storyboards you can better see this setup:</p>
<pre><code class="swift">@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    // MARK - AppLifecycle

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {
        window = UIWindow()
        window?.rootViewController = YourViewController()
        window?.makeKeyAndVisible()

        return true
    }
}</code></pre>
<h3 id="usingwindows">Using windows</h3>
<p>As this new window will only display alerts, let&apos;s subclass <code>UIWindow</code> for this single purpose:</p>
<pre><code class="swift">class AlertWindow: UIWindow {
    // MARK: - Init

    init(withAlertController alertController: UIAlertController) {
        super.init(frame: UIScreen.main.bounds)

        rootViewController = alertController

        windowLevel = .alert
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError(&quot;Unavailable&quot;)
    }

    // MARK: - Present

    func present() {
        makeKeyAndVisible()
    }
}</code></pre>
<p>In the above <code>AlertWindow</code> class an alert is passed into the init&apos;er and set to the window&apos;s <code>rootViewController</code>, the window is then configured to have a <code>.alert</code> window level - this will put it above the app&apos;s main window (which by default has a <code>.normal</code> window level).</p>
<p>If you create an instance of <code>AlertWindow</code> and make it visible, you will notice that it&apos;s not being presented with an animation - it just appears which feels weird. A window&apos;s root view-controller can not be animated on-screen so to keep the alert&apos;s animation we need that alert to be presented from an intermediate view-controller which can be the window&apos;s root view-controller:</p>
<pre><code class="swift">class HoldingViewController: UIViewController {
    private let alertController: UIAlertController

    // MARK: - Init

    init(withAlertController alertController: UIAlertController) {
        self.alertController = alertController
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError(&quot;init(coder:) has not been implemented&quot;)
    }

    // MARK: - ViewLifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .clear
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        present(alertController, animated: true, completion: nil)
    }
}</code></pre>
<p>The above <code>HoldingViewController</code> class only has one responsibility - presenting an alert once <code>viewDidAppear(_:)</code> has been called.</p>
<blockquote>
<p>Trying to present an alert earlier will cause an animation error due to the <code>HoldingViewController</code> instance having not finished its own &quot;animation&quot; on screen. While (at the time of writing - iOS 13) this doesn&apos;t seem to affect the actual presentation of the alert, waiting for <code>HoldingViewController</code> to be presented before attempting another presentation ensures that the error isn&apos;t produced.</p>
</blockquote>
<p>Lets use <code>HoldingViewController</code>:</p>
<pre><code class="swift">class AlertWindow: UIWindow {

    // MARK: - Init

    init(withAlertController alertController: UIAlertController) {
        super.init(frame: UIScreen.main.bounds)

        rootViewController = HoldingViewController(withAlertController: alertController)

        windowLevel = .alert
    }

    //Omitted methods
}</code></pre>
<p>In the above class, instead of the <code>UIAlertController</code> instance being set directly as the <code>rootViewController</code> it is used to create an <code>HoldingViewController</code> instance which is set as the window&apos;s <code>rootViewController</code>.</p>
<p>Each <code>AlertWindow</code> instance is intended to be single-use - to present one alert. This single-use nature allows for its mere presence to be the determining factor in whether a queued alert should be presented or not:</p>
<pre><code class="swift">class AlertPresenter {
    //Omitted properties

    private var alertWindow: AlertWindow?

    // MARK: - Present

    func enqueueAlertForPresentation(_ alertController: UIAlertController) {
        alertQueue.enqueue(alertController)

        showNextAlertIfPresent()
    }

    private func showNextAlertIfPresent() {
        guard alertWindow == nil,
            let alertController = alertQueue.dequeue() else {
                return
        }

        let alertWindow = AlertWindow(withAlertController: alertController)
        alertWindow.present()

        self.alertWindow = alertWindow
    }
}</code></pre>
<p>With the above changes, <code>alertWindow</code> holds a reference to the window that is being used to present the alert. Inside <code>showNextAlertIfPresent()</code> if <code>alertWindow</code> is <code>nil</code> and there is a queued alert then a new <code>AlertWindow</code> instance is created, presented and assigned to <code>alertWindow</code>. Making the window <em>key and visible</em> sets off the chain of activity that results in the alert being animated on screen.</p>
<p>The example so far while functional is limited - it can present only one alert. <code>AlertPresenter</code> needs to be informed when an alert has been dismissed so it can move onto the next alert.</p>
<p>It turns out knowing when an alert has been dismissed is one of the trickier things to know about in an iOS app &#x1F615;. I had to work through a number of different solutions before I got somewhere I was happy:</p>
<ol>
<li>My first attempt involved subclassing <code>UIAlertController</code> and overriding <code>viewDidDisappear(_:)</code> with a call to <code>AlertPresenter</code> - everyone would then use this subclass instead of <code>UIAlertController</code> directly. However, this approach is explicitly warned against in the documentation for <code>UIAlertController</code>:</li>
</ol>
<p><img src="https://williamboles.com/content/images/2019/10/screenshot_of_uialercontroller_documentation.png" alt="Alert queuing with windows" loading="lazy"></p>
<p>&#x1F44E;</p>
<ol start="2">
<li>
<p>My second attempt involved requiring each developer to explicitly call <code>AlertPresenter</code> from inside each <code>UIAlertAction</code> closure. However, this approach puts too much of a burden on each developer to remember to include those calls in each and every action. A missed call from any action closure (that was substantially triggered) would cause the queue to be <em>jammed</em> from that alert until the app was killed. This is too easy a requirement to forget when writing or reviewing &#x1F44E;.</p>
</li>
<li>
<p>My third attempt involved using a view model to abstract the creation of <code>UIAlertController</code> instances to a <a href="https://en.wikipedia.org/wiki/Factory_method_pattern?ref=williamboles.com">factory method</a> where a call to <code>AlertPresenter</code> could then be injected into each <code>UIAlertAction</code> closure (during the conversion from view model to <code>UIAlertController</code> instance). However, this approach would require a lot of custom code to be written, maintained and tested - using <code>UIAlertController</code> directly would avoid all effort &#x1F44E;.</p>
</li>
</ol>
<p>Tricky, tricky &#x1F914;.</p>
<p>Instead of thinking about how to get <code>UIAlertController</code> to tell us about what was happening to it, I decided to start thinking about what impact <code>UIAlertController</code> had on its surroundings. <code>UIViewController</code> has a great suite of methods for when it will appear and disappear - these appearance events can tell us what has happened to the presented alert. When an alert is dismissed, it calls <code>func dismiss(animated:completion:)</code> on the view-controller it was presented from. We can hook into this behaviour to inform <code>AlertWindow</code> about the dismissal:</p>
<pre><code class="swift">protocol HoldingDelegate: class {
    func viewController(_ viewController: HoldingViewController, didDismissAlert alertController: UIAlertController)
}

class HoldingViewController: UIViewController {
    //Omitted properties

    weak var delegate: HoldingDelegate?

    // Omitted methods

    override func dismiss(animated flag: Bool, completion: (() -&gt; Void)? = nil) {
        //Called when a UIAlertController instance is dismissed
        super.dismiss(animated: flag) {
            completion?()

            self.delegate?.viewController(self, didDismissAlert: self.alertController)
        }
    }
}</code></pre>
<h5 id>&#x1F44D;</h5>
<blockquote>
<p>You may be thinking: &quot;Why is <code>func dismiss(animated:completion:)</code> being called on a view-controller that isn&apos;t being dismissed instead of <code>viewDidAppear(_:)</code> as it is actually reappearing&quot;?. The same thought occurred to me, and this mismatch between expectations vs reality left me a little uncomfortable &#x1F612;. However, in the end, I decided that being able to use <code>UIAlertController</code> without having to care about the queue outweighed my discomfort. If Apple changes this to better match the behaviour of other <code>UIViewController</code> instances, modifying this solution to handle both cases will be trivial.</p>
</blockquote>
<p>When an alert has been dismissed, the presenting <code>AlertWindow</code> instance has served its purpose and can itself be dismissed:</p>
<pre><code class="swift">class AlertWindow: UIWindow, HoldingDelegate {
      //Omitted properties

      // MARK: - Init

      init(withAlertController alertController: UIAlertController) {
          super.init(frame: UIScreen.main.bounds)

          let holdingViewController = HoldingViewController(withAlertController: alertController)
          holdingViewController.delegate = self

          rootViewController = holdingViewController

          windowLevel = .alert
      }

      //Omitted other methods

      // MARK: - HoldingDelegate

      func viewController(_ viewController: HoldingViewController, didDismissAlert alertController: UIAlertController) {
          resignKeyAndHide()
      }

      // MARK: - Resign

      private func resignKeyAndHide() {
          resignKey()
          isHidden = true
      }

}</code></pre>
<p>All that is left to do is inform <code>AlertPresenter</code> that the alert has been dismissed, again the <a href="https://en.wikipedia.org/wiki/Delegation_pattern?ref=williamboles.com">delegation pattern</a> can be used here:</p>
<pre><code class="swift">protocol AlertWindowDelegate: class {
    func alertWindow(_ alertWindow: AlertWindow, didDismissAlert alertController: UIAlertController)
}

class AlertWindow: UIWindow, HoldingDelegate {
    weak var delegate: AlertWindowDelegate?

    //Omitted properties and methods

    func viewController(_ viewController: HoldingViewController, didDismissAlert alertController: UIAlertController) {
        resignKeyAndHide()
        delegate?.alertWindow(self, didDismissAlert: alertController)
    }

    //Omitted methods
}</code></pre>
<p><code>AlertPresenter</code> then just needs to present the next alert (if present):</p>
<pre><code class="swift">class AlertPresenter: AlertWindowDelegate {
    //Omitted properties and methods

    private func showNextAlertIfPresent() {
        guard alertWindow == nil,
            let alertController = alertQueue.dequeue() else {
                return
        }

        let alertWindow = AlertWindow(withAlertController: alertController)
        alertWindow.delegate = self

        alertWindow.present()

        self.alertWindow = alertWindow
    }

    // MARK: - AlertWindowDelegate

    func alertWindow(_ alertWindow: AlertWindow, didDismissAlert alertController: UIAlertController) {
        self.alertWindow = nil
        showNextAlertIfPresent()
    }
}</code></pre>
<h3 id="allqueuedout">All queued out &#x1F37E;</h3>
<p>Congratulations on having made it to the end of an article about queuing. You&apos;ve shown a level of patience that even many Brits would struggle to achieve.</p>
<p>To recap, in this article we implemented a minimally intrusive alert queuing mechanism that allows us to continue using <code>UIAlertController</code> without having to ask the creators of those alerts to do anything more complicated than calling <code>AlertPresenter.shared.enqueueAlertForPresentation(_:)</code>.</p>
<p>To see the above code snippets together in a working example, head over to the <a href="https://github.com/wibosco/AlertQueue-Example?ref=williamboles.com">repo</a> and clone the project.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Discovering what's out there]]></title><description><![CDATA[Many of us feel nervous when meeting a group of people for the first time. What are the dynamics of the group, what are the in-jokes, will I find common ground with someone - are just a few questions that can plague you]]></description><link>https://williamboles.com/discovering-whats-out-there-with-ssdp/</link><guid isPermaLink="false">5c615dc32c37de00bf993f51</guid><category><![CDATA[networking]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Mon, 08 Apr 2019 10:59:35 GMT</pubDate><media:content url="https://williamboles.com/content/images/2020/05/friends_chatting-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2020/05/friends_chatting-1.jpg" alt="Discovering what&apos;s out there"><p>Many of us feel nervous when meeting a group of people for the first time. What are the dynamics of the group, what are the in-jokes, will I find common ground with someone - are just a few questions that can plague you. A lot of your hard work at suppressing your crippling social self-doubt can unravel with a shaky introduction or misplaced comment. You may even find yourself wondering:</p>
<p><strong>&quot;Are they laughing with me because I&apos;m funny or at me because of the overly pointy brogues that I&apos;m wearing?&quot;</strong></p>
<blockquote>
<p>Of course, that&apos;s nonsense. Pointy brogues are the height of fashion &#x1F45E;.</p>
</blockquote>
<p>It&apos;s not just you who can be anxious about new introductions; your app can be too. Computers are notoriously fickle about who they speak to and how they expect to be addressed. A barrage of errors await any app that breaks these social norms - it can be enough to make your app want to stay quietly in the corner rather than jump in and face potential rejection.</p>
<p>Thankfully, this social anxiety can be eased if your app already knows someone who understands the dynamics of the group - someone cool and suave like <a href="https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol?ref=williamboles.com">SSDP</a>.</p>
<p><img src="https://williamboles.com/content/images/2020/05/friends_chatting.jpg" alt="Discovering what&apos;s out there" loading="lazy"></p>
<blockquote>
<p>This post will gradually build up to a working example however if you want to jump ahead then head on over to the <a href="https://github.com/wibosco/SSDPDiscovery-Example?ref=williamboles.com">completed example</a> and take a look at <code>SSDPSearchSessionConfiguration</code>, <code>SSDPSearchSession</code>, <code>UDPSocketController</code>, <code>UDPSocket</code>, <code>SSDPServiceParser</code> and <code>SSDPService</code> to see how things end up.</p>
</blockquote>
<blockquote>
<p>If you are already familiar with SSDP, feel free to skip the next section and head directly to <a href="#gettingtoknowwhosthere">building a working Swift solution</a>.</p>
</blockquote>
<h3 id="gettingtoknowssdp">Getting to know SSDP</h3>
<p>SSDP (Simple Service Discovery Protocol) is a discovery protocol used to determine what services are available on a network. It is defined as part of the <a href="https://en.wikipedia.org/wiki/Universal_Plug_and_Play?ref=williamboles.com">UPnP</a> spec. SSDP is a <a href="https://en.wikipedia.org/wiki/Zero-configuration_networking?ref=williamboles.com">zero-configuration networking protocol</a> designed to allow nodes to be added and removed from a network without any involvement from a central service such as <a href="https://en.wikipedia.org/wiki/Domain_Name_System?ref=williamboles.com">DNS</a> or by assigning static IP addresses to specific nodes. This decentralised, dynamic approach is possible because SSDP uses <a href="https://en.wikipedia.org/wiki/User_Datagram_Protocol?ref=williamboles.com">UDP</a> as it&apos;s underlying transportation protocol which allows for <a href="https://en.wikipedia.org/wiki/Multicast?ref=williamboles.com">multicast</a> communication.</p>
<p><img src="https://williamboles.com/content/images/2019/04/multicast_routing_diagram.png" alt="Discovering what&apos;s out there" loading="lazy"></p>
<p>Multicasting allows a node to transmit one message onto the network and for that message to be forwarded on to all interested nodes on the network without the sender node having to know what other network nodes are available (forwarding happens at the IP routing level). SSDP takes advantage of this forwarding functionality to allow any node to ask other nodes if they support a particular service, or conversely for a node which offers services to tell other nodes about those services.</p>
<blockquote>
<p>For multicast messages, <a href="https://www.iana.org/?ref=williamboles.com">IANA</a> has reserved the IPv4 address <code>239.255.255.250</code> and port <code>1900</code> for SSDP.</p>
</blockquote>
<p>SSDP messages conform to the header field format of <a href="https://tools.ietf.org/html/rfc2068?ref=williamboles.com">HTTP 1.1</a>. It&apos;s important to note that SSDP does not allow any message to contain a body; everything is shared via those header fields.</p>
<p>An SSDP node is either a <code>root device</code> or <code>control point</code>. A <code>root device</code> offers one or more SSDP services; a <code>control point</code> uses SSDP services.</p>
<p>When a <code>root device</code> is responding to a discovery message it does so by sending a <a href="https://en.wikipedia.org/wiki/Unicast?ref=williamboles.com">unicast</a> (to a single specific node) message directly to the <code>control point</code>.</p>
<p><img src="https://williamboles.com/content/images/2019/04/unicast_routing_diagram.png" alt="Discovering what&apos;s out there" loading="lazy"></p>
<blockquote>
<p>That&apos;s a lot of information to take in &#x1F625;. If it doesn&apos;t all make sense that&apos;s ok, most of the details will come up again later, and when seen in context, those details are easier to understand.</p>
</blockquote>
<p>SSDP messages fall into two categories:</p>
<ol>
<li>Discovery</li>
<li>Advertisement</li>
</ol>
<p>This post is <strong>mainly</strong> concerned with Discovery messages but to ensure that we have the fullest possible understanding of SSDP I&apos;ll cover Advertisement messages as well (feel free to skip the Advertisement section).</p>
<h6 id="discovery">Discovery</h6>
<p>Discovery involves two message types:</p>
<ol>
<li>Request</li>
<li>Response</li>
</ol>
<p>A request message is when a <code>control point</code> transmits an <code>M-SEARCH</code> message onto the network looking for a particular service (or as we shall see soon, any service) e.g.</p>
<pre><code class="swift">M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: &quot;ssdp:discover&quot;
MX: 1
ST: urn:dial-multiscreen-org:service:dial:1</code></pre>
<p>An <code>M-SEARCH</code> request message contains:</p>
<ul>
<li>The host and port (<code>HOST</code>) the message will be sent to. Typically an <code>M-Search</code> message is multicast (like the example above) but can be unicast.</li>
<li>The message type (<code>MAN</code>), for an <code>M-Search</code> this is always <code>ssdp:discover</code>.</li>
<li>The search target (<code>ST</code>) of the service the search request is attempting to discover.</li>
<li>The maximum wait response time (<code>MX</code>) in seconds that a <code>root device</code> can take before responding. The <code>MX</code> field is an attempt to overcome a scaling issue implicit with SSDP. SSDP is a <em>chatty</em> protocol, in a network with a significant number of nodes that host SSDP services, sending an <code>M-SEARCH</code> message could result in accidentally <a href="https://www.cloudflare.com/learning/ddos/ssdp-ddos-attack/?ref=williamboles.com">DDOS-ing</a> the questing node due to too many services responding at once. The <code>MX</code> field instructs the <code>root device</code> to wait a random time between 0 and <code>MX</code> before attempting to respond - this should allow the responses to be spaced out enough to ease the processing strain on the <code>control point</code>. The <code>MX</code> value should be between 1 and 5. Even with the <code>MX</code> workaround, SSDP is recommended only to be used in home or small office networks.</li>
</ul>
<p>A <code>root device</code> <em>should only</em> respond with services that match the search target field of the the request e.g.</p>
<pre><code class="swift">HTTP/1.1 200 OK
CACHE-CONTROL: max-age=3600
ST: urn:dial-multiscreen-org:service:dial:1
USN: uuid:0175c106-5400-10f8-802d-b0a7374360b7::urn:dial-multiscreen-org:service:dial:1
EXT:
SERVER: Roku UPnP/1.0 MiniUPnPd/1.4
LOCATION: http://192.168.1.104:8060/dial/dd.xml</code></pre>
<p>An <code>M-Search</code> response message contains:</p>
<ul>
<li>The cache-control (<code>CACHE-CONTROL</code>) value to determine for how long the message is valid.</li>
<li>The search target (<code>ST</code>) of the service that is responding. <code>ST</code> should be common across all devices of this type.</li>
<li>The unique service name (<code>USN</code>) to identify the service.</li>
<li>The server system information (<code>SERVER</code>) value providing information in the following format: <code>[OS-Name] UPnP/[Version] [Product-Name]/[Product-Version]</code>.</li>
<li>The location URL (<code>LOCATION</code>) to allow the <code>control point</code> to gain more information about this service.</li>
</ul>
<blockquote>
<p>The <code>EXT</code> field is required for backwards compatibility with UPnP 1.0 but can otherwise be ignored.</p>
</blockquote>
<h6 id="advertisement">Advertisement</h6>
<p>An advertisement message is when a <code>root device</code> shares the status of each service it offers with the other nodes on the network.</p>
<p>There are three types of advertisement:</p>
<ol>
<li>Alive</li>
<li>Update</li>
<li>ByeBye</li>
</ol>
<p>An <code>alive</code> message allows interested devices to know that a service is available. An <code>alive</code> message is a multicast <code>NOTIFY</code> message e.g.</p>
<pre><code class="swift">NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=3600
NT: urn:dial-multiscreen-org:service:dial:1
NTS: ssdp:alive
LOCATION: http://192.168.1.104:8060/dial/dd.xml
USN: uuid:0175c106-5400-10f8-802d-b0a7374360b7::urn:dial-multiscreen-org:service:dial:1</code></pre>
<p>An <code>alive</code> message contains:</p>
<ul>
<li>The host and port (<code>HOST</code>) the message will be sent to.</li>
<li>The cache-control (<code>CACHE-CONTROL</code>) value to determine for how long the message is valid.</li>
<li>The notification type (<code>NT</code>) that defines the service it offers (the equivalent of <code>ST</code> in an <code>M-Search</code> message).</li>
<li>The notification subtype (<code>NTS</code>), for an <code>alive</code> message this will always be <code>ssdp:alive</code> (the equivalent of <code>MAN</code> in an <code>M-Search</code> message).</li>
<li>The location URL (<code>LOCATION</code>) to allow a receiving <code>control point</code> to gain more information about this service.</li>
<li>The unique service name (<code>USN</code>) to identify the service.</li>
</ul>
<p>An <code>update</code> message allows changes to a service to be shared. An <code>update</code> message is also a multicast <code>NOTIFY</code> message like the <code>alive</code> message e.g.</p>
<pre><code class="swift">NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
NT: urn:dial-multiscreen-org:service:dial:1
NTS: ssdp:update
LOCATION: http://192.168.1.160:8060/dial/dd.xml
USN: uuid:0175c106-5400-10f8-802d-b0a7374360b7::urn:dial-multiscreen-org:service:dial:1</code></pre>
<p>An <code>update</code> message has the same header fields as an <code>alive</code> message with only the <code>NTS</code> value differing between them.</p>
<p>A <code>byebye</code> message allows any interested nodes to know when a service is about to be removed from the network. A <code>byebye</code> message should be sent for each valid (non-expired) <code>alive</code> message that was sent. A <code>byebye</code> message is a multicast <code>NOTIFY</code> message e.g.</p>
<pre><code class="swift">NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
NT: urn:dial-multiscreen-org:service:dial:1
NTS: ssdp:byebye
USN: uuid:0175c106-5400-10f8-802d-b0a7374360b7::urn:dial-multiscreen-org:service:dial:1</code></pre>
<p>Again a <code>byebye</code> message has a very similar structure to both an <code>alive</code> and <code>update</code> message only omitting the <code>LOCATION</code> header field and having a different <code>NTS</code> value.</p>
<p>Now that we have an understanding of what SSDP is let&apos;s get back to the solution.</p>
<h3 id="gettingtoknowwhosthere">Getting to know who&apos;s there &#x1F52D;</h3>
<p>As SSDP communication is built on top of UDP, it isn&apos;t possible to use <a href="https://developer.apple.com/documentation/foundation/urlsession?ref=williamboles.com"><code>URLSession</code></a> to send an <code>M-Search</code> message instead we need to read and write from a socket manually. I was tempted to dive in and write a simple socket layer to handle this, but the more I read up about the various network setups I would have to support, the more writing a socket layer started to look like an unforgiving task. Instead, I decided to introduce a 3rd-party dependency into the project: <a href="https://github.com/IBM-Swift/BlueSocket?ref=williamboles.com">BlueSocket</a>. <code>BlueSocket</code> will handle the nitty-gritty socket communication and free us up to focus on sending and receiving SSDP messages.</p>
<blockquote>
<p>In the <a href="https://github.com/wibosco/SSDPDiscovery-Example?ref=williamboles.com">example project</a>, <a href="https://cocoapods.org/?ref=williamboles.com">CocoaPods</a> is used to manage this dependency.</p>
</blockquote>
<blockquote>
<p>At the time of writing (April, 2019) the new(ish) <a href="https://developer.apple.com/documentation/network?ref=williamboles.com"><code>Network</code> framework</a> doesn&apos;t support UDP multicasting.</p>
</blockquote>
<blockquote>
<p>Since I wrote this article, Apple has released iOS 14, which contained a range of user <a href="https://www.wired.com/story/ios-14-privacy-security-features/?ref=williamboles.com">privacy improvements</a>. One area of focus was around how an app accesses the local network. There are two changes that affect the below example; firstly, before accessing the local network, iOS will request permission from the user as it does for, e.g. accessing the camera and secondly, any app that wants to multicast on a network must also have the <code>com.apple.developer.networking.multicast</code> entitlement enabled - this entitlement needs to be requested from Apple. Without both the user permission and the entitlement, the below example won&apos;t be able to send multicast messages from a device (everything will still work on the simulator) - see this <a href="https://developer.apple.com/news/?id=0oi77447&amp;ref=williamboles.com">note</a> for more details.</p>
</blockquote>
<p>Before we get started, let&apos;s look at what we are going to build:</p>
<p><img src="https://williamboles.com/content/images/2020/06/ssdp_discovery_class_diagram.jpg" alt="Discovering what&apos;s out there" loading="lazy"></p>
<ul>
<li><code>SSDPSearchSessionConfiguration</code> represents the configuration of an SSDP search session.</li>
<li><code>SSDPSearchSession</code> is responsible for triggering a search request, parsing and filtering received responses and passing back those services that match our search terms to whoever requested the search. A typical search session will write multiple search requests to the socket to try and overcome the unreliable nature of UDP.</li>
<li><code>SSDPSearchSessionDelegate</code> is a protocol that informs the delegator of any found services, any errors encounter and ultimately that the search session has ended.</li>
<li><code>SocketControllerFactory</code> is responsible for producing sockets controllers configured to use sockets that use a specific transport protocol.</li>
<li><code>UDPSocketController</code> is a wrapper around a <code>Socket</code> instance configured to use UDP. <code>UDPSocketController</code> helps to hide the <em>messiness</em> of socket communication.</li>
<li><code>UDPSocketControllerDelegate</code> a protocol that informs the delegator of any responses received and any errors encountered.</li>
<li><code>SocketFactory</code> is responsible for producing fully configured sockets that are ready to be written/read to/from.</li>
<li><code>UDPSocket</code> a <code>Socket</code> instance configured to use UDP. As <code>Socket</code> is from the <code>BlueSocket</code> dependency, <code>Socket</code> is wrapped in a protocol to limit the spread of <code>BlueSocket</code> in the project.</li>
<li><code>SSDPServiceParser</code> is responsible for parsing an SSDP response into <code>Service</code> instance. If the SSDP response is <em>invalid</em>, no service is created.</li>
<li><code>SSDPService</code> represents an SSDP service.</li>
</ul>
<blockquote>
<p>Don&apos;t worry if that doesn&apos;t all make sense yet, we will look into each class in greater depth below.</p>
</blockquote>
<p>Now that we know where we are going let&apos;s start at the bottom with <code>UDPSocket</code> and work our way up.</p>
<p>Ideally when adding a 3rd-party dependency to our projects we want to hide that dependency behind a <a href="https://en.wikipedia.org/wiki/Facade_pattern?ref=williamboles.com">facade</a> which should make removing that dependency easier by limiting it&apos;s spread in the project. In Swift we can use a protocol and an extension to wrap that 3rd-party dependency inside our facade:</p>
<pre><code class="swift">enum UDPSocketError: Error {
    case addressCreationFailure
    case writeError(underlayingError: Error)
    case readError(underlayingError: Error)
}

protocol UDPSocketProtocol {
    func write(_ string: String, to host: String, on port: UInt) throws
    func readDatagram(into data: inout Data) throws
    func close()
}

extension Socket: UDPSocketProtocol {
    // 1
    func write(_ string: String, to host: String, on port: UInt) throws {
        guard let signature = self.signature, signature.socketType == .datagram, signature.proto == .udp else {
            fatalError(&quot;Only UDP sockets can use this method&quot;)
        }

        guard let address = Socket.createAddress(for: host, on: Int32(port)) else {
            throw(UDPSocketError.addressCreationFailure)
        }
        do {
            try write(from: string, to: address)
        } catch {
            throw(UDPSocketError.writeError(underlayingError: error))
        }
    }

    // 2
    func readDatagram(into data: inout Data) throws {
        guard let signature = self.signature, signature.socketType == .datagram, signature.proto == .udp else {
            fatalError(&quot;Only UDP sockets can use this method&quot;)
        }

        do {
            let (_,_) = try readDatagram(into: &amp;data)
        } catch {
            throw(UDPSocketError.readError(underlayingError: error))
        }
    }
}</code></pre>
<p><code>Socket</code> does a lot more than what we need for our SSDP solution. <code>UDPSocketProtocol</code> reduces the range of tasks that can be performed on a <code>Socket</code> instance to only the three that we need: <code>write</code>, <code>read</code> and <code>close</code>. As well as reducing scope, <code>UDPSocketProtocol</code> also simplifies the interface of <code>Socket</code> by wrapping the <code>Socket</code> methods in our own methods.</p>
<ol>
<li>The <code>Socket</code> type in <code>BlueSocket</code> can be used to create sockets with different configurations. In <code>UDPSocketProtocol</code>, we only want to cater for sockets configured that using UDP to send <a href="https://en.wikipedia.org/wiki/Datagram?ref=williamboles.com">datagram</a> messages, so in <code>write(_:to:on:)</code> a check is made to determine if this <code>Socket</code> instance has been configured for UDP and datagram - if it hasn&apos;t then a fatal error is thrown as this is a developer error. The <code>write(from:to:)</code> method on <code>Socket</code> takes an <code>Address</code> type which is defined in <code>Socket</code> - as mentioned above we want to limit the spread of <code>BlueSocket</code> in the project, so the <code>write</code> method defined in <code>UDPSocketProtocol</code> doesn&apos;t use <code>Address</code> but rather sticks with two <code>String</code> parameters <code>host</code> and <code>port</code>. In the extension, these parameters are used to create an <code>Address</code> instance which is then forwarded to the <code>write(from:to:)</code> method on <code>Socket</code>. If for some reason an <code>Address</code> instance can&apos;t be created, an exception is thrown. It&apos;s also possible for an exception to be thrown when writing to the socket, if an exception is thrown that exception is caught, wrapped inside an <code>UDPSocketError</code> case before a new exception is thrown.</li>
<li>Just like with <code>write(_:to:on:)</code>, in <code>readDatagram(into:)</code> a check is made to ensure that we are dealing with a socket configured to UDP and datagram. The <code>readDatagram(into:)</code> method on <code>Socket</code> returns the number of bytes read as an <code>Int</code> and the address that sent those bytes as an <code>Address</code>. We aren&apos;t interested in either of those return values, so the <code>readDatagram(into:)</code> defined in <code>SocketProtocol</code> doesn&apos;t have a return type - our <code>readDatagram(into:)</code> just eats those details. If so some reason when reading from the socket an exception is thrown, this exception is caught, wrapped inside an <code>UDPSocketError</code> case before a new exception is thrown.</li>
</ol>
<blockquote>
<p>The <code>close()</code> on <code>Socket</code> fits our needs perfectly so we don&apos;t need to wrap that method.</p>
</blockquote>
<p><code>UDPSocketProtocol</code> does a good job of hiding <code>Socket</code> but it can be called on with all configurations of <code>Socket</code> when we really only intend for it be called on UDP sockets sending datagram messages so lets make making that socket configuration as easy as possible:</p>
<pre><code class="swift">extension Socket {
    static func createUDPSocket() throws -&gt; UDPSocketProtocol {
        return try Socket.create(type: .datagram, proto: .udp)
    }
}</code></pre>
<blockquote>
<p>You will need to import the <code>Socket</code> module into any class that uses <code>Socket</code>.</p>
</blockquote>
<p>Now that we have <code>UDPSocketProtocol</code>, lets build <code>Socket</code> instances wrapped in it:</p>
<pre><code class="swift">protocol SocketFactoryProtocol {
    func createUDPSocket() -&gt; UDPSocketProtocol?
}

class SocketFactory: SocketFactoryProtocol {

    // MARK: - UDP

    func createUDPSocket() -&gt; UDPSocketProtocol? {
        guard let socket = try? Socket.createUDPSocket() else {
            return nil
        }

        return socket
    }
}</code></pre>
<p>The above factory, attempts to create a UDP socket, if successful that <code>Socket</code> instance is returned hidden behind the <code>UDPSocketProtocol</code> protocol; if unsuccessful, <code>nil</code> is returned.</p>
<blockquote>
<p><code>SocketFactory</code> conforms to <code>SocketFactoryProtocol</code> so that when it is injected into <code>UDPSocketController</code> - during unit testing we can replace the actual factory with a mock factory that conforms to that protocol. I use this technique throughout this example, so any other protocols that are named after a concrete type are for this purpose.</p>
</blockquote>
<p>Now that we have our socket, let&apos;s try writing to it:</p>
<pre><code class="swift">protocol UDPSocketControllerProtocol: AnyObject {
    var state: UDPSocketControllerState { get }

    func write(message: String)
    func close()
}

// 1
enum UDPSocketControllerState {
    case ready
    case active
    case closed

    var isReady: Bool {
        self == .ready
    }

    var isActive: Bool {
        self == .active
    }

    var isClosed: Bool {
        self == .closed
    }
}

class UDPSocketController: UDPSocketControllerProtocol {
    private(set) var state: UDPSocketControllerState = .ready

    private let socket: UDPSocketProtocol

    private let host: String
    private let port: UInt

    private let callbackQueue: OperationQueue
    private let socketWriterQueue = DispatchQueue(label: &quot;com.williamboles.udpsocket.writer.queue&quot;,  attributes: .concurrent)

    // MARK: - Init

    // 2
    init?(host: String, port: UInt, socketFactory: SocketFactoryProtocol) {
        guard let socket = socketFactory.createUDPSocket() else {
            return nil
        }

        self.host = host
        self.port = port
        self.socket = socket
    }

    // MARK: - Write

    // 3
    func write(message: String) {
        guard !state.isClosed else {
            os_log(.info, &quot;Attempting to write to a closed socket&quot;)
            return
        }

        state = .active

        write(message: message, on: socketWriterQueue)
    }

    // 4
    private func write(message: String, on queue: DispatchQueue) {
        queue.async {
            do {
                try self.socket.write(message, to: self.host, on: self.port)
            } catch {
                self.closeAndReportError(error)
            }
        }
    }

    // MARK: - Close

    private func closeAndReportError(_ error: Error) {
        close()
        os_log(.info, &quot;Error received: \r%{public}@&quot;, error)
        //TODO: Implement reporting error
    }

    func close() {
        state = .closed
        socket.close()
    }
}</code></pre>
<p>Let&apos;s look at what we did above:</p>
<ol>
<li>A socket can be in 3 states: <code>ready</code>, <code>active</code> and <code>closed</code> - <code>UDPSocketControllerState</code> represents these states. As <code>UDPSocketController</code> interacts with its socket, it will move through states. This will ensure that we don&apos;t try to write to a socket that can&apos;t be written to.</li>
<li>To write a message to a socket, we need the host and port on the remote machine that we want to communicate with. The init&apos;er of <code>UDPSocketController</code> accepts this host and port. It also accepts a socket factory instance which is used to create a UDP socket.</li>
<li>When writing to a socket, we only want to allow communication with a socket that hasn&apos;t been closed so the first action of the <code>write(message:)</code> is to check if <code>state</code> is in a closed state.</li>
<li>Writing to a socket can be a time-consuming operation, so the operation is pushed onto a background queue: <code>socketWriterQueue</code>. As a write operation can throw an exception, we wrap that operation in a <code>do...catch</code> whereupon catching an exception the socket is closed, and any error reported (we will implement this shortly).</li>
</ol>
<p><code>UDPSocketController</code> is designed to be tied to one host and port. If that host and port needs to be changed, then a new instance of <code>UDPSocketController</code> needs to be created.</p>
<p>Let&apos;s build a factory to produce socket controllers:</p>
<pre><code class="swift">protocol SocketControllerFactoryProtocol {
    func createUDPSocketController(host: String, port: UInt, socketFactory: SocketFactoryProtocol) -&gt; UDPSocketControllerProtocol?
}

class SocketControllerFactory: SocketControllerFactoryProtocol {

    // MARK: - UDP

    func createUDPSocketController(host: String, port: UInt, socketFactory: SocketFactoryProtocol) -&gt; UDPSocketControllerProtocol? {
        UDPSocketController(host: host, port: port, socketFactory: socketFactory)
    }
}</code></pre>
<p>Now that we have a socket controller and a socket to write to let&apos;s look at how to give it an <code>M-Search</code> message to send.</p>
<p><strong>3</strong> pieces of information are required to send an <code>M-Search</code> message:</p>
<ol>
<li>Search target (<code>ST</code>).</li>
<li>The IP address and port (<code>HOST</code>).</li>
<li>Maximum wait response time (<code>MX</code>).</li>
</ol>
<p>These pieces of information can be represented as:</p>
<pre><code class="swift">struct SSDPSearchSessionConfiguration {
    let searchTarget: String
    let host: String
    let port: UInt
    let maximumWaitResponseTime: TimeInterval

    // MARK: - Init

    init(searchTarget: String, host: String, port: UInt, maximumWaitResponseTime: TimeInterval) {
        assert(maximumWaitResponseTime &gt;= 1 &amp;&amp; maximumWaitResponseTime <= 1 5 5, "maximumwaitresponsetime should be between and (inclusive)") self.searchtarget="searchTarget" self.host="host" self.port="port" self.maximumwaitresponsetime="maximumWaitResponseTime" } }< code></=></code></pre>
<blockquote>
<p><code>SSDPSearchSessionConfiguration</code> has a custom initialiser to allow for an assertion to be performed on the value of <code>maximumWaitResponseTime</code> (which needs to be between 1 and 5 inclusive). I believe having to manually write this custom initialiser is a price worth paying to allow for quicker feedback during development if an invalid value is passed in (by causing the app to crash).</p>
</blockquote>
<p>While it&apos;s possible to send unicast <code>M-Search</code> messages, here we are only interested in sending multicast messages so to make things easier let&apos;s add a small factory method to return a preconfigured multicast <code>SSDPSearchSessionConfiguration</code> instance:</p>
<pre><code class="swift">extension SSDPSearchSessionConfiguration {

    static func createMulticastConfiguration(forSearchTarget searchTarget: String, maximumWaitResponseTime: TimeInterval = 3) -&gt; SSDPSearchSessionConfiguration {
        let configuration = SSDPSearchSessionConfiguration(searchTarget: searchTarget, host: &quot;239.255.255.250&quot;, port: 1900, maximumWaitResponseTime: maximumWaitResponseTime)

        return configuration
    }
}</code></pre>
<blockquote>
<p>Setting the <code>searchTarget</code> to <code>ssdp:all</code> should cause all <code>root devices</code> to respond with their full range of SSDP services.</p>
</blockquote>
<p>With this configuration it is possible to build a simple searcher class to drive any <code>UDPSocketController</code> instance:</p>
<pre><code class="swift">protocol SSDPSearchSessionProtocol {
    func startSearch()
    func stopSearch()
}

class SSDPSearchSession: SSDPSearchSessionProtocol {
    private let socketController: UDPSocketControllerProtocol
    private let configuration: SSDPSearchSessionConfiguration

    // 1
    private lazy var mSearchMessage = {
        // Each line must end in `\r\n`
        return &quot;M-SEARCH * HTTP/1.1\r\n&quot; +
            &quot;HOST: \(configuration.host):\(configuration.port)\r\n&quot; +
            &quot;MAN: \&quot;ssdp:discover\&quot;\r\n&quot; +
            &quot;ST: \(configuration.searchTarget)\r\n&quot; +
            &quot;MX: \(Int(configuration.maximumWaitResponseTime))\r\n&quot; +
        &quot;\r\n&quot;
    }()

    // MARK: - Init

    // 2
    init?(configuration: SSDPSearchSessionConfiguration, socketControllerFactory: SocketControllerFactoryProtocol = SocketControllerFactory()) {
        guard let socketController = socketControllerFactory.createUDPSocketController(host: configuration.host, port: configuration.port, socketFactory: SocketFactory()) else {
            return nil
        }
        self.socketController = socketController
        self.configuration = configuration
    }

    deinit {
        stopSearch()
    }

    // MARK: - Search

    // 3
    func startSearch() {
        os_log(.info, &quot;SSDP search session starting&quot;)
        writeMessageToSocket(mSearchMessage)
    }

    func stopSearch() {
        os_log(.info, &quot;SSDP search session stopping&quot;)
        close()
    }

    // MARK: - Close

    // 4
    private func close() {
        if socketController.state.isActive {
            socketController.close()
        }
    }

    // MARK: - Write

    private func writeMessageToSocket(_ message: String) {
        os_log(.info, &quot;Writing to socket: \r%{public}@&quot;, message)
        socketController.write(message: message)
    }
}</code></pre>
<p><code>SSDPSearchSession</code> above:</p>
<ol>
<li>Configures the <code>M-Search</code> message using the <code>SSDPSearchSessionConfiguration</code> instance.</li>
<li>Creates an <code>UDPSocketController</code> instance.</li>
<li>Writes the <code>M-Search</code> message to the socket controller.</li>
<li>Closes the socket controller if it&apos;s active.</li>
</ol>
<blockquote>
<p>With the <code>M-Search</code> message you may have noticed the <code>\r\n</code> character sequence at the end of each line - don&apos;t be tempted to remove this from the <code>M-Search</code> message as the <code>\r\n</code> sequence is part of the protocol spec.</p>
</blockquote>
<p><code>SSDPSearchSession</code> is designed to be a <em>single-use</em> instance - once <code>stopSearch()</code> is called and the socket closed, a new instance of <code>SSDPSearchSession</code> is needed to perform another search.</p>
<blockquote>
<p>Don&apos;t forget to add <code>import os</code> to get the <code>os_log</code> statements to compile.</p>
</blockquote>
<p>Calling <code>startSearch()</code> should cause an <code>M-Search</code> message to be written to the network however as grizzly, well-travelled developers we know that trusting our code to do something without testing it, is a recipe for disappointment &#x1F61E;. To test that this message is being written to the network, we can snoop on our network traffic using <a href="https://en.wikipedia.org/wiki/Tcpdump?ref=williamboles.com"><code>tcpdump</code></a>.</p>
<p>To test on the simulator, open your terminal and run:</p>
<pre><code class="bash">sudo tcpdump -vv -A -s 0 &apos;port 1900 and host 239.255.255.250 and udp&apos;</code></pre>
<blockquote>
<p><code>-vv</code>: verbose output.<br>
<code>-A</code>: print each packet in ASCII.<br>
<code>-s 0</code>: sets the amount of captured data for each frame. Using <code>0</code> sets the amount to the default: 65535 bytes - we explicitly set this here for backwards compatibility with recent older versions of tcpdump.<br>
<code>&apos;port 1900 and host 239.255.255.250 and udp&apos;</code> is using <a href="https://en.wikipedia.org/wiki/Berkeley_Packet_Filter?ref=williamboles.com">Berkeley Packet Filter (BPF)</a> syntax to filter what traffic we see.</p>
</blockquote>
<p>The above command will capture all the UDP traffic using host <code>239.255.255.250</code> on port <code>1900</code> (i.e. SSDP traffic) on your local machine.</p>
<p><img src="https://williamboles.com/content/images/2019/04/tcpdump-terminal-screenshot-m4.png" alt="Discovering what&apos;s out there" loading="lazy"></p>
<blockquote>
<p>If you are using a VPN, you may need to disable it to see anything in the console.</p>
</blockquote>
<p>Testing on a device is slightly more difficult. We need to tell <code>tcpdump</code> which device to snoop on by creating a remote virtual interface using <a href="https://useyourloaf.com/blog/remote-packet-capture-for-ios-devices/?ref=williamboles.com"><code>rvictl</code></a>:</p>
<p>Connect your device and grab its <code>UDID</code>, then open terminal and run:</p>
<pre><code class="bash">rvictl -s {UDID} &amp;&amp; sudo tcpdump -vv -A -s 0 -i rvi0 &apos;port 1900 and host 239.255.255.250 and udp&apos;</code></pre>
<blockquote>
<p>Replacing <code>{UDID}</code> with the <code>UDID</code> of the device.</p>
</blockquote>
<p>You should see similar traffic to what you would if testing on the simulator.</p>
<blockquote>
<p>Run <code>rvictl -x {UDID}</code> to stop the remote virtual interface and <code>Ctrl-C</code> to kill <code>tcpdump</code>.</p>
</blockquote>
<p>Now that we have confirmation that <code>M-Search</code> messages are being sent, let&apos;s build the functionality to parse any responses we may receive. First <code>UDPSocketController</code> needs to read anything sent to the socket:</p>
<pre><code class="swift">class UDPSocketController: UDPSocketControllerProtocol {
    //Omitted other properties

    // 1
    private let socketListeningQueue = DispatchQueue(label: &quot;com.williamboles.udpsocket.listen.queue&quot;,  attributes: .concurrent)

    //Omitted other methods

    // MARK: - Write

    func write(message: String) {
        //Omitted code

        // 2
        let shouldStartListening = state.isReady
        state = .active

        if shouldStartListening {
           startListening(on: socketListeningQueue)
        }

        //Omitted code
    }

    // MARK: - Listen

    // 3
    private func startListening(on queue: DispatchQueue) {
        queue.async {
            do {
                repeat {
                    var data = Data()
                    try self.socket.readDatagram(into: &amp;data) //blocking call
                    self.reportResponseReceived(data)
                } while self.state.isActive
            } catch {
                if self.state.isActive { // ignore any errors for non-active sockets
                    self.closeAndReportError(error)
                }
            }
        }
    }

    private func reportResponseReceived(_ data: Data) {
        os_log(.info, &quot;Response received: \r%{public}@&quot;, response)
        //TODO: Implement reporting response received
    }

    //Omitted other methods
}</code></pre>
<p>With the above changes, <code>UDPSocketController</code> is now able to read from its socket.</p>
<ol>
<li>Reading from a <code>BlueSocket</code> socket configured to read datagram messages using UDP is a blocking call - any thread that <code>readDatagram(into:)</code> is called on will be blocked at that line until there is data there to be read. To avoid the app from freezing, reading from the socket must be pushed off the caller queue and onto a background queue: <code>socketWriterQueue</code>.</li>
<li>We only need to configure the socket to listen once - on the first write.</li>
<li>Once a response is received, that response is converted into a string and (for the moment) logged. Finally, if the controller is still listening for responses, the socket is polled again. As a read operation can throw an exception, we wrap that operation in a <code>do...catch</code> whereupon catching an exception the socket is closed, and any error reported (we will implement this shortly). An interesting point to note is that closing a socket that is being polled will throw an exception. So when an exception is thrown during polling, we only care about that exception if the session is listening.</li>
</ol>
<p>If you have devices on your network that support SSDP, you should start to see responses in the Console when running the above code. However, if you don&apos;t, it&apos;s possible to fake a response using <a href="https://en.wikipedia.org/wiki/Netcat?ref=williamboles.com">netcat</a>. You will need to extract the host and port from the <code>M-Search</code> request via <code>tcpdump</code> and run the following command:</p>
<pre><code class="bash">echo &quot;HTTP/1.1 200 OK\r\nCache-Control: max-age=3600\r\nST: urn:dial-multiscreen-org:service:dial:1\r\nUSN: uuid:0175c106-5400-10f8-802d-b0a7374360b7::urn:dial-multiscreen-org:service:dial:1\r\nExt: \r\nServer: Roku UPnP/1.0 MiniUPnPd/1.4\r\nLOCATION: http://192.168.1.104:8060/\r\n\r\n&quot; | nc -u {host} {port}</code></pre>
<blockquote>
<p>Replacing {host} {port} with the extracted values.</p>
</blockquote>
<p>The above command will send a response pretending to be a <a href="https://www.roku.com/en-gb/?ref=williamboles.com">Roku</a> set-top box.</p>
<p>Now that it is possible to read and write from a socket, lets pass any responses (and any errors) out of our socket controller:</p>
<pre><code class="swift">protocol UDPSocketControllerDelegate: AnyObject {
    func controller(_ controller: UDPSocketControllerProtocol, didReceiveResponse response: Data)
    func controller(_ controller: UDPSocketControllerProtocol, didEncounterError error: Error)
}
protocol UDPSocketControllerProtocol: AnyObject {
    //Omitted other properties
    var delegate: UDPSocketControllerDelegate? { get set }

    //Omitted methods
}

class UDPSocketController: UDPSocketControllerProtocol {
    //Omitted other properties

    weak var delegate: UDPSocketControllerDelegate?

    private let callbackQueue: OperationQueue

    init?(host: String, port: UInt, socketFactory: SocketFactoryProtocol, callbackQueue: OperationQueue) {
        //Omitted rest of method

        self.callbackQueue = callbackQueue
    }

    //Omitted other methods

    private func reportResponseReceived(_ data: Data) {
        callbackQueue.addOperation {
           self.delegate?.controller(self, didReceiveResponse: data)
        }
    }

    private func closeAndReportError(_ error: Error) {
        close()
        callbackQueue.addOperation {
            self.delegate?.controller(self, didEncounterError: error)
        }
    }
}</code></pre>
<p>With the above changes, <code>SSDPSearchSession</code> can now add itself as the delegate of <code>UDPSocketControllerDelegate</code>. To make the communication via that delegate more predictable when creating that socket, the thread that the communication will happen on is passed in.</p>
<p>Let&apos;s update the <code>SocketControllerFactory</code> to support the <code>callbackQueue</code>:</p>
<pre><code class="swift">protocol SocketControllerFactoryProtocol {
    func createUDPSocketController(host: String, port: UInt, socketFactory: SocketFactoryProtocol, callbackQueue: OperationQueue) -&gt; UDPSocketControllerProtocol?
}

class SocketControllerFactory: SocketControllerFactoryProtocol {

    // MARK: - UDP

    func createUDPSocketController(host: String, port: UInt, socketFactory: SocketFactoryProtocol, callbackQueue: OperationQueue) -&gt; UDPSocketControllerProtocol? {
        UDPSocketController(host: host, port: port, socketFactory: socketFactory, callbackQueue: callbackQueue)
    }
}</code></pre>
<p>Now, let&apos;s update <code>SSDPSearchSession</code> to be the delegate of <code>UDPSocketControllerDelegate</code>:</p>
<pre><code class="swift">class SSDPSearchSession: SSDPSearchSessionProtocol, UDPSocketControllerDelegate {
    //Omitted properties

    init?(configuration: SSDPSearchSessionConfiguration, socketControllerFactory: SocketControllerFactoryProtocol = SocketControllerFactory()) {
        guard let socketController = socketControllerFactory.createUDPSocketController(host: configuration.host, port: configuration.port, socketFactory: SocketFactory(), callbackQueue: .main) else {
            return nil
        }

        //Omitted other assignments

        self.socketController.delegate = self
    }

    //Omitted other methods

    // MARK: - UDPSocketControllerDelegate

    func controller(_ controller: UDPSocketControllerProtocol, didReceiveResponse response: Data) {
        os_log(.info, &quot;Received response: \r%{public}@&quot;, response)
        //TODO: Implement
    }

    func controller(_ controller: UDPSocketControllerProtocol, didEncounterError error: Error) {
        os_log(.info, &quot;Encountered socket error: \r%{public}@&quot;, error.localizedDescription)
        close()
        //TODO: Implement
    }
}</code></pre>
<p><code>SSDPSearchSession</code> can now receive responses from its <code>UDPSocketController</code>, let&apos;s turn those responses into something useful:</p>
<pre><code class="swift">struct SSDPService {
    let cacheControl: Date
    let location: URL
    let server: String
    let searchTarget: String
    let uniqueServiceName: String
    let otherHeaders: [String: String]
}</code></pre>
<p>An <code>M-Search</code> response has mandatory and optional/custom header fields. In <code>SSDPService</code>, the mandatory header fields are mapped to named properties, and the optional/custom header fields are mapped to the <code>otherHeaders</code> property. Each optional/custom header field is represented as a dictionary with the field name as the dictionary key and field&apos;s value as dictionary value.</p>
<p>To get an <code>SSDPService</code> instance, it needs to be parsed:</p>
<pre><code class="swift">private enum SSDPServiceResponseKey: String {
    case cacheControl = &quot;CACHE-CONTROL&quot;
    case location = &quot;LOCATION&quot;
    case server = &quot;SERVER&quot;
    case searchTarget = &quot;ST&quot;
    case uniqueServiceName = &quot;USN&quot;
}

protocol SSDPServiceParserProtocol {
    func parse(_ data: Data) -&gt; SSDPService?
}

class SSDPServiceParser: SSDPServiceParserProtocol {
    private let dateFactory: DateFactoryProtocol

    // Init

    init(dateFactory: DateFactoryProtocol =  DateFactory()) {
        self.dateFactory = dateFactory
    }

    // MARK: - Parse

    func parse(_ data: Data) -&gt; SSDPService? {
        guard let responseString = String(data: data, encoding: .utf8) else {
            return nil
        }

        os_log(.info, &quot;Received SSDP response: \r%{public}@&quot;, responseString)

        // 1
        var responseDict = parseResponseIntoDictionary(responseString)

        // 2
        guard let cacheControl = parseCacheControl(responseDict[SSDPServiceResponseKey.cacheControl.rawValue]),
            let location = parseLocation(responseDict[SSDPServiceResponseKey.location.rawValue]),
            let server = responseDict[SSDPServiceResponseKey.server.rawValue],
            let searchTarget = responseDict[SSDPServiceResponseKey.searchTarget.rawValue],
            let uniqueServiceName = responseDict[SSDPServiceResponseKey.uniqueServiceName.rawValue] else {
                return nil
        }

        // 3
        responseDict.removeValue(forKey: SSDPServiceResponseKey.cacheControl.rawValue)
        responseDict.removeValue(forKey: SSDPServiceResponseKey.location.rawValue)
        responseDict.removeValue(forKey: SSDPServiceResponseKey.server.rawValue)
        responseDict.removeValue(forKey: SSDPServiceResponseKey.searchTarget.rawValue)
        responseDict.removeValue(forKey: SSDPServiceResponseKey.uniqueServiceName.rawValue)

        // 4
        return SSDPService(cacheControl: cacheControl, location: location, server: server, searchTarget: searchTarget, uniqueServiceName: uniqueServiceName, otherHeaders: responseDict)
    }

    private func parseResponseIntoDictionary(_ response: String) -&gt; [String: String] {
        var elements = [String: String]()
        for element in response.split(separator: &quot;\r\n&quot;) {
            let keyValuePair = element.split(separator: &quot;:&quot;, maxSplits: 1)
            guard keyValuePair.count == 2 else {
                continue
            }

            let key = String(keyValuePair[0]).uppercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
            let value = String(keyValuePair[1]).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)

            elements[key] = value
        }

        return elements
    }

    private func parseCacheControl(_ value: String?) -&gt; Date? {
        guard let cacheControlRange = value?.range(of: &quot;[0-9]+$&quot;, options: .regularExpression),
            let cacheControlString = value?[cacheControlRange],
            let cacheControlTimeInterval = TimeInterval(cacheControlString) else {
                return nil
        }

        let currentDate = dateFactory.currentDate()
        return currentDate.addingTimeInterval(cacheControlTimeInterval)
    }

    private func parseLocation(_ value: String?) -&gt; URL? {
        guard let urlString = value,
            let url = URL(string: urlString) else {
                return nil
        }

        return url
    }
}</code></pre>
<p><code>SSDPServiceParser</code> above takes the string response and attempts to parse it into an <code>SSDPService</code> instance by:</p>
<ol>
<li>Splitting the string into a dictionary using <code>\r\n</code> to determine fields and <code>:</code> to determine key and value pairs.</li>
<li>The response dictionary is checked to ensure that all mandatory fields are present. If any of the mandatory fields are missing, the SSDP response is considered invalid, and <code>nil</code> is returned.</li>
<li>As an SSDP response can contain non-mandatory fields, the mandatory fields are stripped from the response dictionary so leaving the only the non-mandatory fields present.</li>
<li>An <code>SSDPService</code> is created using the mandatory and non-mandatory fields.</li>
</ol>
<blockquote>
<p>I could have combined <code>SSDPService</code> and <code>SSDPServiceParser</code> into the one type with the init&apos;er accepting the string response, but I think having an independent parser makes the code easier to read.</p>
</blockquote>
<p>The <code>DateFactory</code> is used to make unit-testing this parser possible:</p>
<pre><code class="swift">protocol DateFactoryProtocol {
    func currentDate() -&gt; Date
}

class DateFactory: DateFactoryProtocol {

    // MARK: - Current

    func currentDate() -&gt; Date {
        return Date()
    }
}</code></pre>
<p>Let&apos;s update <code>SSDPSearchSession</code> to use <code>SSDPServiceParser</code>:</p>
<pre><code class="swift">class SSDPSearchSession: SSDPSearchSessionProtocol, UDPSocketControllerDelegate {
    //Omitted other properties

    private let parser: SSDPServiceParserProtocol

    // MARK: - Init

    init?(configuration: SSDPSearchSessionConfiguration, socketControllerFactory: SocketControllerFactoryProtocol = SocketControllerFactory(), parser: SSDPServiceParserProtocol = SSDPServiceParser()) {
        //Omitted other assignments
        self.parser = parser
    }

    //Omitted methods

    // MARK: - UDPSocketControllerDelegate

    func controller(_ controller: UDPSocketControllerProtocol, didReceiveResponse response: Data) {
        guard !response.isEmpty,
            let service = parser.parse(response) else {
                return
        }

        os_log(.info, &quot;Received service \r%{public}@&quot;, service)
    }

    //Omitted methods
}</code></pre>
<p>Once you start parsing responses, you will notice that some <code>root devices</code> respond to any <code>M-Search</code> message they receive rather than just those discovery requests that match one of their services. To counter these <em>chatty</em> <code>root devices</code> we need to ensure that the parsed <code>SSDPService</code> instance is the searched-for-service:</p>
<pre><code class="swift">class SSDPSearchSession {
    //Omitted properties &amp; methods

    // MARK: - UDPSocketControllerDelegate

    func controller(_ controller: UDPSocketControllerProtocol, didReceiveResponse response: Data) {
       guard !response.isEmpty,
           let service = parser.parse(response),
           searchedForService(service) else {
               return
       }

       os_log(.info, &quot;Received service \r%{public}@&quot;, service)
    }

    private func searchedForService(_ service: SSDPService) -&gt; Bool {
       return service.searchTarget.contains(configuration.searchTarget) || configuration.searchTarget == &quot;ssdp:all&quot;
    }

    //Omitted methods
}</code></pre>
<blockquote>
<p>If the search target is set to the special <code>ssdp:all</code> value, all services that respond are treated as valid.</p>
</blockquote>
<p><code>SSDPSearchSession</code> is doing good work but isn&apos;t able to share the fruits of its labour with anyone. Let&apos;s add in a delegate to tell interested parties how things are going with the search:</p>
<pre><code class="swift">protocol SSDPSearchSessionDelegate: AnyObject {
    func searchSession(_ searchSession: SSDPSearchSession, didFindService service: SSDPService)
    func searchSession(_ searchSession: SSDPSearchSession, didEncounterError error: SSDPSearchSessionError)
    func searchSessionDidStopSearch(_ searchSession: SSDPSearchSession, foundServices: [SSDPService])
}

enum SSDPSearchSessionError: Error {
    case searchAborted(Error)
}

class SSDPSearchSession: SSDPSearchSessionProtocol, UDPSocketControllerDelegate {
    //Omitted properties

    // 1
    private var servicesFoundDuringSearch = [SSDPService]()

    weak var delegate: SSDPSearchSessionDelegate?

    //Omitted methods

    func stopSearch() {
        os_log(.info, &quot;SSDP search session stopping&quot;)
        close()

        // 2
        delegate?.searchSessionDidStopSearch(self, foundServices:servicesFoundDuringSearch)
    }

    //Omitted methods

    // MARK: - UDPSocketControllerDelegate

    func controller(_ controller: UDPSocketControllerProtocol, didReceiveResponse response: Data) {
        guard !response.isEmpty,
            let service = parser.parse(response),
            searchedForService(service) else {
                return
        }

        os_log(.info, &quot;Received a valid service response&quot;)

        servicesFoundDuringSearch.append(service)

        // 3
        delegate?.searchSession(self, didFindService: service)
    }

    func controller(_ controller: UDPSocketControllerProtocol, didEncounterError error: Error) {
        os_log(.info, &quot;Encountered socket error: \r%{public}@&quot;, error.localizedDescription)

        // 4
        let wrappedError = SSDPSearchSessionError.searchAborted(error)
        delegate?.searchSession(self, didEncounterError: wrappedError)
        close()
    }

    //Omitted methods
}</code></pre>
<p>With the above changes, we now:</p>
<ol>
<li>Store all valid services that have been received during that search session.</li>
<li>Inform the delegate when the search has been stopped, returning all valid services found.</li>
<li>Inform the delegate when a valid service has been found.</li>
<li>Wrap any received error in an <code>SSDPSearchSessionError</code> error and inform the delegate of that error.</li>
</ol>
<blockquote>
<p>It&apos;s interesting to note that <code>searchSession(_:, didFindService:)</code> is called as soon as a valid <code>SSDPService</code> instance is parsed rather than waiting for all services to be parsed. This will allow the app to respond immediately to any found services.</p>
</blockquote>
<blockquote>
<p>An alternative to delegation would have been to pass a closure into <code>startSearch()</code>. In fact, using a closure was my preferred option to begin with. However, after experimenting, I felt that having one closure handling three possible states resulted in code that was very <em>busy</em> and that readability suffered because of this.</p>
</blockquote>
<p>Every <code>M-Search</code> message contains an <code>MX</code> value that represents the maximum time a service can wait before responding. When this time has elapsed, it can be confidently assumed that all services that can respond, have responded. Meaning that <code>MX</code> can be used as a timeout for the search session:</p>
<pre><code class="swift">class SSDPSearchSession: SSDPSearchSessionProtocol, UDPSocketControllerDelegate {
    //Omitted other properties

    private let searchTimeout: TimeInterval

    private var timeoutTimer: Timer?

    // MARK: - Init

    init?(configuration: SSDPSearchSessionConfiguration, socketControllerFactory: SocketControllerFactoryProtocol = SocketControllerFactory(), parser: SSDPServiceParserProtocol = SSDPServiceParser()) {
        //Omitted other assignments

        self.searchTimeout = configuration.maximumWaitResponseTime + 0.1
    }

    //Omitted methods

    // MARK: - Search

    func startSearch() {
        //Omitted code

        timeoutTimer = Timer.scheduledTimer(withTimeInterval: searchTimeout, repeats: false, block: { [weak self] (timer) in
            self?.searchTimedOut()
        })
    }

    private func searchTimedOut() {
        os_log(.info, &quot;SSDP search timed out&quot;)
        stopSearch()
    }

    //Omitted methods

    // MARK: - Close

    private func close() {
        timeoutTimer?.invalidate()
        timeoutTimer = nil

        //Omitted code
    }

    //Omitted methods
}</code></pre>
<p>With the above changes, the search session is ended after <code>maximumWaitResponseTime</code> seconds causing the <code>socket</code> to be closed. The more eagle-eyed reader may have spotted that <code>timeoutTimer</code> has a trigger time that is <code>0.1</code> seconds longer than <code>maximumWaitResponseTime</code> - this is to allow any responses from <code>root devices</code> that waited the full <code>maximumWaitResponseTime</code> seconds before responding to reach the app and be processed before the search session is ended.</p>
<h3 id="somepeoplearehardertotalktothanothers">Some people are harder to talk to than others</h3>
<p>If you have been combining the above code snippets into a working project, you will now be able to search for SSDP services and parse any response received. However, every so often, you may notice that an SSDP service that you know exists on the network does not respond.</p>
<h5 id>&#x1F914;</h5>
<p>As described above, SSDP uses the unreliable UDP transportation protocol (because UDP supports multicasting). UDP is unreliable because there is no acknowledgement if a message made it to its destination. This means there is no way of knowing if a service hasn&apos;t responded because the message was dropped along the way or that the service is no longer available. Unreliability <strong>isn&apos;t</strong> a great characteristic for a discovery service to have. While not foolproof, it is possible to increase the reliability of an SSDP based discovery service by sending multiple <code>M-Search</code> messages over the lifecycle of an <code>SSDPSearchService</code> instance. Sending multiple <code>M-Search</code> messages will increase the chances that at least one message makes it to each <code>root device</code> on the network. For this to be possible, our <code>SSDPSearchService</code> instance must exist longer than the <code>MX</code> value before timing out:</p>
<pre><code class="swift">struct SSDPSearchSessionConfiguration {
    //Omitted properties

    let maximumSearchRequestsBeforeClosing: UInt

    // MARK: - Init

    init(searchTarget: String, host: String, port: UInt, maximumWaitResponseTime: TimeInterval, maximumSearchRequestsBeforeClosing: UInt) {
        //Omitted other assignments

        self.maximumSearchRequestsBeforeClosing = maximumSearchRequestsBeforeClosing
    }
}

extension SSDPSearchSessionConfiguration {

    static func createMulticastConfiguration(forSearchTarget searchTarget: String, maximumWaitResponseTime: TimeInterval = 3, maximumSearchRequestsBeforeClosing: UInt = 3) -&gt; SSDPSearchSessionConfiguration {
        let configuration = SSDPSearchSessionConfiguration(searchTarget: searchTarget, host: &quot;239.255.255.250&quot;, port: 1900, maximumWaitResponseTime: maximumWaitResponseTime, maximumSearchRequestsBeforeClosing: maximumSearchRequestsBeforeClosing)

        return configuration
    }
}</code></pre>
<p><code>maximumSearchRequestsBeforeClosing</code> will control how many <code>M-Search</code> messages are sent before the search session is closed. <code>maximumSearchRequestsBeforeClosing</code> needs to be at <strong>least 1</strong>, or no <code>M-Search</code> will be sent.</p>
<blockquote>
<p>An alternative to using a <em>count</em> property would have been to use a <em>total-duration</em> property. However, the <em>total-duration</em> approach would have required the <em>config-maintainer</em> to ensure that this timeout property was always a multiple of the <code>maximumWaitResponseTime</code>. As having a value which isn&apos;t a multiple would result in the situation where the session was stopped before the last <code>M-Search</code> iteration&apos;s <code>maximumWaitResponseTime</code> had expired - potentially resulting in ignored responses because the <code>root devices</code> waited until the <code>maximumWaitResponseTime</code> value before responding. It&apos;s easy to imagine this mistake happening. By expressing the timeout as the value to multiply <code>maximumWaitResponseTime</code> by, we ensure that this error scenario can never happen.</p>
</blockquote>
<pre><code class="swift">class SSDPSearchSession: SSDPSearchSessionProtocol, UDPSocketControllerDelegate {
    //Omitted properties

    private var searchRequestTimer: Timer?

    //Omitted methods

    // MARK: - Init

    init?(configuration: SSDPSearchSessionConfiguration, socketControllerFactory: SocketControllerFactoryProtocol = SocketControllerFactory(), parser: SSDPServiceParserProtocol = SSDPServiceParser()) {
        //Omitted other assignments

        // 1
        self.searchTimeout = (TimeInterval(configuration.maximumSearchRequestsBeforeClosing) * configuration.maximumWaitResponseTime) + 0.1
    }

    // MARK: - Search

    func startSearch() {
        // 2
        guard configuration.maximumSearchRequestsBeforeClosing &gt; 0 else {
            delegate?.searchSessionDidStopSearch(self, foundServices: servicesFoundDuringSearch)
            return
        }

        os_log(.info, &quot;SSDP search session starting&quot;)
        sendMSearchMessages()

        //Omitted code
    }

    //Omitted methods

    // MARK: - Close

    private func close() {
        //Omitted code

        // 3
        searchRequestTimer?.invalidate()
        searchRequestTimer = nil

        //Omitted code
    }

    // MARK: Write

    private func sendMSearchMessages() {
        let message = mSearchMessage

        // 4
        if configuration.maximumSearchRequestsBeforeClosing &gt; 1 {
            let window = searchTimeout - configuration.maximumWaitResponseTime
            let interval = window / TimeInterval((configuration.maximumSearchRequestsBeforeClosing - 1))

            searchRequestTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: { [weak self] (timer) in
                self?.writeMessageToSocket(message)
            })
        }
        writeMessageToSocket(message)
    }

    //Omitted methods
}</code></pre>
<p>With the above changes, a new <code>M-Search</code> message is sent every <code>maximumWaitResponseTime</code> seconds until the desired number of messages have been sent.</p>
<ol>
<li>Increased the total search time for multiple <code>M-Search</code> message writes.</li>
<li>Check that <code>maximumSearchRequestsBeforeClosing</code> is greater than 0 and return if it isn&apos;t.</li>
<li>Invalidate new search request timer.</li>
<li>Send multiple <code>M-Search</code> messages inside the maximum send window.</li>
</ol>
<p>However, while making <code>SSDPSearchSession</code> a more reliable discovery service, sending multiple <code>M-Search</code> messages creates a new problem. In ideal network conditions, the same SSDP service would respond to each sent <code>M-Search</code> message. If these responses were just blindly passed to the <code>delegate</code>, the <code>SSDPSearchService</code> instance would in effect be <strong>spamming</strong> that <code>delegate</code> with the same service multiple times. Thankfully each service parsed is already stored in the <code>servicesFoundDuringSearch</code> array (to be used when <code>searchSessionDidStopSearch(_:, foundServices:)</code> is called), so to prevent becoming a spammer a check can be made to determine if an <code>SSDPService</code> instance representing the same service has already been passed to the <code>delegate</code>:</p>
<pre><code class="swift">class SSDPSearchSession: SSDPSearchSessionProtocol, UDPSocketControllerDelegate {
    //Omitted properties and methods

    // MARK: - UDPSocketControllerDelegate

    func controller(_ controller: UDPSocketControllerProtocol, didReceiveResponse response: Data) {
        guard !response.isEmpty,
            let service = parser.parse(response),
            searchedForService(service),
            // 1
            !servicesFoundDuringSearch.contains(service) else {
                return
        }

        //Omitted code
    }

    //Omitted methods
}</code></pre>
<p>With the above change:</p>
<ol>
<li>A check is made to see if <code>servicesFoundDuringSearch</code> already contains the service that has been parsed.</li>
</ol>
<p>In order to get the above code to work, <code>SSDPService</code> needs to conform to <a href="https://developer.apple.com/documentation/swift/equatable?ref=williamboles.com"><code>Equatable</code></a>:</p>
<pre><code class="swift">struct SSDPService: Equatable {
    //Omitted properties

    // MARK: - Equatable

    static func == (lhs: SSDPService, rhs: SSDPService) -&gt; Bool {
        return lhs.location == rhs.location &amp;&amp;
            lhs.server == rhs.server &amp;&amp;
            lhs.searchTarget == rhs.searchTarget &amp;&amp;
            lhs.uniqueServiceName == rhs.uniqueServiceName
    }
}</code></pre>
<blockquote>
<p>The above custom equality check excludes <code>cacheControl</code> as this will change with each response because it&apos;s based on the date when the service was parsed.</p>
</blockquote>
<h5 id>&#x1F389;&#x1F389;&#x1F389;</h5>
<p>And that&apos;s everything you need for discovering what SSDP services are available on a network - congratulations.</p>
<h3 id="happytogettoknoweveryone">Happy to get to know everyone &#x1F3AF;</h3>
<p>Social situations can be tricky. It&apos;s easy to think that you don&apos;t have anything of value to add and to allow that thought to leave you alone in the corner. However, with a little bit of effort (and bravery), you can reach out and get to know new people.</p>
<p>This is just as true for your app.</p>
<p>SSDP is a lightweight, widely supported protocol that makes it straightforward to discover services on a network. It has a few gotchas but provided that we treat it with care and don&apos;t 100% trust any root devices to behave as expected, SSDP can be a useful tool to have in the toolbox.</p>
<p>To see the above code snippets together in a working example, head over to the <a href="https://github.com/wibosco/SSDPDiscovery-Example?ref=williamboles.com">repo</a> and clone the project.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Progressive Core Data Migrations]]></title><description><![CDATA[There are very few certainties in app development, but one is that once your app is released it will change in unexpected ways. And no matter how flexible your architecture is, inevitably one of those changes will be a breaking change]]></description><link>https://williamboles.com/progressive-core-data-migration/</link><guid isPermaLink="false">5c3138ad5aff9800bfbe00d8</guid><category><![CDATA[core data]]></category><category><![CDATA[migration]]></category><category><![CDATA[persistence]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Mon, 04 Feb 2019 13:48:20 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/11/beautiful-car-min.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2019/11/beautiful-car-min.jpg" alt="Progressive Core Data Migrations"><p>There are very few certainties in app development, but one is that once your app is released it will change in unexpected ways. And no matter how flexible your architecture is, inevitably one of those changes will be a <a href="https://en.wiktionary.org/wiki/breaking_change?ref=williamboles.com">breaking change</a>. Perhaps the most important breaking changes involve the user&apos;s data. If your app loses or corrupts user data you can expect at least some reputational damage and if the loss is severe enough you can end up doing your competitors marketing for them by turning your users into their users. If you have any doubt about the impact of data loss imagine how you would feel if a game you had been playing was updated and deleted your recently hard earned <em><a href="http://www.thefreedictionary.com/thingamabob?ref=williamboles.com">thingamabob</a></em> - all that time and effort lost through no fault of yours. And that&apos;s just a game, now imagine how your users would feel when your <em>far-more-important</em> app starts losing their data.</p>
<p>In our iOS apps we often store these <em>thingamabobs</em> in <a href="https://developer.apple.com/documentation/coredata?ref=williamboles.com">Core Data</a>. The structure of which is defined by a model/schema - a set of entities with attributes and relationships. To allow changes to be made to this model to meet your apps changing needs, Core Data has a built-in mechanism to migrate data from one model structure to another model structure.</p>
<p>In this post, we are going to build a simple system to manipulate the inbuilt Core Data migration mechanism to make migrations simpler and so reduce the risk of losing your user&apos;s data.</p>
<p><img src="https://williamboles.com/content/images/2019/11/beautiful-car-min-1.jpg" alt="Progressive Core Data Migrations" loading="lazy"></p>
<blockquote>
<p>This post will gradually build up to a working example however if you&apos;re on a tight deadline and/or there is murderous look creeping into your manager&apos;s eyes &#x1F621;, then head on over to the <a href="https://github.com/wibosco/CoreDataMigrationRevised-Example?ref=williamboles.com">completed example</a> and take a look at <code>CoreDataManager</code>, <code>CoreDataMigrator</code>, <code>CoreDataMigrationStep</code> and <code>CoreDataMigrationVersion</code> to see how things end up.</p>
</blockquote>
<h3 id="themigrationprocess">The Migration Process</h3>
<p>As mentioned above, Core Data allows the model to evolve through model versions. Typically a model version&apos;s <em>changeable</em> lifecycle (when it can be changed) is from when that version is created until it&apos;s released as an app update. Once released, a version is effectively &quot;frozen&quot; - any further changes made to that version would result in an app crash upon launch. To change an already released model, you need to create a new version of that model and migrate users from the old version to the new version. Thankfully, Core Data has a builtin migration system.</p>
<p>Migrations can be handled using one of two techniques:</p>
<ol>
<li><a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html?ref=williamboles.com">Lightweight Migration</a> - when Core Data can automatically infer how the migration should happen and creates the mapping model on the fly.</li>
<li><a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmMappingOverview.html?ref=williamboles.com###//apple_ref/doc/uid/TP40004399-CH5-SW1">Standard Migration</a> - when Core Data cannot infer how the migration should happen and so we must write a custom migration by providing a mapping model (<code>xcmappingmodel</code>) and/or a migration policy (<code>NSEntityMigrationPolicy</code>).</li>
</ol>
<p>By default, Core Data will attempt to perform a migration automatically when it detects a mismatch between the model used in the persistent store and the bundle&apos;s <strong>current</strong> model. When this happens, Core Data will first attempt to perform a Standard migration by searching in the app&apos;s bundle for a mapping model that maps from the persistent store model to the <strong>current</strong> bundle model. If a custom mapping model isn&apos;t found, Core Data will then attempt to perform a Lightweight migration. If neither form of migration is possible an exception is thrown.</p>
<blockquote>
<p>If you are using <code>NSPersistentContainer</code>, Lightweight migrations are enabled by default, however if you are still directly setting up the <code>NSPersistentStoreCoordinator</code> then you need to enable Lightweight migrations by passing in an <code>options</code> dictionary with both <code>NSMigratePersistentStoresAutomaticallyOption</code> and <code>NSInferMappingModelAutomaticallyOption</code> set to <code>true</code> when loading the persistent store.</p>
</blockquote>
<p>These automatic migrations are performed as one-step migrations; directly from the source to destination model. So if we support 4 model versions, mapping models would exist for <code>1 to 4</code>, <code>2 to 4</code> and <code>3 to 4</code>. While this is the most efficient migration approach from a device performance point-of-view, it can actually be quite wasteful from a development point-of-view. For example if we added a new model version (<code>5</code>) we would need to create <strong>4 new mapping models</strong> from <code>1 to 5</code>, <code>2 to 5</code>, <code>3 to 5</code> and <code>4 to 5</code> which as you can see doesn&apos;t reuse any of the mapping models for migrating to version <code>4</code>. With a one-step migration approach, each newly added model version requires <code>n-1</code> mapping models (where <code>n</code> is the number of supported model versions) to be created.</p>
<p>It&apos;s possible to reduce the amount of work required to perform a Core Data migration by disabling automatic migrations and so break the requirement to perform migrations in one-step. With a manual migration approach, we can perform the full migration by chaining multiple smaller migrations together. As the full migration is split into smaller migrations when adding a new model version we only need to handle migrating to the new model version from its direct predecessor rather than all it&apos;s predecessors e.g. <code>4 to 5</code> because we can reuse the existing <code>1 to 2</code>, <code>2 to 3</code> and <code>3 to 4</code> mapping models. Not only do manual migrations reduce the amount of work involved they also help to reduce the complexity of the migration as the conceptional distance between the source and destination version is reduced when compared to one-step migrations i.e. version <code>4</code> is much nearer to the structure of version <code>5</code> than version <code>1</code> is - this should make it easier spot any issues with the migration.</p>
<h3 id="progressivemigrations">Progressive migrations</h3>
<p>In order to support progressive migrations we&apos;ll need to answer a few questions:</p>
<ol>
<li>Which model version comes after <em>version X</em>?</li>
<li>What is a migration step?</li>
<li>How can we combine the migration steps into a migration path?</li>
<li>How do we trigger a migration?</li>
</ol>
<p>These questions will be answered with the help of 4 separate types:</p>
<ol>
<li><code>CoreDataMigrationVersion</code></li>
<li><code>CoreDataMigrationStep</code></li>
<li><code>CoreDataMigrator</code></li>
<li><code>CoreDataManager</code></li>
</ol>
<p>These types will come together in the following class structure (along with several helper extensions):</p>
<p><img src="https://williamboles.com/content/images/2019/01/core-data-migration-class-diagram-1.png" alt="Progressive Core Data Migrations" loading="lazy"></p>
<blockquote>
<p>Don&apos;t worry if that doesn&apos;t all make sense yet, we will look into each type in greater depth below.</p>
</blockquote>
<h6 id="whichmodelversioncomesafterversionx">Which model version comes after <em>version X</em>?</h6>
<p>Each <code>CoreDataMigrationVersion</code> instance will represent a Core Data model version. As each Core Data model version is unique and known at compile time they can be perfectly represented as enum cases, with the raw value of each case being the Core Data model name:</p>
<pre><code class="swift">enum CoreDataMigrationVersion: String, CaseIterable {
    case version1 = &quot;CoreDataMigration_Example&quot;

    // MARK: - Current

    static var current: CoreDataMigrationVersion {
        guard let current = allCases.last else {
            fatalError(&quot;no model versions found&quot;)
        }

        return current
    }

    // MARK: - Migration

    func nextVersion() -&gt; CoreDataMigrationVersion? {
        switch self {
        case .version1:
            return nil
        }
    }
}</code></pre>
<p>Migrations are often concerned with what the latest model version is - the static <code>current</code> property allows easy access to this version. Before <a href="https://www.hackingwithswift.com/articles/77/whats-new-in-swift-4-2?ref=williamboles.com">Swift 4.2</a> we would probably have had to hardcode this property to one case which would then lead to bugs if we forgot to update that property when adding a new version. However in Swift 4.2 we got the <a href="https://developer.apple.com/documentation/swift/caseiterable?ref=williamboles.com"><code>CaseIterable</code></a> protocol which makes it possible to get an array of the cases in an enum in the order they were defined in via the <code>allCases</code> property. This means that to get the latest model version should be as simple as calling <code>last</code> on the <code>allCases</code> array - no need to hardcode anything.</p>
<p>In <code>CoreDataMigrationVersion</code> the <code>nextVersion()</code> method is where the real work happens as it determines which (if any) version comes after <code>self</code>.</p>
<p>You may be thinking:</p>
<p>&quot;Why bother with <code>nextVersion()</code> when we can just always choose the next enum case?&quot;</p>
<p>If you are reading this post before performing your first migration I congratulate you on your:</p>
<ol>
<li>Excellent taste in selecting blog posts.</li>
<li>Organisational ability.</li>
</ol>
<p>However, I&apos;m guessing it&apos;s more likely that you&apos;ve found this post having already performed a number of migrations and been hit by the inherent scaling issue with the default one-step migration approach. If you are in the latter camp then you will have already implemented one-step migrations having configured various mapping models and maybe even written a migration policy or two. Instead of throwing all that work away we can use it and tie it into the new <em>progressive</em> approach. In a hypothetical project that had 6 model versions which until model version <code>4</code> used the one-step migration approach before switching over to the progressive migration approach, then <code>nextVersion</code> would look like:</p>
<pre><code class="swift">func nextVersion() -&gt; CoreDataMigrationVersion? {
    switch self {
    case .version1, .version2, .version3:
        return .version4
    case .version4:
        return .version5
    case .version5:
        return .version6
    case .version6:
        return nil
    }
}</code></pre>
<p>In the above code snippet, <code>version1</code>, <code>version2</code> and <code>version3</code>  migrate directly to <code>version4</code> and then <code>version4</code> and <code>version5</code> migrate to their direct successor. As you can see both these migration approaches can co-exist very happily with each other.</p>
<p>Even if you don&apos;t have any existing migrations, it&apos;s possible that at some point in the future a broken model version is released that corrupts your user&apos;s data upon migration. In order to minimise to the impact of this mistake, <code>nextVersion</code> could be configured to bypass that broken model version so that any currently unaffected user are never impacted:</p>
<pre><code class="swift">func nextVersion() -&gt; CoreDataMigrationVersion? {
    switch self {
    case .version1:
        return .version2
    case .version2:
        return .version4 // skipping corrupted .version3
    case .version3:
        return .version4
    case .version4:
        return nil
    }
}</code></pre>
<p>Both these issues are easily bypassed using <code>nextVersion()</code> without adding too much complexity to the overall solution.</p>
<h6 id="whatisamigrationstep">What is a migration step?</h6>
<p>A migration happens between 2 model versions by having a mapping from the entities, attributes and relationships of the source model and their counterpoints in the destination model. As such <code>CoreDataMigrationStep</code> needs to contain 3 properties:</p>
<ol>
<li>Source version model.</li>
<li>Destination version model.</li>
<li>Mapping model.</li>
</ol>
<pre><code class="swift">struct CoreDataMigrationStep {

    let sourceModel: NSManagedObjectModel
    let destinationModel: NSManagedObjectModel
    let mappingModel: NSMappingModel

    // MARK: Init

    init(sourceVersion: CoreDataMigrationVersion, destinationVersion: CoreDataMigrationVersion) {
        let sourceModel = NSManagedObjectModel.managedObjectModel(forResource: sourceVersion.rawValue)
        let destinationModel = NSManagedObjectModel.managedObjectModel(forResource: destinationVersion.rawValue)

        guard let mappingModel = CoreDataMigrationStep.mappingModel(fromSourceModel: sourceModel, toDestinationModel: destinationModel) else {
            fatalError(&quot;Expected modal mapping not present&quot;)
        }

        self.sourceModel = sourceModel
        self.destinationModel = destinationModel
        self.mappingModel = mappingModel
    }

    // MARK: - Mapping

    private static func mappingModel(fromSourceModel sourceModel: NSManagedObjectModel, toDestinationModel destinationModel: NSManagedObjectModel) -&gt; NSMappingModel? {
        guard let customMapping = customMappingModel(fromSourceModel: sourceModel, toDestinationModel: destinationModel) else {
            return inferredMappingModel(fromSourceModel:sourceModel, toDestinationModel: destinationModel)
        }

        return customMapping
    }

    private static func inferredMappingModel(fromSourceModel sourceModel: NSManagedObjectModel, toDestinationModel destinationModel: NSManagedObjectModel) -&gt; NSMappingModel? {
        return try? NSMappingModel.inferredMappingModel(forSourceModel: sourceModel, destinationModel: destinationModel)
    }

    private static func customMappingModel(fromSourceModel sourceModel: NSManagedObjectModel, toDestinationModel destinationModel: NSManagedObjectModel) -&gt; NSMappingModel? {
        return NSMappingModel(from: [Bundle.main], forSourceModel: sourceModel, destinationModel: destinationModel)
    }
}</code></pre>
<blockquote>
<p>It&apos;s possible to have multiple mapping models between versions, (this can be especially useful when migrating large data sets) in this post in an attempt to keep things simple I assume only one mapping model.</p>
</blockquote>
<p><code>CoreDataMigrationStep</code> takes the source model and destination model and attempts to find a way to map between them. As we know there are two types of migrations: Lightweight and Standard - both of which use a <a href="https://developer.apple.com/documentation/coredata/nsmappingmodel?ref=williamboles.com"><code>NSMappingModel</code></a> instance to hold the mapping path between the versions. Because of this shared output type <code>mappingModel(fromSourceModel:toDestinationModel)</code> handles searching for a mapping model using either Lightweight and Standard migration. First, a search is made for a custom migration mapping existing in the bundle (Standard migration) and then if no custom mapping model is found Core Data is asked to try and infer a mapping model (Lightweight migration). If a mapping model can&apos;t be found using either approach, a fatal error is thrown as this migration path isn&apos;t supported.</p>
<h6 id="howcanwecombinethemigrationstepsintoamigrationpath">How can we combine the migration steps into a migration path?</h6>
<p><code>CoreDataMigrator</code> is at the heart of our migration solution and has <strong>3</strong> tasks:</p>
<ol>
<li>Determining if there needs to be a migration.</li>
<li>Ensuring the persistent store is ready to be migrated.</li>
<li>Performing the migration.</li>
</ol>
<p>As <code>CoreDataManager</code> (we will see this later) holds a reference to <code>CoreDataMigrator</code> we can make our lives easier by wrapping <code>CoreDataMigrator</code> in a protocol so that it&apos;s easier to mock when writing tests for <code>CoreDataManager</code>:</p>
<pre><code class="swift">protocol CoreDataMigratorProtocol {
    func requiresMigration(at storeURL: URL, toVersion version: CoreDataMigrationVersion) -&gt; Bool
    func migrateStore(at storeURL: URL, toVersion version: CoreDataMigrationVersion)
}</code></pre>
<p>Now that we have that protocol lets look at how <code>CoreDataMigrator</code> implements the first of those protocol methods:</p>
<pre><code class="swift">class CoreDataMigrator: CoreDataMigratorProtocol {

    // MARK: - Check

    func requiresMigration(at storeURL: URL, toVersion version: CoreDataMigrationVersion) -&gt; Bool {
        guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL) else {
            return false
        }

        return (CoreDataMigrationVersion.compatibleVersionForStoreMetadata(metadata) != version)
    }

    //Omitted other methods
}</code></pre>
<p>In the above method, the persistent store&apos;s metadata is loaded and checked to see if it&apos;s compatible with the current bundle model&apos;s metadata. To support this we need to extend <code>CoreDataMigrationVersion</code> to include:</p>
<pre><code class="swift">private extension CoreDataMigrationVersion {

    // MARK: - Compatible

    static func compatibleVersionForStoreMetadata(_ metadata: [String : Any]) -&gt; CoreDataMigrationVersion? {
        let compatibleVersion = CoreDataMigrationVersion.allCases.first {
            let model = NSManagedObjectModel.managedObjectModel(forResource: $0.rawValue)

            return model.isConfiguration(withName: nil, compatibleWithStoreMetadata: metadata)
        }

        return compatibleVersion
    }
}</code></pre>
<p>The above method attempts to find a compatible model for the <code>metadata</code> by iterating through the model associated with a case of <code>CoreDataMigrationVersion</code>. If a compatible model is found the associated version is returned, else <code>nil</code> is returned.</p>
<p>Now that we know if a migration is required or not, lets look at how that migration happens by implementing the next protocol method:</p>
<pre><code class="swift">class CoreDataMigrator: CoreDataMigratorProtocol {

    //Omitted other methods

    // MARK: - Migration

    func migrateStore(at storeURL: URL, toVersion version: CoreDataMigrationVersion) {
        forceWALCheckpointingForStore(at: storeURL)

        var currentURL = storeURL
        let migrationSteps = self.migrationStepsForStore(at: storeURL, toVersion: version)

        for migrationStep in migrationSteps {
            let manager = NSMigrationManager(sourceModel: migrationStep.sourceModel, destinationModel: migrationStep.destinationModel)
            let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString)

            do {
                try manager.migrateStore(from: currentURL, sourceType: NSSQLiteStoreType, options: nil, with: migrationStep.mappingModel, toDestinationURL: destinationURL, destinationType: NSSQLiteStoreType, destinationOptions: nil)
            } catch let error {
                fatalError(&quot;failed attempting to migrate from \(migrationStep.sourceModel) to \(migrationStep.destinationModel), error: \(error)&quot;)
            }

            if currentURL != storeURL {
                //Destroy intermediate step&apos;s store
                NSPersistentStoreCoordinator.destroyStore(at: currentURL)
            }

            currentURL = destinationURL
        }

        NSPersistentStoreCoordinator.replaceStore(at: storeURL, withStoreAt: currentURL)

        if (currentURL != storeURL) {
            NSPersistentStoreCoordinator.destroyStore(at: currentURL)
        }
    }

    private func migrationStepsForStore(at storeURL: URL, toVersion destinationVersion: CoreDataMigrationVersion) -&gt; [CoreDataMigrationStep] {
        guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL), let sourceVersion = CoreDataMigrationVersion.compatibleVersionForStoreMetadata(metadata) else {
            fatalError(&quot;unknown store version at URL \(storeURL)&quot;)
        }

        return migrationSteps(fromSourceVersion: sourceVersion, toDestinationVersion: destinationVersion)
    }

    private func migrationSteps(fromSourceVersion sourceVersion: CoreDataMigrationVersion, toDestinationVersion destinationVersion: CoreDataMigrationVersion) -&gt; [CoreDataMigrationStep] {
        var sourceVersion = sourceVersion
        var migrationSteps = [CoreDataMigrationStep]()

        while sourceVersion != destinationVersion, let nextVersion = sourceVersion.nextVersion() {
            let migrationStep = CoreDataMigrationStep(sourceVersion: sourceVersion, destinationVersion: nextVersion)
            migrationSteps.append(migrationStep)

            sourceVersion = nextVersion
        }

        return migrationSteps
    }

    // MARK: - WAL

    func forceWALCheckpointingForStore(at storeURL: URL) {
        guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL), let currentModel = NSManagedObjectModel.compatibleModelForStoreMetadata(metadata) else {
            return
        }

        do {
            let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: currentModel)

            let options = [NSSQLitePragmasOption: [&quot;journal_mode&quot;: &quot;DELETE&quot;]]
            let store = persistentStoreCoordinator.addPersistentStore(at: storeURL, options: options)
            try persistentStoreCoordinator.remove(store)
        } catch let error {
            fatalError(&quot;failed to force WAL checkpointing, error: \(error)&quot;)
        }
    }
}</code></pre>
<p>There is quite a bit of code there, let&apos;s break it down into smaller pieces and explore each separately, building the migration process up from bottom-to-top.</p>
<p>Before attempting a migration, we need to undertake some housekeeping on our persistent store.</p>
<p>Since iOS 7, Core Data has used the <a href="https://www.sqlite.org/wal.html?ref=williamboles.com">Write-Ahead Logging (WAL)</a> option on SQLite stores to provide the ability to recover from crashes by allowing changes to be rolled back until the database is stable. If you have ever had to perform a rollback before, the WAL approach may work a little differently from what you are expecting. Rather than directly writing changes to the <code>sqlite</code> file and having a pre-write copy of the changes to rollback to, in WAL mode the changes are first written to the <code>sqlite-wal</code> file and at some future date those changes are transferred to the <code>sqlite</code> file. The <code>sqlite-wal</code> file is in effect an up-to-date copy of some of the data stored in the main <code>sqlite</code> file.</p>
<p>The <code>sqlite-wal</code> and <code>sqlite</code> files store their data using the same structure to allow data to be transferred easily between them. However, this shared structure causes issues during migration as Core Data only migrates the data stored in the <code>sqlite</code> file to the new structure, leaving the data in the <code>sqlite-wal</code> file in the old structure. The resulting mismatch in structure will lead to a crash when Core Data attempts to update/use data stored in the <code>sqlite-wal</code> file &#x1F61E; . To avoid this crash, we need to force any data in the <code>sqlite-wal</code> file into the <code>sqlite</code> file before we perform a migration - a process known as <code>checkpointing</code>:</p>
<pre><code class="swift">func forceWALCheckpointingForStore(at storeURL: URL) {
    guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL), let currentModel = NSManagedObjectModel.compatibleModelForStoreMetadata(metadata) else {
        return
    }

    do {
        let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: currentModel)

        let options = [NSSQLitePragmasOption: [&quot;journal_mode&quot;: &quot;DELETE&quot;]]
        let store = persistentStoreCoordinator.addPersistentStore(at: storeURL, options: options)
        try persistentStoreCoordinator.remove(store)
    } catch let error {
        fatalError(&quot;failed to force WAL checkpointing, error: \(error)&quot;)
    }
}</code></pre>
<p>The above method, forces <code>checkpointing</code> to occur. A side effect of <code>checkpointing</code> is that the empty <code>sqlite-wal</code> file is deleted for us so removing the <code>store</code> from the <code>persistentStoreCoordinator</code> is all the cleanup that we need to perform.</p>
<blockquote>
<p>An easy mistake to make when <code>checkpointing</code> is using the bundle&apos;s model rather than the store&apos;s model - remember we want to perform <code>checkpointing</code> on the live (store) model before attempting to migrate to the latest (bundle) model.</p>
</blockquote>
<p>Before a migration can be performed Core Data must first construct the individual migration steps into a migration path:</p>
<pre><code class="swift">private func migrationStepsForStore(at storeURL: URL, toVersion destinationVersion: CoreDataMigrationVersion) -&gt; [CoreDataMigrationStep] {
    guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL), let sourceVersion = CoreDataMigrationVersion.compatibleVersionForStoreMetadata(metadata) else {
        fatalError(&quot;unknown store version at URL \(storeURL)&quot;)
    }

    return migrationSteps(fromSourceVersion: sourceVersion, toDestinationVersion: destinationVersion)
}

private func migrationSteps(fromSourceVersion sourceVersion: CoreDataMigrationVersion, toDestinationVersion destinationVersion: CoreDataMigrationVersion) -&gt; [CoreDataMigrationStep] {
    var sourceVersion = sourceVersion
    var migrationSteps = [CoreDataMigrationStep]()

    while sourceVersion != destinationVersion, let nextVersion = sourceVersion.nextVersion() {
        let migrationStep = CoreDataMigrationStep(sourceVersion: sourceVersion, destinationVersion: nextVersion)
        migrationSteps.append(migrationStep)

        sourceVersion = nextVersion
    }

    return migrationSteps
}</code></pre>
<p>In the above methods, the migration path is built by looping through the appropriate model versions until the destination model version is reached. This migration path will take the users data from the persistent store&apos;s model version to the bundle model version in a progressive migration:</p>
<pre><code class="swift">func migrateStore(at storeURL: URL, toVersion version: CoreDataMigrationVersion) {
    forceWALCheckpointingForStore(at: storeURL)

    var currentURL = storeURL
    let migrationSteps = self.migrationStepsForStore(at: storeURL, toVersion: version)

    for migrationStep in migrationSteps {
        let manager = NSMigrationManager(sourceModel: migrationStep.sourceModel, destinationModel: migrationStep.destinationModel)
        let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString)

        do {
            try manager.migrateStore(from: currentURL, sourceType: NSSQLiteStoreType, options: nil, with: migrationStep.mappingModel, toDestinationURL: destinationURL, destinationType: NSSQLiteStoreType, destinationOptions: nil)
        } catch let error {
            fatalError(&quot;failed attempting to migrate from \(migrationStep.sourceModel) to \(migrationStep.destinationModel), error: \(error)&quot;)
        }

        if currentURL != storeURL {
            //Destroy intermediate step&apos;s store
            NSPersistentStoreCoordinator.destroyStore(at: currentURL)
        }

        currentURL = destinationURL
    }

    NSPersistentStoreCoordinator.replaceStore(at: storeURL, withStoreAt: currentURL)

    if (currentURL != storeURL) {
        NSPersistentStoreCoordinator.destroyStore(at: currentURL)
    }
}</code></pre>
<p>In the above method, we iterate through each migration step and attempt to perform a migration using <a href="https://developer.apple.com/documentation/coredata/nsmigrationmanager?ref=williamboles.com"><code>NSMigrationManager</code></a>. The result of each completed migration step is saved to a temporary persistent store, only once the migration is complete is the original persistent store overwritten. If there is a failure during any individual migration step a fatal error is thrown - this is especially useful during the development of a custom migration path.</p>
<p>In the above code snippets, we&apos;ve seen a number of methods used that are not part of the standard API so I&apos;ve included the extensions that contain these methods below. As with most extensions, the methods are used to reduce boilerplate code:</p>
<pre><code class="swift">extension NSPersistentStoreCoordinator {

    // MARK: - Destroy

    static func destroyStore(at storeURL: URL) {
        do {
            let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: NSManagedObjectModel())
            try persistentStoreCoordinator.destroyPersistentStore(at: storeURL, ofType: NSSQLiteStoreType, options: nil)
        } catch let error {
            fatalError(&quot;failed to destroy persistent store at \(storeURL), error: \(error)&quot;)
        }
    }

    // MARK: - Replace

    static func replaceStore(at targetURL: URL, withStoreAt sourceURL: URL) {
        do {
            let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: NSManagedObjectModel())
            try persistentStoreCoordinator.replacePersistentStore(at: targetURL, destinationOptions: nil, withPersistentStoreFrom: sourceURL, sourceOptions: nil, ofType: NSSQLiteStoreType)
        } catch let error {
            fatalError(&quot;failed to replace persistent store at \(targetURL) with \(sourceURL), error: \(error)&quot;)
        }
    }

    // MARK: - Meta

    static func metadata(at storeURL: URL) -&gt; [String : Any]?  {
        return try? NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, at: storeURL, options: nil)
    }

    // MARK: - Add

    func addPersistentStore(at storeURL: URL, options: [AnyHashable : Any]) -&gt; NSPersistentStore {
        do {
            return try addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
        } catch let error {
            fatalError(&quot;failed to add persistent store to coordinator, error: \(error)&quot;)
        }
    }
}</code></pre>
<pre><code class="swift">extension NSManagedObjectModel {

    // MARK: - Resource

    static func managedObjectModel(forResource resource: String) -&gt; NSManagedObjectModel {
        let mainBundle = Bundle.main
        let subdirectory = &quot;CoreDataMigration_Example.momd&quot;
        let omoURL = mainBundle.url(forResource: resource, withExtension: &quot;omo&quot;, subdirectory: subdirectory) // optimised model file
        let momURL = mainBundle.url(forResource: resource, withExtension: &quot;mom&quot;, subdirectory: subdirectory)

        guard let url = omoURL ?? momURL else {
            fatalError(&quot;unable to find model in bundle&quot;)
        }

        guard let model = NSManagedObjectModel(contentsOf: url) else {
            fatalError(&quot;unable to load model in bundle&quot;)
        }

        return model
    }
}</code></pre>
<blockquote>
<p>I won&apos;t go into detail about what these extension methods do as I believe their names do a good enough job.</p>
</blockquote>
<h6 id="howdowetriggeramigration">How do we trigger a migration?</h6>
<p><code>CoreDataManager</code> handles both setting up the Core Data stack and triggering a migration (if needed):</p>
<pre><code class="swift">class CoreDataManager {

    let migrator: CoreDataMigratorProtocol
    private let storeType: String

    lazy var persistentContainer: NSPersistentContainer = {
        let persistentContainer = NSPersistentContainer(name: &quot;CoreDataMigration_Example&quot;)
        let description = persistentContainer.persistentStoreDescriptions.first
        description?.shouldInferMappingModelAutomatically = false //inferred mapping will be handled else where
        description?.shouldMigrateStoreAutomatically = false
        description?.type = storeType

        return persistentContainer
    }()

    lazy var backgroundContext: NSManagedObjectContext = {
        let context = self.persistentContainer.newBackgroundContext()
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

        return context
    }()

    lazy var mainContext: NSManagedObjectContext = {
        let context = self.persistentContainer.viewContext
        context.automaticallyMergesChangesFromParent = true

        return context
    }()

    // MARK: - Singleton

    static let shared = CoreDataManager()

    // MARK: - Init

    init(storeType: String = NSSQLiteStoreType, migrator: CoreDataMigratorProtocol = CoreDataMigrator()) {
        self.storeType = storeType
        self.migrator = migrator
    }

    // MARK: - SetUp

    func setup(completion: @escaping () -&gt; Void) {
        loadPersistentStore {
            completion()
        }
    }

    // MARK: - Loading

    private func loadPersistentStore(completion: @escaping () -&gt; Void) {
        migrateStoreIfNeeded {
            self.persistentContainer.loadPersistentStores { description, error in
                guard error == nil else {
                    fatalError(&quot;was unable to load store \(error!)&quot;)
                }

                completion()
            }
        }
    }

    private func migrateStoreIfNeeded(completion: @escaping () -&gt; Void) {
        guard let storeURL = persistentContainer.persistentStoreDescriptions.first?.url else {
            fatalError(&quot;persistentContainer was not set up properly&quot;)
        }

        if migrator.requiresMigration(at: storeURL, toVersion: CoreDataMigrationVersion.current) {
            DispatchQueue.global(qos: .userInitiated).async {
                self.migrator.migrateStore(at: storeURL, toVersion: CoreDataMigrationVersion.current)

                DispatchQueue.main.async {
                    completion()
                }
            }
        } else {
            completion()
        }
    }
}</code></pre>
<p>If you have ever seen a Core Data stack setup before, you will instantly notice how little code the <code>CoreDataManager</code> contains. Over the years Core Data has evolved and become more developer friendly, above we are taking advantage of a relatively new piece of the Core Data family - <a href="https://developer.apple.com/documentation/coredata/nspersistentcontainer?ref=williamboles.com"><code>NSPersistentContainer</code></a> which was introduced in iOS 10:</p>
<pre><code class="swift">lazy var persistentContainer: NSPersistentContainer = {
    let persistentContainer = NSPersistentContainer(name: &quot;CoreDataMigration_Example&quot;)
    let description = persistentContainer.persistentStoreDescriptions.first
    description?.shouldInferMappingModelAutomatically = false //inferred mapping will be handled else where
    description?.shouldMigrateStoreAutomatically = false
    description?.type = storeType

    return persistentContainer
}()</code></pre>
<p><code>NSPersistentContainer</code> simplifies the creation of the managed object model, persistent store coordinator and the managed object contexts by making smart assumptions on how we want our persistent store configured. It&apos;s still possible to access the <code>NSManagedModel</code>, <code>NSPersistentStoreCoordinator</code> and <code>NSManagedObjectContext</code> instances via this container but we no longer have to handle their set-up code.</p>
<blockquote>
<p>Our example project is called <code>CoreDataMigration-Example</code> however as you can see when creating the <code>NSPersistentContainer</code> we give <code>CoreDataMigration_Example</code> as our model&apos;s name - see Apple&apos;s <a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html?ref=williamboles.com###//apple_ref/doc/uid/TP40014216-CH10-ID138">documentation</a> on why the <code>-</code> became a <code>_</code>.</p>
</blockquote>
<p>As we only have one Core Data stack, <code>CoreDataManager</code> is a singleton:</p>
<pre><code class="swift">static let shared = CoreDataManager()

init(storeType: String = NSSQLiteStoreType, migrator: CoreDataMigratorProtocol = CoreDataMigrator()) {
    self.storeType = storeType
    self.migrator = migrator
}</code></pre>
<blockquote>
<p><code>CoreDataManager</code> is a little odd when it comes to being a singleton in that it has an explicit <code>init</code> implementation. This explicit <code>init</code> method allows for changing the type of persistent store used - by default it&apos;s <code>NSSQLiteStoreType</code> however when unit testing we will actually create multiple instances of <code>CoreDataManager</code> using <code>NSInMemoryStoreType</code> to avoid persisting data between tests (and having tests potentially pollute each other). A persistent store type of <code>NSInMemoryStoreType</code> will cause our Core Data stack to only be created in-memory and so be more cheaply torn down and set up than if we used <code>NSSQLiteStoreType</code>. In the accompanying example project, you can see how this is used in the <code>CoreDataManagerTests</code> class.</p>
</blockquote>
<p>Loading the persistent store involves interacting with the disk which compared to memory interactions is more expensive &#x23F2;&#xFE0F;, as such the <code>loadPersistentStores(completionHandler:)</code> method on <code>NSPersistentContainer</code> is asynchronous. This is mirrored by the <code>setup()</code>, <code>loadPersistentStore(completion:)</code> and <code>migrateStoreIfNeeded(completion:)</code> methods:</p>
 <pre><code class="swift">func setup(completion: @escaping () -&gt; Void) {
   loadPersistentStore {
       completion()
   }
}

private func loadPersistentStore(completion: @escaping () -&gt; Void) {
   migrateStoreIfNeeded {
       self.persistentContainer.loadPersistentStores { description, error in
           guard error == nil else {
               fatalError(&quot;was unable to load store \(error!)&quot;)
           }

           completion()
       }
   }
}

private func migrateStoreIfNeeded(completion: @escaping () -&gt; Void) {
    guard let storeURL = persistentContainer.persistentStoreDescriptions.first?.url else {
        fatalError(&quot;persistentContainer was not set up properly&quot;)
    }

    if migrator.requiresMigration(at: storeURL, toVersion: CoreDataMigrationVersion.current) {
        DispatchQueue.global(qos: .userInitiated).async {
            self.migrator.migrateStore(at: storeURL, toVersion: CoreDataMigrationVersion.current)

            DispatchQueue.main.async {
                completion()
            }
        }
    } else {
        completion()
    }
}</code></pre>
<p>Before an attempt is made to load the persistent store, we check if the model needs to be migrated by calling <code>migrateStoreIfNeeded(completion:)</code>.</p>
<p>If the answer is <code>yes</code> - the migrator attempts to migrate the user&apos;s data. As migrating can be a relatively slow process, the migration happens on a background queue to avoid hanging the UI. Once the migration is completed the <code>completion</code> closure is called on the main queue.</p>
<p>If the answer is <code>no</code> - the <code>completion</code> closure is called straight away.</p>
<p>Once the persistent store is successfully loaded, the <code>setup()</code> method calls its <code>completion</code> closure and the stack finishes setting up.</p>
<p>This <code>setup</code> method is called in the <code>AppDelegate</code>:</p>
<pre><code class="swift">func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {
    CoreDataManager.shared.setup {
        self.presentMainUI()
    }

    return true
}</code></pre>
<p>The above code snippet is from the example project where the user is shown a loading screen while the Core Data stack is being set up. Only once the setup is complete is the user allowed into the <em>app proper</em>. <code>presentMainUI</code> switches out the window&apos;s root view controller for a navigation stack that can freely use Core Data. While this is strictly not necessary, by splitting the UI into <code>pre</code> and <code>post</code> <em>Core Data stack set up</em> it is possible to avoid race conditions where the app is attempting to use Core Data before it has finished setting up.</p>
<h3 id>&#x1F483;&#x1F942;&#x1F389;&#x1F57A;</h3>
<p><strong>Congratulations, that&apos;s all there is to the progressive migration approach.</strong></p>
<p>The rest of this post is devoted to putting the above migration approach into practice by migrating an app through <strong>3</strong> Core Data model versions.</p>
<h3 id="colourfulposts">Colourful Posts</h3>
<p><code>Colourful Posts</code> is a simple app that allows the user to create posts that are persisted in Core Data. Each post consists of:</p>
<ul>
<li>A unique ID.</li>
<li>A random associated colour represented as a hex string.</li>
<li>The body/content of the post.</li>
<li>The date the post was created on.</li>
</ul>
<p>So that the model looks like:</p>
<p><img src="https://williamboles.com/content/images/2019/01/Screenshot-showing-colourful-posts-model-1.png" alt="Progressive Core Data Migrations" loading="lazy"></p>
<p>Each post that the user creates is then displayed in a tableview as a brightly coloured cell.</p>
<p><img src="https://williamboles.com/content/images/2019/01/screenshot-of-colourful-posts-2.png" alt="Progressive Core Data Migrations" loading="lazy"></p>
<blockquote>
<p>To keep this post to a responsible length I won&apos;t show any code from <code>Colourful Posts</code> that isn&apos;t connected to performing a migration.</p>
</blockquote>
<p>It&apos;s a simple, fun app that we submit to Apple for approval &#x1F91E;.</p>
<h5 id="migratingtoversion2">Migrating to version 2</h5>
<p>Despite not being able to edit posts, Apple not only approves <code>Colourful Posts</code>, they love it. So much so that they feature it on the <code>Today</code> tab. <code>Colourful Posts</code> is instantly propelled to the top of the charts. After hundreds of thousands of downloads, we decide to hire a new developer to help steer the success-train we find ourselves on &#x1F682;. However, in their first week the new developer mistakes the information stored in the <code>color</code> property on <code>Post</code> to be using RGB rather hex to store the color as a string. Unfortunately we don&apos;t catch this mismatch until it&apos;s in production and leads to the app crashing on launch &#x1F61E;. To avoid this issue happening when we hire more developers we decide to rename <code>color</code> to <code>hexColor</code>. As this is a change to the model we need to create a new model version and handle the migration between the old and new version.</p>
<blockquote>
<p>To create a new model version, select the <code>*.xcdatamodel</code> (it may be called <code>*.xcdatamodeld</code>) file in the <code>Project Navigator</code>, open the <code>Editor</code> menu from the top bar and click on the <code>Add Model Version...</code> option. In the wizard that opens, this new model will already be given a name, this typically follows <code>[ModelName] [Number]</code> so <code>CoreDataMigration_Example 2</code> but this can be changed to whatever you want.</p>
</blockquote>
<p>Lightweight migrations are typically a less intensive form of migration than Standard migrations (both from a developer and <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html?ref=williamboles.com">performance POV</a>) because of this I prefer to perform Lightweight migrations whenever possible. Lightweight migrations can handle the following transformations to the model:</p>
<ul>
<li>Adding an attribute.</li>
<li>Removing an attribute.</li>
<li>Changing a non-optional attribute to be optional.</li>
<li>Changing an optional attribute to non-optional (by defining a default value).</li>
<li>Renaming an entity, attribute or relationship (by providing a <code>Renaming ID</code>).</li>
<li>Adding a relationship.</li>
<li>Removing a relationship.</li>
<li>Changing the entity hierarchy.</li>
</ul>
<p>An impressive list of transformations that we get <em>free</em> (or almost free) with Lightweight migrations. The <code>color</code> to <code>hexColor</code> change is covered by the <code>Renaming an entity, attribute or relationship</code> which has a small caveat: <code>by providing a Renaming ID</code>. The <code>Renaming ID</code> creates a link between the old attribute and the new attribute. All it requires is to add the old attribute name to the new attribute&apos;s metadata:</p>
<p><img src="https://williamboles.com/content/images/2019/01/Screenshot-showing-renaming-id-3.png" alt="Progressive Core Data Migrations" loading="lazy"></p>
<p>With this information, Core Data now knows that <code>color</code> and <code>hexColor</code> are the same attribute just with different names and that rather than discarding <code>color</code> during a Lightweight migration the value should be transferred to <code>hexColor</code>.</p>
<p>With that change the only thing that&apos;s left to do is update <code>CoreDataMigrationVersion</code> to allow migrations from <code>CoreDataMigration_Example</code> to <code>CoreDataMigration_Example 2</code>:</p>
<pre><code class="swift">enum CoreDataMigrationVersion: String, CaseIterable {
    case version1 = &quot;CoreDataMigration_Example&quot;
    case version2 = &quot;CoreDataMigration_Example 2&quot;

    //Omitting methods

    func nextVersion() -&gt; CoreDataMigrationVersion? {
    switch self {
    case .version1:
        return .version2
    case .version2:
        return .nil
    }
}</code></pre>
<p>A new case was added to <code>CoreDataMigrationVersion</code> - <code>version2</code>. As with <code>version1</code>, this new version has a raw value which maps to the name of its respective model version - <code>CoreDataMigration_Example 2</code>. <code>nextVersion()</code> has also been updated so that there is a migration path from <code>version1</code> to <code>version2</code>.</p>
<p>Now that we have a migration path, let&apos;s look at unit testing it. Unit testing a migration path requires:</p>
<ol>
<li>Populating a SQLite database using the <code>CoreDataMigration_Example</code> model.</li>
<li>Copying that SQLite database into the test target.</li>
<li>Asserting that the contents of that SQLite database migrated as expected.</li>
</ol>
<blockquote>
<p>Before copying your SQLite database, it&apos;s important to ensure it is in fact populated with test data. As we discussed above, Core Data uses <code>Write-Ahead Logging</code> to improve performance so your data could be residing in the <code>sqlite-wal</code> file rather than the <code>sqlite</code> file. The easiest way to force any uncommitted changes is to <em>fake</em> a migration - add a breakpoint just after the <code>forceWALCheckpointingForStore(at:)</code> method, open the <code>Application Support</code> folder, copy the <code>sqlite</code> file and then abort the migration.</p>
</blockquote>
<pre><code class="swift">class CoreDataMigratorTests: XCTestCase {

    var sut: CoreDataMigrator!

    // MARK: - Lifecycle

    override class func setUp() {
        super.setUp()

        FileManager.clearTempDirectoryContents()
    }

    override func setUp() {
        super.setUp()

        sut = CoreDataMigrator()
    }

    override func tearDown() {
        sut = nil

        super.tearDown()
    }

    func tearDownCoreDataStack(context: NSManagedObjectContext) {
        context.destroyStore()
    }

    // MARK: - Tests

    // MARK: SingleStepMigrations

    func test_individualStepMigration_1to2() {
        let sourceURL = FileManager.moveFileFromBundleToTempDirectory(filename: &quot;CoreDataMigration_Example_1.sqlite&quot;)
        let toVersion = CoreDataMigrationVersion.version2

        sut.migrateStore(at: sourceURL, toVersion: toVersion)

        XCTAssertTrue(FileManager.default.fileExists(atPath: sourceURL.path))

        let model = NSManagedObjectModel.managedObjectModel(forResource: toVersion.rawValue)
        let context = NSManagedObjectContext(model: model, storeURL: sourceURL)
        let request = NSFetchRequest<nsmanagedobject>.init(entityName: &quot;Post&quot;)
        let sort = NSSortDescriptor(key: &quot;postID&quot;, ascending: false)
        request.sortDescriptors = [sort]

        let migratedPosts = try? context.fetch(request)

        XCTAssertEqual(migratedPosts?.count, 10)

        let firstMigratedPost = migratedPosts?.first

        let migratedDate = firstMigratedPost?.value(forKey: &quot;date&quot;) as? Date
        let migratedHexColor = firstMigratedPost?.value(forKey: &quot;hexColor&quot;) as? String
        let migratedPostID = firstMigratedPost?.value(forKey: &quot;postID&quot;) as? String
        let migratedContent = firstMigratedPost?.value(forKey: &quot;content&quot;) as? String

        XCTAssertEqual(migratedDate?.timeIntervalSince1970, 1547494150.058821)
        XCTAssertEqual(migratedHexColor, &quot;1BB732&quot;)
        XCTAssertEqual(migratedPostID, &quot;FFFECB21-6645-4FDD-B8B0-B960D0E61F5A&quot;)
        XCTAssertEqual(migratedContent, &quot;Test body&quot;)

        tearDownCoreDataStack(context: context)
    }
}</nsmanagedobject></code></pre>
<blockquote>
<p>There is no need to test every object stored in the persistent store rather we just have to assert that each entity has the correct number of objects and then select one object per entity and assert the values on that object.</p>
</blockquote>
<p>In the above test, a migration is triggered between the <code>CoreDataMigration_Example</code> and <code>CoreDataMigration_Example 2</code> models. An interesting point to note is that rather than making use of the <code>Post</code> subclass of <code>NSManagedObject</code>, the above test uses a plain <code>NSManagedObject</code> instance and <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/index.html?ref=williamboles.com"><code>KVC</code></a> to determine if the migration was a success. This is to handle the very likely scenario that the <code>Post</code> structure defined in the <code>CoreDataMigration_Example 2</code> model will not be the final <code>Post</code> structure. If we used <code>Post</code> instances then as the <code>Post</code> entity changed in later versions of the model, those changes would be mirrored in <code>Post</code> <code>NSManagedObject</code> subclass which would result in this test potentially breaking. By using plain <code>NSManagedObject</code> instances and <code>KVC</code> it is possible to ensure that this test is 100% accurate to the structure of the <code>Post</code> entity as defined in <code>CoreDataMigration_Example 2</code> model.</p>
<p>As changes are being made to the file system the last thing the test does is tear down the Core Data stack using the <code>tearDownCoreDataStack(context:)</code> method.</p>
<blockquote>
<p>Just deleting the migrated SQLite files from the file system would result in a rather serious sounding error <code>BUG IN CLIENT OF libsqlite3.dylib: database integrity compromised by API violation: vnode unlinked while in use:....</code> being printed to the console. This is because the store would be being deleted from under an active Core Data stack. While the active Core Data stack in question will then be discarded resulting in this error not actually creating any issues, having it clutter the console would make it that much harder to read it and spot any genuine issues printed there so best to tear things down properly.</p>
</blockquote>
<p>In the above test class there are a few extensions being used to make things easier:</p>
<pre><code class="swift">extension FileManager {

    // MARK: - Temp

    static func clearTempDirectoryContents() {
        let tmpDirectoryContents = try! FileManager.default.contentsOfDirectory(atPath: NSTemporaryDirectory())
        tmpDirectoryContents.forEach {
            let fileURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent($0)
            try? FileManager.default.removeItem(atPath: fileURL.path)
        }
    }

    static func moveFileFromBundleToTempDirectory(filename: String) -&gt; URL {
        let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(filename)
        try? FileManager.default.removeItem(at: destinationURL)
        let bundleURL = Bundle(for: CoreDataMigratorTests.self).resourceURL!.appendingPathComponent(filename)
        try? FileManager.default.copyItem(at: bundleURL, to: destinationURL)

        return destinationURL
    }
}</code></pre>
<pre><code class="swift">extension NSManagedObjectContext {

    // MARK: Model

    convenience init(model: NSManagedObjectModel, storeURL: URL) {
        let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
        try! persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)

        self.init(concurrencyType: .mainQueueConcurrencyType)

        self.persistentStoreCoordinator = persistentStoreCoordinator
    }

    // MARK: - Destroy

    func destroyStore() {
        persistentStoreCoordinator?.persistentStores.forEach {
            try? persistentStoreCoordinator?.remove($0)
            try? persistentStoreCoordinator?.destroyPersistentStore(at: $0.url!, ofType: $0.type, options: nil)
        }
    }
}</code></pre>
<blockquote>
<p>As stated above, I won&apos;t expand on the extension methods.</p>
</blockquote>
<h5 id="migratingtoversion3">Migrating to version 3</h5>
<p>After another successful release, we decide to expand our posting functionality by allowing the user to add multiple sections to a post. These sections will be stored alongside the post in Core Data. As with any model change we need to create a new model version: <code>CoreDataMigration_Example 3</code>.</p>
<p>Each section consists of:</p>
<ul>
<li>A title.</li>
<li>A body.</li>
<li>An index.</li>
</ul>
<p>which in turn reduces a post to:</p>
<ul>
<li>A unique ID.</li>
<li>A random associated colour represented as a hex string.</li>
<li>The date the post was created on.</li>
<li>A collection of sections.</li>
</ul>
<p>Such that:</p>
<p><img src="https://williamboles.com/content/images/2019/01/Screenshot-showing-section-entity-1.png" alt="Progressive Core Data Migrations" loading="lazy"></p>
<p>Migrating from <code>CoreDataMigration_Example 2</code> to <code>CoreDataMigration_Example 3</code> is slightly trickier than the previous migration as <code>CoreDataMigration_Example 2</code> splits an existing entity into two entities and creates a relationship between them. This will require implementing both a mapping model and migration policy.</p>
<blockquote>
<p>To create a mapping model open the <code>File</code> menu on the top bar then click on <code>New File-&gt;New</code>, in the window that opens scroll down to the <code>Core Data</code> section and double tap on <code>Mapping Model</code>. This will open a wizard where you can select your source and destination model versions so in this case: <code>CoreDataMigration_Example 2</code> and <code>CoreDataMigration_Example 3</code>. After that you need to give the mapping a name and save it, I tend to follow <code>Migration[sourceVersion]to[destinationVersion]ModelMapping</code> as a naming convention so <code>Migration2to3ModelMapping</code>.</p>
</blockquote>
<p>A mapping model defines the transformations required to migrate from the source model to the destination model. In Xcode, a mapping model is an <code>xcmappingmodel</code> file that when opened has a GUI that&apos;s very similar to the Core Data Model GUI. A mapping model handles mapping between entities, attributes and relationships. The mapping model GUI even allows for simple transformations. If the model had a <code>percentage</code> attribute that used to have a value between <code>0 - 100</code> but in the new model that value should be between <code>0 - 1</code>, we could use the <code>Expression</code> field on that attribute to perform this transformation by setting the expression to: <code>$source.percentage/100</code>. Despite the range of transformations possible within the mapping model GUI some changes are just too complex and require a more custom approach - this is handled by creating a migration policy. A migration policy is an <code>NSEntityMigrationPolicy</code> subclass that defines how to map between two entities from two different model versions using the full Core-Data/Swift toolkit.</p>
<p>Migrating from <code>CoreDataMigration_Example 2</code> to <code>CoreDataMigration_Example 3</code> will require a custom migration policy as we will need to move the current <code>content</code> attribute&apos;s value on <code>Post</code> to both the <code>title</code> and <code>body</code> attributes on a newly created <code>Section</code> instance:</p>
<pre><code class="swift">final class Post2ToPost3MigrationPolicy: NSEntityMigrationPolicy {

    override func createDestinationInstances(forSource sourcePost: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
        try super.createDestinationInstances(forSource: sourcePost, in: mapping, manager: manager)

        guard let destinationPost = manager.destinationInstances(forEntityMappingName: mapping.name, sourceInstances: [sourcePost]).first else {
            fatalError(&quot;was expected a post&quot;)
        }

        let sourceBody = sourcePost.value(forKey: &quot;content&quot;) as? String
        let sourceTitle = sourceBody?.prefix(4).appending(&quot;...&quot;)

        let section = NSEntityDescription.insertNewObject(forEntityName: &quot;Section&quot;, into: destinationPost.managedObjectContext!)
        section.setValue(sourceTitle, forKey: &quot;title&quot;)
        section.setValue(sourceBody, forKey: &quot;body&quot;)
        section.setValue(destinationPost, forKey: &quot;post&quot;)
        section.setValue(0, forKey: &quot;index&quot;)

        var sections = Set<nsmanagedobject>()
        sections.insert(section)

        destinationPost.setValue(sections, forKey: &quot;sections&quot;)
    }
}</nsmanagedobject></code></pre>
<blockquote>
<p>Just like with mapping models I have a naming convention for migration policies: <code>[Entity][Version]To[Entity][Version]MigrationPolicy</code>, this way I can know at a glance exactly what the migration policy is doing.</p>
</blockquote>
<p>The above migration policy overrides <code>createDestinationInstances(forSource:in:manager)</code> to allow for transforming existing <code>CoreDataMigration_Example 2</code> model <code>Post</code> instances into <code>CoreDataMigration_Example 3</code> model <code>Post</code> and <code>Section</code> instances. Again in order to interact with attributes on each <code>Post</code> instance, we need to use <code>KVC</code>. First, a new <code>CoreDataMigration_Example 3</code> model <code>Post</code> (<code>destinationPost</code>) is created using the mapping rules defined in the mapping model (these rules are set in the mapping model GUI). Then a <code>Section</code> instance from the new <code>Section</code> entity. As the old <code>Post</code> didn&apos;t have the concept of a <code>title</code>, we take the first 4 characters of that older post&apos;s <code>body</code> value and combine it with <code>...</code> so that it can be used as the <code>title</code> of the new <code>Section</code> instance. After setting the other properties of the section, a relationship between this section and the new post is created.</p>
<p>In order for this migration policy to be used during the migration we need to add it to the mapping model by setting the <code>Custom Policy</code> on the <code>PostToPost</code> entity mapping:</p>
<p><img src="https://williamboles.com/content/images/2019/01/Screenshot-showing-custom-policy-3.png" alt="Progressive Core Data Migrations" loading="lazy"></p>
<blockquote>
<p>It&apos;s important to note that the migration policy class name is prefixed with the module name.</p>
</blockquote>
<p>All that&apos;s left to do is to update: <code>CoreDataMigrationVersion</code> by introducing a <code>version3</code> case and updating <code>nextVersion</code>:</p>
<pre><code class="swift">enum CoreDataMigrationVersion: String, CaseIterable {
    case version1 = &quot;CoreDataMigration_Example&quot;
    case version2 = &quot;CoreDataMigration_Example 2&quot;
    case version3 = &quot;CoreDataMigration_Example 3&quot;

    //Omitting methods

    func nextVersion() -&gt; CoreDataMigrationVersion? {
    switch self {
    case .version1:
        return .version2
    case .version2:
        return .version3
    case .version3:
        return nil
    }
}</code></pre>
<p>And that&apos;s it - we now have a migration path from not only <code>CoreDataMigration_Example 2</code> to <code>CoreDataMigration_Example 3</code> but also from <code>CoreDataMigration_Example</code> to <code>CoreDataMigration_Example 3</code>.</p>
<blockquote>
<p>Check out <a href="https://github.com/wibosco/CoreDataMigrationRevised-Example/blob/master/CoreDataMigration-ExampleTests/CoreData/Migration/CoreDataMigratorTests.swift?ref=williamboles.com">CoreDataMigratorTests</a> for the unit test that supports this migration.</p>
</blockquote>
<h5 id="migratingtoversion4">Migrating to version 4</h5>
<p>The success of <code>Colourful Posts</code> knows no bounds and we decide to release our next killer feature: deleting posts. This deletion functionality is actually a soft delete which means that the post will still exist in Core Data but won&apos;t be shown to the user. We can achieve this by adding a new attribute to the <code>Post</code> entity - <code>softDelete</code>. Of course, this change will require a new model version and for us to handle the migration to that version. This migration can be handled as a Lightweight migration and in fact requires very little effort on our part. We only need to add a new case to <code>CoreDataMigrationVersion</code> and update <code>nextVersion</code>:</p>
<pre><code class="swift">enum CoreDataMigrationVersion: String, CaseIterable {
    case version1 = &quot;CoreDataMigration_Example&quot;
    case version2 = &quot;CoreDataMigration_Example 2&quot;
    case version3 = &quot;CoreDataMigration_Example 3&quot;
    case version4 = &quot;CoreDataMigration_Example 4&quot;

    // Omitted methods

    // MARK: - Migration

    func nextVersion() -&gt; CoreDataMigrationVersion? {
        switch self {
        case .version1:
            return .version2
        case .version2:
            return .version3
        case .version3:
            return .version4
        case .version4:
            return nil
        }
    }
}</code></pre>
<blockquote>
<p>Check out <a href="https://github.com/wibosco/CoreDataMigrationRevised-Example/blob/master/CoreDataMigration-ExampleTests/CoreData/Migration/CoreDataMigratorTests.swift?ref=williamboles.com">CoreDataMigratorTests</a> for the unit test that supports this migration.</p>
</blockquote>
<h3 id="wegotthere">We got there &#x1F3C1;</h3>
<p>Core Data migration can often seem like a tedious and cumbersome process that punishes developers for mutating their models. However (hopefully) this post shows that by diverging from the default one-step migration approach we can simplify the process and significantly cut down the amount of work required to perform a successful migration. This simplification makes it much easier to treat our user&apos;s data with the care that I hope others treat my data with.</p>
<hr>
<p>I want to acknowledge that I based the above approach on the migration example shown in the excellent <code>Core Data</code> book by <a href="https://twitter.com/floriankugler?lang=en&amp;ref=williamboles.com">Florian Kugler</a> and <a href="https://twitter.com/danielboedewadt?lang=en&amp;ref=williamboles.com">Daniel Eggert</a> which you can get <a href="https://www.objc.io/books/core-data/?ref=williamboles.com">here</a>. I would highly recommend that you give that book a read as it&apos;s a treasure trove of Core Data knowledge.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Keeping dates local]]></title><description><![CDATA[In our day-to-day life, dates are pretty straight forward - we read them all the time, make plans around them, and share them with other people. Apart from the occasional missed birthday party all of these date-based tasks go surprisingly smoothly - which is remarkable]]></description><link>https://williamboles.com/keeping-dates-local/</link><guid isPermaLink="false">5c03bb095aff9800bfbe0075</guid><category><![CDATA[date & time]]></category><category><![CDATA[dateformatter]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Tue, 11 Dec 2018 20:08:41 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/11/clocks-timezones-min-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2019/11/clocks-timezones-min-2.jpg" alt="Keeping dates local"><p>In our day-to-day life, dates are pretty straight forward - we read them all the time, make plans around them, and share them with other people. Apart from the occasional missed birthday party all of these date-based tasks go surprisingly smoothly. Which is remarkable when you stop to think how complex our date systems are. It works this smoothly because everyone is making some pretty large, unspoken assumptions around the dates that they see - what calendar is used, what timezone is used, the ordering of date elements, etc. While for most of us these assumptions rarely create issues in our day-to-day lives, if we want to build a system that uses dates we need to discover what assumptions we are making and remove them. Take for example the following date:</p>
<p><code>02/12/06</code></p>
<p>If you are from the UK then you would read this as:</p>
<p><strong>2nd of December 2006</strong></p>
<p>whereas if you are from the US then you would read this as:</p>
<p><strong>February 12th, 2006</strong></p>
<p>Both of these interpretations are valid however only one is correct. If the developer is from the UK then the former interpretation is correct and any US users are going to be either confused when they start seeing dates like <code>14/12/06</code> &#x1F615; or angry when they show up to an appointment on the wrong day &#x1F92C;. It&apos;s easy to fall into this date ordering trap especially if everyone in the development and testing teams is working off the same set of date assumptions/conventions - this is the real danger with assumptions, often we don&apos;t know that we are making them.</p>
<h3 id="gettingtoknowwhatslocal">Getting to know what&apos;s local</h3>
<p>When it comes to formatting dates we can choose to take the user&apos;s conventions into account or not, so for example, with the above date example <code>02/12/06</code> if we want to confuse our US users we could hardcode the <em>date element order</em> to match UK conventions by:</p>
<pre><code class="swift">let dateFormatter = DateFormatter()
dateFormatter.dateFormat = &quot;dd/MM/yy&quot;
let formattedDate = dateFormatter.string(from: date)</code></pre>
<p>It&apos;s not uncommon to see this approach to configuring a <code>DateFormatter</code> and while there are valid scenarios for hardcoding the <code>dateFormat</code> value (we will see an example of that later) I can&apos;t think of any valid scenarios for doing so when displaying a date to a user. By hardcoding the <code>dateFormat</code> value we are instructing the <code>DateFormatter</code> to ignore the date element ordering for the user&apos;s conventions and instead use the same element ordering for all users. By not meeting our user&apos;s date element ordering expectations we degrade that users experience by making something that should be as simple as reading a date into a surprise maths puzzle &#x1F4D6;. I&apos;ve seen a number of novel but naive approaches on how to solve the expectation mismatch a hardcoded <code>dateFormat</code> produces. These solutions tend to centre on two approaches:</p>
<ol>
<li>Add conditional logic and set a <code>dateFormat</code> value that&apos;s unique for each user&apos;s conventions.</li>
<li>Move the <code>dateFormat</code> value into the localisation <code>.strings</code> file.</li>
</ol>
<p>While it&apos;s possible to meet our user&apos;s expectations with either of these solutions both have two major drawbacks:</p>
<ol>
<li>The developer needs to actively decide which conventions each <code>DateFormatter</code> will support and.</li>
<li>The developer needs to take on the responsibility of ensuring that any date and time formatting rules are correct for each supported set of conventions.</li>
</ol>
<p>In order to define the formatting rules for each <code>dateFormat</code> value, the developer would need to answer questions such as:</p>
<ul>
<li>Which calendar to use?</li>
<li>Which clock to use: <a href="https://en.wikipedia.org/wiki/24-hour_clock?ref=williamboles.com">24-hour</a> or <a href="https://en.wikipedia.org/wiki/12-hour_clock?ref=williamboles.com">12-hour</a>?</li>
<li>What is the ordering of date elements?</li>
<li>What is the date separator character(s)?</li>
</ul>
<p>And the list goes on.</p>
<p>Answering all these questions correctly is a <strong>massive</strong> task and as it turns out, a totally unnecessary one as iOS already answers them (and many more besides) for us. In iOS, the linguistic, cultural and technological conventions are collected within the <a href="https://developer.apple.com/documentation/foundation/locale?ref=williamboles.com"><code>Locale</code></a> class (each locale&apos;s conventions are provided by the <a href="http://cldr.unicode.org/?ref=williamboles.com">Unicode Common Locale Data Repository</a> (CLDR) project). The conventions defined within each locale should match our user&apos;s expectations for how their world is measured and represented; as such <code>Locale</code> plays a vital role in making our users comfortable when using our apps to complete their tasks. By working with the user&apos;s locale conventions, it&apos;s possible to produce formatted dates that match our user&apos;s expectations. Even better than just improving our user&apos;s in-app experience, is that this can all be achieved without actually having to know or care what those locale conventions actually are.</p>
<blockquote>
<p>While each locale comes with default conventions, some of them can be customised by the user e.g. switching from 12-hour to 24-hour clock representation. So even if two users have the same locale, it would be a mistake to assume these locales where identical.</p>
</blockquote>
<p><img src="https://williamboles.com/content/images/2019/11/clocks-timezones-min-3.jpg" alt="Keeping dates local" loading="lazy"></p>
<blockquote>
<p>For the rest of this post I&apos;m going to assume your locale is the default <code>US</code> locale. To keep the examples comparable, each will use a date based off of the Unix epoch timestamp: <code>1165071389</code> which equates to <code>2nd of December, 2006 at 14:56:29</code>. You can see the completed playground with all of the below examples by following this <a href="https://github.com/wibosco/AvoidingFixedStringDateFormats-Example?ref=williamboles.com">link</a>.</p>
</blockquote>
<h3 id="stayinglocal">Staying local</h3>
<p>When it comes to presenting date information to the user, it&apos;s best to leave all locale concerns to <code>DateFormatter</code> - there are two ways to do this:</p>
<ol>
<li>Using <code>DateFormatter.Style</code></li>
<li>Using a template</li>
</ol>
<p>Let&apos;s explore both these approaches in more detail.</p>
<h6 id="usingdateformatterstyle">Using <code>DateFormatter.Style</code></h6>
<p>One way to ensure that a <code>Date</code> is always shown to the user using a formatting style that they are expecting is to use the <code>DateFormatter.Style</code> enum. The <code>DateFormatter.Style</code> enum is a set of predefined values that correspond to a date format, at the time of writing (iOS 12) this enum has <strong>5</strong> possible cases to choose from:</p>
<ol>
<li><code>none</code></li>
<li><code>short</code></li>
<li><code>medium</code></li>
<li><code>long</code></li>
<li><code>full</code></li>
</ol>
<p>Each case will take into account the user&apos;s locale settings when formatting dates. <code>DateFormatter</code> has two <code>DateFormatter.Style</code> properties: <code>dateStyle</code> and <code>timeStyle</code>. Each property works semi-independently of the other and as such each can take a different value - this flexibility allows for a wide range of formatting options (<strong>25</strong> possible permutations):</p>
<pre><code class="swift">let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
dateFormatter.timeStyle = .medium
let formattedDate = dateFormatter.string(from: date)</code></pre>
<p><code>formattedDate</code> would be set to <code>Saturday, December 2, 2006 at 2:56:29 PM</code>.</p>
<p>In the above example, we can see the user&apos;s locale influencing the <code>formattedDate</code> value in a number of ways:</p>
<ol>
<li>The date format ordering - <code>EEEE, MMMM d, y &apos;at&apos; h:m:s a</code>.</li>
<li>The names of the months and days.</li>
<li>The separator between the various date elements - <code>/</code> for calendar date elements and <code>:</code> for time date elements.</li>
</ol>
<p>It&apos;s also interesting to note that <code>DateFormatter</code> is handling combining the calendar date and time elements together into a sentence structure that is appropriate for both <code>DateFormatter.Style</code> values. So in the above example <code>at</code> is being used as a connector between those two elements however if <code>dateStyle</code> was changed to <code>.short</code> then <code>at</code> becomes <code>,</code> and we get <code>12/2/06, 2:56:29 PM</code> as the formatted date should be shown in a more compacted form -  pretty powerful stuff &#x1F92F;.</p>
<p>To demonstrate the power of this further, if we set the <code>locale</code> to <code>Locale(identifier: &quot;de_DE&quot;)</code> the above <code>.full</code> example becomes <code>Samstag, 2. Dezember 2006 um 14:56:29</code> and the <code>.short</code> example becomes <code>02.12.06, 14:56:29</code> - all this localisation without me having to know anything about German date conventions or even anything about the German language - &#x1F92F; in &#x1F1E9;&#x1F1EA;.</p>
<p>Another way to use the <code>DateFormatter.Style</code> approach is by using the available class method:</p>
<pre><code class="swift">let formattedDate = DateFormatter.localizedString(from: date, dateStyle: .short, timeStyle: .medium)</code></pre>
<p><code>formattedDate</code> has the same value as the property based approach: <code>12/2/06, 2:56:29 PM</code>.</p>
<p>When deciding which option to use I tend to favour the property(s) approach when I either need to cache the <code>DateFormatter</code> instance being used (see <a href="https://williamboles.com/sneaky-date-formatters-exposing-more-than-you-think/">&quot;Sneaky date formatters exposing more than you think&quot;</a> for more details on how to cache date formatters) or further customise it (outside of just the <code>dateStyle</code> and/or <code>timeStyle</code> properties). For all other scenarios, I favour the class method approach as I think it&apos;s easier to reason about and reads better (from the method name I know that I&apos;m getting a localised string value back).</p>
<blockquote>
<p>This is a helpful <a href="http://blog.chrishannah.me/dates-and-dateformatters-in-swift/?ref=williamboles.com">cheatsheet</a> showing the output for each <code>DateFormatter.Style</code> case. To see the conventions used in different locales set the <code>locale</code> property for the <code>DateFormatter</code> instance to the locale you are interested in e.g. <code>dateFormatter.locale = Locale(identifier: &quot;en_CA&quot;)</code>.</p>
</blockquote>
<p>Using <code>DateFormatter.Style</code> works really well if you to want to display a date in one of the available formats but if you want to display a custom format you need to go down a different path.</p>
<h6 id="usingatemplate">Using a template</h6>
<p>A <code>template</code> is a customised instruction to <code>DateFormatter</code> of the date elements that the formatted date should contain - these date elements are specified using the same symbols as used with a fixed string date format approach: <code>d</code>, <code>MM</code>, <code>y</code> etc. The beauty of the <code>template</code> approach is that it will take this customised instruction and produce a formatted date that takes into account the user&apos;s locale when displaying those elements. For example to produce a formatted date only containing the <em>day</em> and <em>month</em>, the template could look like:</p>
<pre><code class="swift">let dateFormatter = DateFormatter()
dateFormatter.setLocalizedDateFormatFromTemplate(&quot;d MM&quot;)
let formattedDate = dateFormatter.string(from: date)</code></pre>
<p><code>formattedDate</code> would be set to <code>12/2</code>.  The more eagle-eyed among you will have spotted that two transformations have occurred here, the first is that a locale-specific separator was added between the date elements and that the date elements have switched positions from the ordering in the template i.e. from <code>d MM</code> to <code>MM d</code> &#x1F680;.</p>
<blockquote>
<p>It&apos;s important to note that when defining the above template I added a space between the different date elements, strictly speaking this space wasn&apos;t needed - <code>dMM</code> would have resulted in the same <code>formattedDate</code> value. I included the space here only to make the template easier to read.</p>
</blockquote>
<p>Just like with the <code>DateFormatter.Style</code>, the template approach has both an instance and class interface:</p>
<pre><code class="swift">let localisedDateFormat = DateFormatter.dateFormat(fromTemplate: &quot;d MM&quot;, options: 0, locale: Locale.current)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = localisedDateFormat
let formattedDate = dateFormatter.string(from: date)</code></pre>
<p>Again, <code>formattedDate</code> would be set to <code>12/2</code>. The class approach is little more verbose than the instance approach but could be more useful if you wanted to pass the localised formatting string around rather a <code>DateFormatter</code> instance.</p>
<h3 id="goingremote">Going remote</h3>
<p>You may be thinking at this point:</p>
<p><strong>&quot;Is using <code>dateFormat</code> with a fixed string ever the correct approach?&quot;</strong></p>
<p>The simple answer:</p>
<p><strong>Yes</strong>.</p>
<p>The date formatting examples shown so far have been concerned with presenting a formatted date to the user. However as iOS developers we (often) have another consumer of our date data: <em>the backend</em>. Typically the backend will be expecting all date values to be sent using a fixed, locale-neutral format. In order to ensure that the user&apos;s locale does not affect these dates when converting from a <code>Date</code> instance to the formatted date value we need to take full control of our <code>DateFormatter</code> instances and hardcode the <code>locale</code>, <code>timeZone</code> and <code>dateFormat</code> properties to match the backend&apos;s expectations:</p>
<pre><code class="swift">let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
dateFormatter.timeZone = TimeZone(identifier: &quot;UTC&quot;)
dateFormatter.dateFormat = &quot;yyyy-MM-dd&apos;T&apos;HH:mm:ss&apos;Z&apos;&quot;
let formattedDate = dateFormatter.string(from: date)</code></pre>
<p><code>formattedDate</code> would be set to <code>2006-12-02T14:56:29Z</code> regardless of the user&apos;s locale settings. An interesting side effect of setting the <code>locale</code> property is that the formatter&apos;s <code>calendar</code> property is always set to the default calendar for that locale (which for this local is the <a href="https://en.wikipedia.org/wiki/Gregorian_calendar?ref=williamboles.com">Gregorian calendar</a>). It&apos;s important to note that <code>en_US_POSIX</code> is not the same locale as <code>en_US</code> - <code>en_US_POSIX</code> while based off of <code>en_US</code> is a special locale that isn&apos;t tied to any country/region so shouldn&apos;t change even if <code>en_US</code> does change.</p>
<blockquote>
<p>You may have noticed that the <code>dateFormatter</code> is using the <a href="https://en.wikipedia.org/wiki/ISO_8601?ref=williamboles.com">ISO 8601</a> string format, in this case (and if your project has a base iOS version of at least iOS 10) I would recommend using <code>ISO8601DateFormatter</code> instead of the more generic <code>DateFormatter</code> class.</p>
</blockquote>
<h3 id="lettinggoofsomecontrol">Letting go of (some) control</h3>
<p>When displaying a date in our apps, it makes sense to do so in a format that the user is expecting and can easily understand- this isn&apos;t as simple as it first seems. Thankfully, <code>DateFormatter</code> has two very straight forward ways to achieve this:</p>
<ol>
<li>Using <code>DateFormatter.Style</code></li>
<li>Using a template</li>
</ol>
<p>Both approaches work well and require very little effort to solve what is a real iceberg of a problem. The only thing it costs us is that we need to give up a little bit of control on exactly how the date is formatted - that&apos;s a price I&apos;m happy to pay to ensure that my users are shown dates in the most convenient format for them and I don&apos;t need to think about which calendar a certain locale uses or if month comes before day, etc.</p>
<p>Now I just need to convince those pixel-perfect designers on my team that giving up some control is actually a good thing... &#x1F605;</p>
<blockquote>
<p>If you are interested, there is an accompanying <a href="https://github.com/wibosco/AvoidingFixedStringDateFormats-Example?ref=williamboles.com">playground</a> that contains all of the above code snippets.</p>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Sneaky date formatters exposing more than you think]]></title><description><![CDATA[Ask any iOS developer about DateFormatter and one of the first things you will hear is that creating a DateFormatter instance is an expensive operation. The second thing you will hear is that after creating one you need to cache it]]></description><link>https://williamboles.com/sneaky-date-formatters-exposing-more-than-you-think/</link><guid isPermaLink="false">5ba26cd3dced3400bfc255fe</guid><category><![CDATA[date & time]]></category><category><![CDATA[dateformatter]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Mon, 29 Oct 2018 15:28:26 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/11/close-up-watch-min.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2019/11/close-up-watch-min.jpg" alt="Sneaky date formatters exposing more than you think"><p>Ask any iOS developer about <code>DateFormatter</code> and one of the first things you will hear is that creating a <code>DateFormatter</code> instance is an expensive operation. The second thing you will hear is that after creating one you need to cache it.</p>
<p>In this post, I want to look at how expensive it is to create <code>DateFormatter</code> instances and how we can cache them effectively.</p>
<p>To determine just how expensive they are we will need to conduct a small experiment &#x1F52C;.</p>
<blockquote>
<p>If you&apos;d prefer not to have to manually build up the below examples, you can download the completed playground <a href="https://github.com/wibosco/ImmutableDateFormatters-Example?ref=williamboles.com">here</a>.</p>
</blockquote>
<h3 id="theexpenseexperiment">The expense experiment</h3>
<p>Our experiment will have two scenarios:</p>
<ol>
<li>Create a new instance of <code>DateFormatter</code> for each date conversion.</li>
<li>Reuse the same instance of <code>DateFormatter</code> for all date conversions.</li>
</ol>
<pre><code class="swift">class DateConverter {

    let dateFormat = &quot;y/MM/dd @ HH:mm&quot;

    func convertDatesWithUniqueFormatter(_ dates: [Date]) {
        for date in dates {
            let dateFormatter = DateFormatter()
            dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
            dateFormatter.dateFormat = dateFormat

            _ = dateFormatter.string(from: date)
        }
    }

    func convertDatesWithReusedFormatter(_ dates: [Date]) {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
        dateFormatter.dateFormat = dateFormat

        for date in dates {
            _ = dateFormatter.string(from: date)
        }
    }
}</code></pre>
<p><code>convertDatesWithUniqueFormatter</code> represents the first scenario and <code>convertDatesWithReusedFormatter</code> the second scenario. Both methods follow a similar structure - loop over an array of dates and format each date into a string representation, the only difference is in how <code>DateFormatter</code> is used.</p>
<p>The <code>XCTest</code> framework makes it straight forward to measure the performance of our code using the appropriately named <a href="https://developer.apple.com/documentation/xctest/xctestcase/1496290-measure?ref=williamboles.com"><code>measure(_:)</code></a> method. <code>measure(_:)</code> tracks the time in seconds that it takes for the code under test to finish executing. As it&apos;s possible for factors outside of our control to affect performance, <code>measure(_:)</code> executes the same test 10 times and reports the average time as its result &#x23F2;&#xFE0F;.</p>
<pre><code class="swift">class DateConverterTests: XCTestCase {
    var sut: DateConverter!
    let dates = Array(repeating: Date(), count: 100)

    // MARK: - Lifecycle

    override func setUp() {
        super.setUp()
        sut = DateConverter()
    }

    override func tearDown() {
        sut = nil
        super.tearDown()
    }

    // MARK: - Tests

    func test_convertDatesWithUniqueFormatter_performance() {

        measure {
            sut.convertDatesWithUniqueFormatter(dates)
        }
    }

    func test_convertDatesWithReusedFormatter_performance() {

        measure {
            sut.convertDatesWithReusedFormatter(dates)
        }
    }
}</code></pre>
<p>Running the two above tests results in generating the following console output:</p>
<p><img src="https://williamboles.com/content/images/2018/10/Screenshot-performance-execution-result.png" alt="Sneaky date formatters exposing more than you think" loading="lazy"></p>
<blockquote>
<p>Of course running the tests on your computer will give you different results.</p>
</blockquote>
<p>The above results show that when converting 100 dates, reusing a <code>DateFormatter</code> instance takes on average <strong>20%</strong> (0.02 vs 0.004 seconds) of the time that creating a unique <code>DateFormatter</code> instance for each conversion does. While in real terms the difference in time isn&apos;t much, as a percentage the difference is pretty conclusive - reusing a <code>DateFormatter</code> instance is <strong>5x cheaper</strong>. If we are thinking in terms of UX, the additional overhead from always creating a new instance of <code>DateFomatter</code> could be the difference between a smooth scrolling table view and a <em>jumpy</em> one.</p>
<blockquote>
<p>Premature optimisation is the mother of many bugs so before deciding to improve the performance of any one area, it&apos;s important to make sure that that area is actually a performance bottleneck - <code>Instruments</code> and <code>Xcode</code> itself are great tools for profiling. By only optimising actual performance bottlenecks we can ensure that we are not wasting time and not unnecessarily making our codebase more complex than it needs to be.</p>
</blockquote>
<p><img src="https://williamboles.com/content/images/2019/11/close-up-watch-min-1.jpg" alt="Sneaky date formatters exposing more than you think" loading="lazy"></p>
<h3 id="workingwithperformantdateformatters">Working with performant DateFormatters</h3>
<p>Now that it&apos;s been determined that reusing a <code>DateFormatter</code> instance offers a performance improvement and we&apos;ve identified that this performance improvement will lead to a better user experience, the question is:</p>
<p>&quot;How do we reuse it?&quot;</p>
<p>It&apos;s simple enough to extract the <code>DateFormatter</code> instance into a local variable or private property so it can be reused and I won&apos;t explore these here - things really start to get interesting when the same formatter is needed across the project.</p>
<p>While normally derided as an overused pattern, I think sharing formatters would be greatly served by using the <a href="https://en.wikipedia.org/wiki/Singleton_pattern?ref=williamboles.com">Singleton</a> pattern:</p>
<pre><code class="swift">class DateFormattingHelper {

    // MARK: - Shared

    static let shared = DateFormattingHelper()

    // MARK: - Formatters

    let dobDateFormatter: DateFormatter = {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
        dateFormatter.dateFormat = &quot;y/MM/dd @ HH:mm&quot;

        return dateFormatter
    }()
}</code></pre>
<p>If we then wanted to format <code>Date</code> instances into <em>date-of-birth</em> strings, we could add a method similar to:</p>
<pre><code class="swift">func formatDOB(_ date: Date, with dateFormatter: DateFormatter) -&gt; String {
    let formattedDate = dateFormatter.string(from: date)
    return (&quot;Date of birth: \(formattedDate)&quot;)
}</code></pre>
<p>And when it&apos;s called, pass in the shared <code>DateFormatter</code> instance that is a property on <code>DateFormattingHelper</code> singleton like:</p>
<pre><code class="swift">let dateFormatter = DateFormattingHelper.shared.dobDateFormatter
let dobFormattedString = formatDOB(date, with: dateFormatter)</code></pre>
<p>It&apos;s a simple, easy to read and easy to test approach that ensures that <code>DateFormatter</code> instances are being reused. It&apos;s not too hard to imagine adding more <code>DateFormatter</code> properties to <code>DateFormattingHelper</code> and more helper methods like <code>formatDOB</code> to our view controllers to meet all of our date formatting needs.</p>
<p>But before we get too carried away with our success, let&apos;s examine our use of the <code>Singleton</code> pattern here in more detail. A key design consideration when using any pattern to expose shared state is: <em>mutability</em>. Ideally shared state should be immutable to prevent a situation where different parts of the codebase can change that state and so indirectly affect other parts that depend on that shared state. However, as we have seen <code>DateFormattingHelper</code> only has one property and that property is a constant (<code>let</code>) so mutability shouldn&apos;t be an issue here &#x1F44F;.</p>
<p><strong>Or is it?</strong> &#x1F914;</p>
<p>Swift has two categories of Type:</p>
<ol>
<li>Value - <code>structs</code>, <code>enums</code>, or <code>tuples</code></li>
<li>Reference - <code>classes</code>, <code>functions</code> or <a href="https://docs.swift.org/swift-book/LanguageGuide/Closures.html?ref=williamboles.com#ID104"><code>closures</code></a></li>
</ol>
<p>The main difference between the types is shown when they are used in an assignment.</p>
<p>When a <em>value-type</em> instance is assigned, an exact copy of that instance is made and it is this copy that is then assigned. This results in two instances of that value-type existing, with both instances (at least initially) having the same property values:</p>
<pre><code class="swift">struct PersonValueTypeExample: CustomStringConvertible {
    var name: String
    var age: Int

    var description: String {
        return (&quot;name: \(name), age: \(age)&quot;)
    }
}

var a = PersonValueTypeExample(name: &quot;Susie&quot;, age: 29)
var b = a
b.name = &quot;Samantha&quot;
a.age = 56

print(&quot;a: \(a)&quot;) // prints &quot;a: name: Susie, age: 56&quot;
print(&quot;b: \(b)&quot;) // prints &quot;b: name: Samantha, age: 29&quot;</code></pre>
<blockquote>
<p><code>CustomStringConvertible</code> is used here only to override the <code>description</code> property and ensure a consistent printout format between the value-type and reference-type examples.</p>
</blockquote>
<p>As the above example shows, when <code>a</code> is assigned to <code>b</code>, a copy of <code>a</code> is made and assigned to <code>b</code>. Any change made to either <code>a</code> or <code>b</code> will be limited to that instance only - this can be seen in the description string printed for both.</p>
<blockquote>
<p>Copying a value-type instance can be an expensive process so a number of techniques are used to try and avoid making the copy. These techniques range from compiler optimisations to implementation details in the struct itself such as <a href="https://medium.com/@lucianoalmeida1/understanding-swift-copy-on-write-mechanisms-52ac31d68f2f?ref=williamboles.com">Copy-On-Write</a>. These optimisation techniques can result in our value-type instances behaving more like reference-types under the hood. While this is useful to know about, the important thing to note is that these optimisations are transparent to us as users of these value-types. So we can build a system which is dependent upon the difference between value and reference types without having to care how the instances of these types are actually stored in memory and accessed.</p>
</blockquote>
<p>When a <em>reference-type</em> instance is assigned, a reference (or pointer) to that instance is created and it is this reference that is then actually assigned (rather than the instance itself). This can result in an instance having multiple references so that the instance becomes an <em>informal shared</em> instance to those references:</p>
<pre><code class="swift">class PersonReferenceTypeExample: CustomStringConvertible {
    var name: String
    var age: Int

    var description: String {
    return (&quot;name: \(name), age: \(age)&quot;)
    }

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let c = PersonReferenceTypeExample(name: &quot;Susie&quot;, age: 29)
var d = c
d.name = &quot;Samantha&quot;
c.age = 56

print(&quot;c: \(c)&quot;) // prints &quot;c: name: Samantha, age: 56&quot;
print(&quot;d: \(d)&quot;) // prints &quot;d: name: Samantha, age: 56&quot;</code></pre>
<p>As the above example shows when <code>c</code> is assigned to <code>d</code>, a new reference to the same instance that <code>c</code> is pointing at is created and assigned to <code>d</code>. Any change made to either <code>c</code> or <code>d</code> will affect the other - this can be seen in the description string printed for both.</p>
<p>The sharper eyed among you may have spotted that the above examples differ in one additional aspect other than just <code>class</code>/<code>struct</code> and the naming of the variables - in the struct example <code>a</code> is defined as a <code>var</code> whereas in the class example <code>c</code> is a <code>let</code>. When a value-type instance is assigned to a constant, all of its properties are treated as constant properties (and any <code>mutating</code> methods are no longer available) - this isn&apos;t true for reference-type instances where <code>var</code> and <code>let</code> are always respected.</p>
<blockquote>
<p>You may have heard of <em>pass-by-reference</em> and <em>pass-by-value</em> and currently be thinking that these terms are connected to the above types. This is a common misunderstanding, by default Swift uses <em>pass-by-value</em> when passing both <em>value-type</em> and <em>reference-type</em> instances. The only difference as we have seen is what is copied - with a <em>value-type</em> it&apos;s the entire instance, with a <em>reference-type</em> instance it&apos;s a reference/pointer. Why this is the case took me a while to fully get my head around, I found the following Stack Overflow <a href="https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value?ref=williamboles.com">question</a> very informative, as well as the wiki page on <a href="https://en.wikipedia.org/wiki/Evaluation_strategy?ref=williamboles.com">Evaluation Strategy</a> especially the <a href="https://en.wikipedia.org/wiki/Evaluation_strategy?ref=williamboles.com#Call_by_sharing">&quot;Call by sharing&quot;</a> section which I think best describes what happens when we pass a reference-type into a method and why that isn&apos;t pass-by-reference. Using the linked wiki definition of <em>pass-by-reference</em>, the nearest that Swift gets to it is using the <code>inout</code> keyword.</p>
</blockquote>
<p>While an interesting detour, you may be thinking:</p>
<p>&quot;What does any of it have to do with <code>DateFormattingHelper</code>?&quot;</p>
<p>Well, <code>dobDateFormatter</code> is a reference-type so when passing it around, new references are being made that point to the same shared <code>DateFomatter</code> instance. On first glance that&apos;s fine because <code>dobDateFormatter</code> is a constant property and so is immutable however the trouble lays in that some of the properties on <code>DateFormatter</code> are mutable. As we seen above (unlike a value-type instance) a reference-type instance&apos;s property declarations are not affected by how that instance was defined. So while it looks like in <code>DateFormattingHelper</code> we have immutable shared state we <em>actually</em> have mutable shared state - just hidden one layer deeper.</p>
<p>A bug that is introduced by changing shared state is often discovered not where the shared state is changed but elsewhere in the codebase where the previous state of the shared state was expected - think about what the consequences of changing the <code>badgeFormat</code> value on <code>dobDateFormatter</code> would be. Even then the other part of the app will only surface the bug if the code that introduces the bug is called first i.e. the bug is dependent on how the user moves through the app &#x1F631;. A nightmare of a bug that can take a long time to track down and solve. We can avoid the many sleepless nights this bug would cause by turning hidden mutable properties into immutable properties.</p>
<p>Going from mutable to immutable is actually surprisingly straightforward in this example, we just need to hide our <code>DateFomatter</code> instances behind a protocol. When it comes to using the <code>dobDateFormatter</code> property above from outside the <code>DateFormattingHelper</code> class we are actually only interested in using a <code>DateFomatter</code> to convert a <code>Date</code> into a <code>String</code> so we can produce an extremely limited protocol:</p>
<pre><code class="swift">protocol DateFormatterType {
    func string(from date: Date) -&gt; String
}</code></pre>
<p>We just need to conform <code>DateFomatter</code> to our custom <code>DateFormatterType</code> using an extension:</p>
<pre><code class="swift">extension DateFormatter: DateFormatterType { }</code></pre>
<p>Now that we have this protocol we can return <code>DateFomatter</code> instances wrapped up as <code>DateFormatterType</code> instance:</p>
<pre><code class="swift">let dobDateFormatter: DateFormatterType = {
    let dateFormatter = DateFormatter()
    dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
    dateFormatter.dateFormat = &quot;y/MM/dd @ HH:mm&quot;

    return dateFormatter
}()</code></pre>
<p>So whenever we use <code>DateFomatter</code> we would use <code>DateFormatterType</code> instead so:</p>
<pre><code class="swift">func formatDOB(_ date: Date, with dateFormatter: DateFormatterType) -&gt; String {
    let formattedDate = dateFormatter.string(from: date)
    return (&quot;Date of birth: \(formattedDate)&quot;)
}</code></pre>
<p>In fact we can take this even further by absorbing the <code>formatDOB</code> method into <code>DateFormattingHelper</code> and hide <code>DateFormatterType</code> altogether:</p>
<pre><code class="swift">private protocol DateFormatterType {
    func string(from date: Date) -&gt; String
}

class DateFormattingHelper {

    // MARK: - Shared

    static let shared = DateFormattingHelper()

    // MARK: - Formatters

    private let dobDateFormatter: DateFormatterType = {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
        dateFormatter.dateFormat = &quot;y/MM/dd @ HH:mm&quot;

        return dateFormatter
    }()

    // MARK: - DOB

    func formatDOBDate(_ date: Date) -&gt; String {
        let formattedDate = dobDateFormatter.string(from: date)
        return (&quot;Date of birth: \(formattedDate)&quot;)
    }
}</code></pre>  
<p><code>DateFormatterType</code> and <code>dobDateFormatter</code> are now private and so only accessible from inside <code>DateFormattingHelper</code>. At this point, you may be thinking:</p>
<p>&quot;What&apos;s the point of still having the <code>DateFormatterType</code> protocol?&quot;</p>
<p>I think it&apos;s important to understand that the issue that <code>DateFormatterType</code> is solving isn&apos;t one of malicious intent from someone deliberately trying to crash the app but from a trusted colleague who through either clumsiness or a lack of domain knowledge, accidentally misuses a <code>DateFormatter</code> instance. So if we simply made the <code>dobDateFormatter</code> property private, it would still be possible to introduce this type of bug into <code>DateFormattingHelper</code> (by modifying the properties of e.g. <code>dobDateFormatter</code> from inside <code>formatDOBDate</code>) however by continuing to use the protocol approach we can actually reduce this risk further. So by being both private and hidden behind a protocol, we are protecting against unintended mutations of a shared instance both external and internal to <code>DateFormattingHelper</code>.</p>
<h3 id="goingastepbeyond">Going a step beyond</h3>
<p>Now that we have the basic mechanisms in place, lets look at a more complete version of <code>DateFormattingHelper</code>:</p>
<pre><code class="swift">class DateFormattingHelper {

    // MARK: - Shared

    static let shared = DateFormattingHelper()

    // MARK: - Formatters

    private let dobDateFormatter: DateFormatterType = {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
        dateFormatter.dateFormat = &quot;y/MM/dd @ HH:mm&quot;

        return dateFormatter
    }()

    private let dayMonthTimeDateFormatter: DateFormatterType = {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
        dateFormatter.dateFormat = &quot;dd MMM @ HH:mm&quot;

        return dateFormatter
    }()

    private let hourMinuteDateFormatter: DateFormatterType = {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
        dateFormatter.dateFormat = &quot;HH:mm&quot;

        return dateFormatter
    }()

    private let dayMonthYearDateFormatter: DateFormatterType = {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
        dateFormatter.dateFormat = &quot;d MMM &apos;of&apos; y&quot;

        return dateFormatter
    }()

    // MARK: - DOB

    func formatDOBDate(_ date: Date) -&gt; String {
        let formattedDate = dobDateFormatter.string(from: date)
        return (&quot;Date of birth: \(formattedDate)&quot;)
    }

    // MARK: - Account

    func formatLastActiveDate(_ date: Date, now: Date = Date()) -&gt; String {
        let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)!

        var dateFormatter = dayMonthTimeDateFormatter
        if date &gt; yesterday {
            dateFormatter = hourMinuteDateFormatter
        }

        let formattedDate = dateFormatter.string(from: date)
        return (&quot;Last active: \(formattedDate)&quot;)
    }

    // MARK: - Post

    func formatPostCreatedDate(_ date: Date) -&gt; String {
        let formattedDate = dayMonthYearDateFormatter.string(from: date)
        return formattedDate
    }

    // MARK: - Commenting

    func formatCommentedDate(_ date: Date) -&gt; String {
        let formattedDate = dayMonthTimeDateFormatter.string(from: date)
        return (&quot;Comment posted: \(formattedDate)&quot;)
    }
}</code></pre>
<p>This gives a better idea of what <code>DateFormattingHelper</code> can become. However, you can quickly see how just adding three more properties has bloated this class. There is room for further optimisation here.</p>
<p>In the above example the only configuration difference between the <code>DateFomatter</code> instances is the <code>dateFormat</code> value. This shared configuration can be used to our advantage to remove the distinct <code>DateFormatter</code> properties:</p>
 <pre><code class="swift">class CachedDateFormattingHelper {

    // MARK: - Shared

    static let shared = CachedDateFormattingHelper()
    
    // MARK: - Queue
    
    let cachedDateFormattersQueue = DispatchQueue(label: &quot;com.boles.date.formatter.queue&quot;)

    // MARK: - Cached Formatters

    private var cachedDateFormatters = [String : DateFormatterType]()

    private func cachedDateFormatter(withFormat format: String) -&gt; DateFormatterType {
        return cachedDateFormattersQueue.sync {
            let key = format
            if let cachedFormatter = cachedDateFormatters[key] {
                return cachedFormatter
            }
            
            let dateFormatter = DateFormatter()
            dateFormatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
            dateFormatter.dateFormat = format
            
            cachedDateFormatters[key] = dateFormatter
            
            return dateFormatter
        }
    }

    // MARK: - DOB

    func formatDOBDate(_ date: Date) -&gt; String {
        let dateFormatter = cachedDateFormatter(withFormat: &quot;y/MM/dd @ HH:mm&quot;)
        let formattedDate = dateFormatter.string(from: date)
        return (&quot;Date of birth: \(formattedDate)&quot;)
    }

    // MARK: - Account

    func formatLastActiveDate(_ date: Date, now: Date = Date()) -&gt; String {
        let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)!

        var dateFormatter = cachedDateFormatter(withFormat: &quot;dd MMM @ HH:mm&quot;)
        if date &gt; yesterday {
            dateFormatter = cachedDateFormatter(withFormat: &quot;HH:mm&quot;)
        }

        let formattedDate = dateFormatter.string(from: date)
        return (&quot;Last active: \(formattedDate)&quot;)
    }

    // MARK: - Post

    func formatPostCreatedDate(_ date: Date) -&gt; String {
        let dateFormatter = cachedDateFormatter(withFormat: &quot;d MMM &apos;of&apos; y&quot;)
        let formattedDate = dateFormatter.string(from: date)
        return formattedDate
    }

    // MARK: - Commenting

    func formatCommentedDate(_ date: Date) -&gt; String {
        let dateFormatter = cachedDateFormatter(withFormat: &quot;dd MMM @ HH:mm&quot;)
        let formattedDate = dateFormatter.string(from: date)
        return (&quot;Comment posted: \(formattedDate)&quot;)
    }
}</code></pre>
<p>Above we have added a private dictionary (<code>cachedDateFormatters</code>) where all the <code>DateFormatter</code> instances are stored and an accessor method (<code>cachedDateFormatter</code>) to determine if a new instance of <code>DateFormatter</code> needs to be created or not based on if it exists in that dictionary, with the <code>dateFormat</code> value itself acting as the key.</p>
<p>Setting or getting a cached <code>DateFormatter</code> instance has been wrapped in a <code>sync</code> operation on its own queue to make using <code>cachedDateFormatter</code> thread safe - effectively, the <code>cachedDateFormattersQueue</code> queue here is acting as lock.</p>
<blockquote>
<p>Credit for the above dictionary approach must be given to Jordon Smith, who describes it in this <a href="http://jordansmith.io/performant-date-parsing/?ref=williamboles.com">post</a>.</p>
</blockquote>
<p>Of course, if further customisation is needed in each of the formatters this approach isn&apos;t suitable, however for this example I think it offers a very elegant solution.</p>
<blockquote>
<p>In the <a href="https://github.com/wibosco/ImmutableDateFormatters-Example?ref=williamboles.com">completed playground</a> accompanying this post, I&apos;ve added the same tests to <code>CachedDateFormattingHelper</code> that I use for <code>DateFormattingHelper</code> to show that externally both implementations behave the same way.</p>
</blockquote>
<h3 id="lookingbackatthosedates">Looking back at those dates</h3>
<p>In this post we questioned the common belief that creating <code>DateFormatter</code> instances is expensive (yes they are), looked at ways that we could avoid paying too much for them (local variables, properties and singletons), delved deep on why exposing shared <code>DateFormatter</code> instances was a bad idea (unexpected shared mutable state), how we could avoid that shared mutable state (helper methods and an immutable protocol) and even how we can take caching <code>DateFormatter</code> instances a step beyond (hidden dictionary). Overall we covered a lot of ground and if you&apos;ve made it this far (as you have) I believe you deserve whatever you&apos;ve been denying yourself all day &#x1F609;.</p>
<p>For me it&apos;s a lovely hot chocolate with marshmallows &#x2615; - I can guarantee no unexpected shared state there!</p>
<blockquote>
<p>Special thanks to <a href="https://twitter.com/Paul_Pires?ref=williamboles.com">Paul Pires</a> for helping to come up with this idea of how to share <code>DateFormatter</code> instances.</p>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Building a networking layer with operations]]></title><description><![CDATA[Networking is central to how most apps add value to their users which is why significant effort has been put into creating an easy to use networking layer by both Apple and the open-source community]]></description><link>https://williamboles.com/building-a-networking-layer-with-operations/</link><guid isPermaLink="false">5b7c47f0dced3400bfc255c3</guid><category><![CDATA[networking]]></category><category><![CDATA[stackoverflow-api]]></category><category><![CDATA[Concurrency]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Wed, 03 Oct 2018 08:39:39 GMT</pubDate><media:content url="https://williamboles.com/content/images/2020/05/hoover_dam_front-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2020/05/hoover_dam_front-1.jpg" alt="Building a networking layer with operations"><p>Networking is central to how most apps add value to their users which is why significant effort has been put into creating an easy to use networking layer by both Apple and the open-source community.</p>
<p>When iOS was first released, <a href="https://developer.apple.com/documentation/foundation/nsurlconnection?ref=williamboles.com"><code>NSURLConnection</code></a> was the native networking suite it shipped with. However the <code>NSURLConnection</code> story actually began long before iOS as it was first released in 2003 with Safari, this meant that <code>NSURLConnection</code> was never designed for the magnitude of tasks that it ended up having to support. These add-ons resulted in <code>NSURLConnection</code> having a number of sharp edges - such as how much programming effort was required to construct the response of a request: gradually building up an <code>NSMutableData</code> instance by responding to the delegate calls. Making networking trickier than it needed to be. A lot of the code for making network requests was boilerplate code which allowed us to abstract it away into base/parent classes that could then be reused for multiple requests. This <em>not-quite-fit-for-purpose</em> networking approach coupled with boilerplate code resulted in us creating generic, well-encapsulated networking layers to hide all that nasty away.</p>
<p>Then great third-party libraries like <a href="https://github.com/AFNetworking/AFNetworking?ref=williamboles.com"><code>AFNetworking</code></a> began to appear that operated on top of <code>NSURLConnection</code> and looked to smooth out some of those sharp edges for us. <code>AFNetworking</code> did this by implementing the boilerplate that <code>NSURLConnection</code> required and presenting to the developer an easy to use block/closure based interface. This closure based networking approach proved to be hugely successful and eventually Apple decided to embrace it with <a href="https://developer.apple.com/documentation/foundation/urlsession?ref=williamboles.com"><code>URLSession</code></a>. However not everything was rosy &#x1F940; - due to the ease of which we could now make networking requests and process the responses, you started to see a lot more networking code implemented directly in view controllers &#x1F4A5;. These bloated view controllers became responsible for multiple different domains - interacting with views, making network requests and parsing network responses into model objects. Classes that combine multiple domains are trickier to reason about, maintain and extend which is why the <a href="https://en.wikipedia.org/wiki/Single_responsibility_principle?ref=williamboles.com">single responsibility principle</a> is a very common design practice.</p>
<p>In this post, I want to explore how we can build an app that avoids bringing multiple domains together in its view controllers by having a distinct networking layer using <code>URLSession</code> and <code>Operation</code>.</p>
<h3 id="thinkingaboutthatlayer">Thinking about that layer</h3>
<p>Whenever I think of <em>layers</em> in software engineering I always picture that great engineering marvel: the <a href="https://en.wikipedia.org/wiki/Hoover_Dam?ref=williamboles.com">Hoover Dam</a>.</p>
<p><img src="https://williamboles.com/content/images/2020/05/hoover_dam_front.jpg" alt="Building a networking layer with operations" loading="lazy"></p>
<p>A very visible, physical divider between two different domains - the reservoir before and the river after. In order for the water to continue to flow through the canyons, it must pass through the dam in the way that the dam dictates - just like how data flows around a system based on an agreed contract.</p>
<p>(Of course like any good metaphor, the dam one doesn&apos;t quite work because water should only flow one direction unlike our data but it will serve here as a visual aid)</p>
<p>Our networking layer will have 3 main responsibilities:</p>
<ol>
<li>Scheduling network requests</li>
<li>Performing networking requests</li>
<li>Parsing the response from network requests</li>
</ol>
<p>These responsibilities will come together to produce the following class structure:</p>
<p><img src="https://williamboles.com/content/images/2019/11/class-diagram-of-genric-networking-layer-1.jpg" alt="Building a networking layer with operations" loading="lazy"></p>
<p>Just like with the Hoover Dam our networking layer will have a well encapsulated outer edge that will transform data requests into distinct tasks that will then be scheduled for execution. Each task will consist of the actual network request and its response parsing &#x1F339;.</p>
<h3 id="recapping">Recapping</h3>
<p>At the heart of this approach will be the ability to transform a network request and the subsequent parsing of its response into one task to be completed together. With <a href="https://developer.apple.com/documentation/foundation/operationqueue?ref=williamboles.com"><code>OperationQueue</code></a> and <a href="https://developer.apple.com/documentation/foundation/operation?ref=williamboles.com">Operation</a>, iOS already has a simple, well-supported mechanism of isolating distinct tasks within our apps. Combined <code>OperationQueue</code> and <code>Operation</code> form one of the options to support the concurrent execution of tasks.</p>
<p>Before continuing, let&apos;s take a small recap of what <code>OperationQueue</code> and <code>Operation</code> are.</p>
<p>An <code>OperationQueue</code> is a queue that controls the order of execution of its operations by their readiness value (must be <code>true</code> in order to start execution) and then scoring each operation by its priority level and time spent in the queue. This means that an  <code>OperationQueue</code> is built on top of <code>GCD</code> which allows it to take advantage of an iOS device&apos;s multiple cores without the developer having to know anything about how it does this. By default, an <code>OperationQueue</code> will attempt to execute as many operations in parallel as the device is capable of supporting. As an <code>OperationQueue</code> maintains a link to its operations, it&apos;s possible to query those operations and perform additional tasks on them such as pausing or cancelling operations (which can be useful if the user logs out).</p>
<p><code>Operation</code> is an abstract class which need to be subclassed to undertake a specific task. An <code>Operation</code> typically runs on a separate thread from the one that created it. Each operation is controlled via an internal state machine, the possible states are:</p>
<ul>
<li><code>Pending</code> indicates that our <code>Operation</code> subclass has been added to the queue.</li>
<li><code>Ready</code> indicates that our <code>Operation</code> subclass is good to go and if there is space on the queue, this operation&apos;s task can be started</li>
<li><code>Executing</code> indicates that our <code>Operation</code> subclass is actually doing work at the moment.</li>
<li><code>Finished</code> indicates that our <code>Operation</code> subclass has completed its task and should be removed from the queue.</li>
<li><code>Cancelled</code> indicates that our <code>Operation</code> subclass has been cancelled and should stop its execution.</li>
</ul>
<p>A typical operations lifecycle will move through the following states:</p>
<p><img src="https://williamboles.com/content/images/2019/11/operation_possible_states-1.jpg" alt="Building a networking layer with operations" loading="lazy"></p>
<p>It&apos;s important to note that cancelling an executing operation will not actually stop that operation on its own, instead it is up to the individual operation to clean up after itself and move into the <code>Finished</code> state.</p>
<p>Operations come in two flavours:</p>
<ul>
<li>Non-Concurrent</li>
<li>Concurrent</li>
</ul>
<p>Non-Concurrent operations perform all their work on the same thread so that when the <code>main</code> method returns the operation is moved into the <code>Finished</code> state. The queue is then be notified of this and removes the operation from its active operation pool, freeing resources for the next operation.</p>
<p>Concurrent operations can perform some of their work on a different thread so returning from the <code>main</code> method can no longer be used to move the operation into a <code>Finished</code> state. Instead, when we create a concurrent operation we are assuming the responsibility for moving the operation between the <code>Ready</code>, <code>Executing</code> and <code>Finished</code> states.</p>
<h3 id="buildingtheconcurrentoperation">Building the concurrent operation &#x1F3D7;&#xFE0F;</h3>
<p>A networking operation is a specialised concurrent operation because when an <code>URLSession</code> is making a network request, it does so on a different thread from the thread that <em>resumed</em> that task. Rather than cramming everything into one operation, this solution will focus on building an abstract concurrent operation and more specific operations that actually do the networking.</p>
<p>This post will gradually build up to a working example however if time is tight, then head on over to the completed <a href="https://github.com/wibosco/NetworkingInOperations-Example?ref=williamboles.com">example</a> and take a look at <code>ConcurrentOperation</code>, <code>QuestionsRetrievalOperation</code>, <code>QuestionsDataManager</code> and <code>QueueManager</code> to see how things end up.</p>
<p>As mentioned, a concurrent operation takes responsibility for ensuring that its internal state is correct. This state is controlled by manipulating the <code>isReady</code>, <code>isExecuting</code> and <code>isFinished</code> properties. However, these are <strong>read-only</strong> so these properties will need to overridden and an operations&apos; current state tracked via a different property. When mapping state I often find it best to use an enum type:</p>
<pre><code class="swift">class ConcurrentOperation: Operation {

    // MARK: - State

    private enum State {
        case ready
        case executing
        case finished
    }

    private var state = State.ready

    override var isReady: Bool {
        return super.isReady &amp;&amp; state == .ready
    }

    override var isExecuting: Bool {
        return state == .executing
    }

    override var isFinished: Bool {
        return state == .finished
    }
}</code></pre>
<p>In the code snippet above, the private mutable property <code>state</code> will track this internal state with the <code>state</code> value then being used in each of the overridden properties to return the correct boolean result. The cases of the <code>State</code> enum map directly to the state properties that are being overridden.</p>
<p>It&apos;s important to note that the <code>isReady</code> property is a little more complex than <code>isExecuting</code> or <code>isFinished</code>. One of the advantages of using operations is that it&apos;s possible to chain operations together so that one operation is dependent on another operation finishing before it can begin execution. If <code>isReady</code> just checked <code>state == .ready</code>, <code>ConcurrentOperation</code> would lose the ability to create dependencies, as an operation that has an unfinished dependency has an <code>isReady</code> value of <code>false</code>.</p>
<p>(I&apos;ve seen examples of concurrent operation implementations that use 3 separate, settable <em>state</em> properties e.g. <code>_isReady</code>, <code>_isExecuting</code> and <code>_isFinished</code>. While this is a valid approach, I find having 6 properties that are so similarly named hard to read and led to class bloat)</p>
<p>In concurrent operations, it&apos;s not uncommon to see the <code>isAsynchronous</code> property being overridden to always return <em>true</em> however in this example these operations are always going to be executed via an operation queue so there is no need to actually override this property as the queue will just ignore that value.</p>
<p><code>OperationQueue</code> uses <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html?ref=williamboles.com">KVO</a> to <em>know</em> when its operations change state however so far <code>ConcurrentOperation</code> will not inform any observers. The key paths that the queue will observe, map directly with the enum cases. So we can transform our basic <code>State</code> enum into a <code>String</code> backed enum and use the raw-value of each to hold the expected key path string:</p>
<pre><code class="swift">enum State: String {
    case ready = &quot;isReady&quot;
    case executing = &quot;isExecuting&quot;
    case finished = &quot;isFinished&quot;
}</code></pre>
<p>With this new powerful enum, let&apos;s add property observers:</p>
<pre><code class="swift">var state = State.ready {
    willSet {
        willChangeValue(forKey: newValue.rawValue)
        willChangeValue(forKey: state.rawValue)
    }
    didSet {
        didChangeValue(forKey: oldValue.rawValue)
        didChangeValue(forKey: state.rawValue)
    }
}</code></pre>
<p><code>ConcurrentOperation</code> will now trigger KVO notifications correctly but so far the <code>state</code> property isn&apos;t actually being mutated. The first state change is from <code>Ready</code> -&gt; <code>Executing</code> which is handled by overriding the <code>start</code> method:</p>
<pre><code class="swift">override func start() {
    guard !isCancelled else {
        finish()
        return
    }

    if !isExecuting {
        state = .executing
    }

    main()
}</code></pre>
<p>The <code>start</code> method is the entry point into an operation, it is called when the operation should begin its work. In the above code snippet a check is made to see if the operation has been moved into the <code>Cancelled</code> state (yep, an operation can begin in a cancelled state) - if it has, <code>finish</code> is immediately called (this will be shown soon) which will move the operation into the <code>Finished</code> state and then <em>return</em>. If the operation hasn&apos;t been cancelled, it is moved into the <code>Executing</code> state and <code>main</code> is called (for the actual work to begin). Each subclass of <code>ConcurrentOperation</code> would implement <code>main</code> to perform its specific task. <code>main</code> is actually the entry point for non-concurrent operations and technically for a concurrent operation any method could have been chosen as the <em>work</em> method (provided each subclass implements it). However, by choosing <code>main</code> the cognitive load on any future developer is lessened by allowing them to transfer the expectation of how non-concurrent operations work to our concurrent operation implementation.</p>
<p>(In older versions of iOS, concurrent operations were responsible for calling <code>main</code> on a different thread - this is no longer the case)</p>
<p>Its important to note that <code>super.start()</code> isn&apos;t being called on purpose, as by overriding <code>start</code> this operation assumes full control of maintaining its state.</p>
<pre><code class="swift">func finish() {
    if isExecuting {
        state = .finished
    }
}</code></pre>
<p><code>finish</code> acts as a nice symmetrical opposite of the <code>start</code> method and ensures that when an operation has finished that it is moved into the <code>Finished</code> state. It&apos;s essential that all operations eventually call this method - if you are experiencing odd behaviour where your queue seems to have <em>jammed</em> and no operations are being processed, one of your operations is probably missing a <code>finish</code> call somewhere.</p>
<p>Finally the <code>cancel</code> method needs to be overridden:</p>
<pre><code class="swift">override func cancel() {
    super.cancel()

    finish()
}</code></pre>
<p>It&apos;s important that regardless of how a concurrent operation ends that it moves into the <code>Finished</code> state and it does so when cancelled.</p>
<p>And that&apos;s it. By taking control of its state, the above operation has transformed itself from a <em>boring</em> non-concurrent operation into....a...well, <em>slightly less boring</em> concurrent operation.</p>
<h3 id="addingnetworkingspecificaddons">Adding networking specific add-ons &#x1F4E1;</h3>
<p>Before we start using <code>ConncurentOperation</code> however let&apos;s make one more change to make it more useful by adding a custom completion closure that will use the widespread <code>Result</code> enum type to return the outcome of the operations task:</p>
<pre><code class="swift">enum Result<t> {
    case success(T)
    case failure(Error)
}

class ConcurrentOperation<t>: Operation {

    typealias OperationCompletionHandler = (_ result: Result<t>) -&gt; Void

    var completionHandler: (OperationCompletionHandler)?

    // omitting properties and methods that we have already seen

    func complete(result: Result<t>) {
        finish()

        if !isCancelled {
            completionHandler?(result)
        }
    }
}</t></t></t></t></code></pre>
<p><code>ConcurrentOperation</code> now supports returning results of an unknown type <code>T</code> which each operation subclass will define. The custom completion closure is then triggered when the <code>complete</code> method is called.</p>
<p>And that&apos;s it, we have an abstract concurrent networking operation &#x1F393;.</p>
<p>Now, let&apos;s build some concrete operations.</p>
<h3 id="makingthatcall">Making that call</h3>
<p>StackOverflow has a wonderful, open <a href="https://api.stackexchange.com/docs?ref=williamboles.com">API</a> that will be used below to build a networking operation. The networking operation will retrieve and parse the latest questions which are tagged with <code>iOS</code> via the <code>/questions</code> endpoint.</p>
<p>Instead of building the operation piece by piece like we did above, lets look at the complete operation and then examine &#x1F440; more closely the interesting parts:</p>
<pre><code class="swift">class QuestionsRetrievalOperation: ConcurrentOperation<questionpage> {

    private let session: URLSession
    private let urlRequestFactory: QuestionsURLRequestFactory
    private var task: URLSessionTask?
    private let pageIndex: Int

    // MARK: - Init

    init(pageIndex: Int, session: URLSession = URLSession.shared, urlRequestFactory: QuestionsURLRequestFactory = QuestionsURLRequestFactory()) {
        self.pageIndex = pageIndex
        self.session = session
        self.urlRequestFactory = urlRequestFactory
    }

    // MARK: - Main

    override func main() {
        let urlRequest = urlRequestFactory.requestToRetrieveQuestions(pageIndex: pageIndex)

        task = session.dataTask(with: urlRequest) { (data, response, error) in
            guard let data = data else {
                DispatchQueue.main.async {
                    if let error = error {
                        self.complete(result: .failure(error))
                    } else {
                        self.complete(result: .failure(APIError.missingData))
                    }
                }
                return
            }

            do {
                let page = try JSONDecoder().decode(QuestionPage.self, from: data)

                DispatchQueue.main.async {
                    self.complete(result: .success(page))
                }
            } catch let error {
                DispatchQueue.main.async {
                    self.complete(result: .failure(APIError.serialization))
                }
            }
        }

        task?.resume()
    }

    // MARK: - Cancel

    override func cancel() {
        task?.cancel()
        super.cancel()
    }
}</questionpage></code></pre>
<p>(I won&apos;t show the <code>QuestionPage</code>, <code>QuestionsURLRequestFactory</code> and <code>APIError</code> in this post but if you are interested you can see them by cloning the example project <a href="https://github.com/wibosco/NetworkingInOperations-Example?ref=williamboles.com">here</a>. <code>QuestionPage</code> is a model struct that will be populated with the questions endpoint JSON response. <code>QuestionsURLRequestFactory</code> is a helper factory for returning a <code>URLRequest</code> object configured for accessing the <code>/questions</code> endpoint. <code>APIError</code> is a custom <code>Error</code> enum)</p>
<p>In the <code>main</code> method above, an instance of <code>URLSession</code> is used to create a <code>URLSessionDataTask</code> which will make a network request and parse its response. Depending on the outcome of the network request and parsing of its response, <code>complete</code> is called and either an error or a parsed <code>QuestionPage</code> model is passed back wrapped into a <code>Result</code>. When <em>completing</em> an operation, special attention needs to be paid to ensure that the <code>completionHandler</code> property is used directly as this would cause the operation to <em>end</em> but not move it into the <code>Finished</code> state and what would result eventually is a jammed queue (as spoken about above).</p>
<p>In the <code>cancel</code> method, the <code>URLSessionTask</code> instance used to make the network request is cancelled so ensuring that no bandwidth is needlessly wasted.</p>
<p>And that&apos;s pretty much it for what a networking operation looks like. Of course, each different network operation will be unique but the general structure will be similar to <code>QuestionsRetrievalOperation</code> (if you want to see another concurrent operation example, take a look at this <a href="https://github.com/wibosco/NetworkingInOperations-Example/blob/master/NetworkingInOperations-Example/Data/Managers/User/Operations/AvatarRetrievalOperation.swift?ref=williamboles.com">one</a>).</p>
<h3 id="gettingthemanagersinvolved">Getting the managers involved</h3>
<p>So far <strong>2</strong> of the <strong>3</strong> responsibilities of the networking layer shown above have been built:</p>
<ul>
<li><code>Performing networking requests</code></li>
<li><code>Parsing the response from network requests</code></li>
</ul>
<p>Time to look at the final responsibility:</p>
<ul>
<li><code>Scheduling network requests</code>.</li>
</ul>
<p>If we refer back to the class structure above, this responsibility is handled by both the <code>DataManager</code> and <code>Scheduler</code> classes.</p>
<pre><code class="swift">class QuestionsDataManager {

    private let queueManager: QueueManager

    // MARK: - Init

    init(withQueueManager queueManager: QueueManager = QueueManager.shared) {
        self.queueManager = queueManager
    }

    // MARK: - Retrieval

    func retrievalQuestions(pageIndex: Int, completionHandler: @escaping (_ result: Result<questionpage>) -&gt; Void) {
        let operation = QuestionsRetrievalOperation(pageIndex: pageIndex)
        operation.completionHandler = completionHandler
        queueManager.enqueue(operation)
    }
}</questionpage></code></pre>
<p>The above manager is responsible for creating, configuring and scheduling operations on a queue. While this example only shows one method, the idea here is that this manager would handle all networking requests for the <code>/questions</code> endpoint.</p>
<p>The sharper reader will have spotted another custom class in the above code snippet: <code>QueueManager</code>. This class is responsible for the queue (or queues) that the operations will be added to - it acts as the <code>Scheduler</code> class.</p>
<pre><code class="swift">class QueueManager {

    lazy var queue: OperationQueue = {
        let queue = OperationQueue()

        return queue;
    }()

    // MARK: - Singleton

    static let shared = QueueManager()

    // MARK: - Addition

    func enqueue(_ operation: Operation) {
        queue.addOperation(operation)
    }
}</code></pre>
<p>The above manager creates a shared <code>OperationQueue</code> instance and controls access to it via the <code>enqueue</code>. To ensure that operations are consistently added to the same queue, this manager is a singleton. While this class only has the one <code>queue</code> it can be easily extended to hold multiple queues (perhaps one queue for user-driven events and another for system events - allowing greater control on suspending/cancelling queues) with each <code>DataManager</code> specifying which queue to add the operation to by passing an additional parameter when enqueuing that operation.</p>
<h3 id="lookingatwhatwehave">Looking at what we have</h3>
<p>While using operations adds to the complexity of making a network request, when we step back and look at the benefit that having an isolated, well-encapsulated networking layer brings to our apps, I hope that we can all agree it is a cost worth paying &#x1F4B0;.</p>
<p>To see this networking layer in action, head over to the <a href="https://github.com/wibosco/NetworkingInOperations-Example?ref=williamboles.com">repo</a> and clone the project.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Keeping things going when the user leaves]]></title><description><![CDATA[In the past iOS was always about living in the moment - totally focused on whatever the user was attempting to do. This approach was good news for users as it allowed for a very responsive user experience on performance constrained devices however as app developers it limited what we could do]]></description><link>https://williamboles.com/keeping-things-going-when-the-user-leaves-with-urlsession-and-background-transfers/</link><guid isPermaLink="false">5addb8c367d53000229bde33</guid><category><![CDATA[networking]]></category><category><![CDATA[multi-tasking]]></category><category><![CDATA[imgur-api]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Sun, 15 Jul 2018 08:29:32 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/11/walking-away-min.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2019/11/walking-away-min.jpg" alt="Keeping things going when the user leaves"><p>In the past iOS was always about living in the moment - totally focused on whatever the user was attempting to do. This approach was good news for users as it allowed for a very responsive user experience on performance constrained devices however as app developers it limited what we could do because as soon as an app went into the background iOS suspended it. As devices have become more powerful and energy-efficient, iOS has eased the &quot;living in the moment&quot; restrictions while maintaining responsiveness.</p>
<p><img src="https://williamboles.com/content/images/2019/11/walking-away-min-1.jpg" alt="Keeping things going when the user leaves" loading="lazy"></p>
<p>One way that iOS has eased restrictions is allowing apps to start/continue download and upload requests in the background - collectively known as <code>background-transfers</code>. Support for background-transfers was first introduced in iOS 7, so it&apos;s not a recent change, but it&apos;s such a powerful tool that I wanted to explore it further and especially show a solution for recovering from a terminated state.</p>
<h3 id="differenttypesofsessions">Different types of sessions</h3>
<p>When Apple introduced the <a href="https://developer.apple.com/documentation/foundation/urlsession?ref=williamboles.com"><code>URLSession</code></a> suite of classes, they addressed a long standing complaint - how to configure the networking stack to handle various types of network requests that an app has to make. Rather than having one networking stack like with <code>NSURLConnection</code>, <code>NSURLSession</code> instead allowed for multiple independent networking stacks (or sessions as they are known). The configurations of these independent sessions are controlled through <a href="https://developer.apple.com/documentation/foundation/urlsessionconfiguration?ref=williamboles.com"><code>URLSessionConfiguration</code></a>. The main session configurations are:</p>
<ol>
<li><code>Default</code> - allowing for all types of network requests in the foreground.</li>
<li><code>Ephemeral</code> - similar to <code>default</code> but more forgetful (doesn&#x2019;t write caches, cookies, or credentials to disk).</li>
<li><code>Background</code> - allowing for downloading and uploading content even when the app isn&apos;t running.</li>
</ol>
<blockquote>
<p>Of course, it&apos;s possible to further differentiate sessions within those configurations by adjusting the properties on each session.</p>
</blockquote>
<p>At the time <code>URLSession</code> was introduced I was building a very media-heavy app, and the promise of being able to continue to upload or download content when the app wasn&apos;t running by using a background session was very appealing.</p>
<h3 id="peekingunderthehood">Peeking under the hood &#x1F575;&#xFE0F;</h3>
<p>When scheduling a background-transfer on a background session, that transfer is passed to the <code>nsurlsessiond</code> daemon, which runs as a separate process from the app, to be actioned. As this daemon lives outside of the lifecycle of the app if the app is suspended or killed the transfer will continue unaffected - this is the main difference from scheduling an <code>URLSessionDownloadTask</code> or <code>URLSessionUploadTask</code> request on a <code>default</code> or <code>ephemeral</code> session where the request is tied to the lifecycle of the app. Once a background-transfer is complete, if the app has been suspended/terminated iOS will <em>wake</em> it up (in the background) and allow the app to perform any post-transfer work (within a limited time frame). If the app is in the foreground, control will be passed back to the app as if the transfer has been scheduled on <code>default</code> or <code>ephemeral</code> session (without a limited time frame for post-transfer processing).</p>
<blockquote>
<p>Important to note here that you can schedule <code>URLSessionDataTask</code> requests on a background session but they will only be acted on when the app is in the foreground.</p>
</blockquote>
<p>You&apos;re probably thinking:</p>
<p><strong>&quot;That sounds pretty amazing! Why isn&apos;t this the default behaviour for transfers?&quot;</strong></p>
<p>Well, there are a few reasons:</p>
<ul>
<li>
<p>The more background processing that the device has to support the quicker the battery will drain and the more bandwidth that will be consumed. So while Apple understands that background processing is needed, it wants to ensure that as app developers we use it responsibly and only where it&apos;s actually adding value.</p>
</li>
<li>
<p>From a developer-POV supporting background-transfers requires a bit more programming than a foreground-only session. Rather than the simple closure based API that foreground-only sessions support, to use background sessions the developer needs to conform to various protocols. This additional programming while not hugely taxing (as we shall see shortly) does require each developer to know more about how <code>URLSession</code> works before they can make a network request. So in true Apple fashion, they have opted to not only keep it simple for the users but also for us developers.</p>
</li>
</ul>
<p>Now that the hood has been peeked under, let&apos;s get back to how to use background-transfers to provide a better experience for our users.</p>
<h3 id="scenarios">Scenarios</h3>
<p>Any background-transfer solution should be able to handle 3 different transfer scenarios:</p>
<ol>
<li><code>Foreground</code> - when the app is open, the transfer should behave the same way as if the session was using the <code>default</code> configuration.</li>
<li><code>Suspended/Backgrounded</code> - when the app is in a suspended state and the asset is downloaded, the app should be able to be woken up to finish the transfer.</li>
<li><code>Terminated</code> - when the app has been terminated by iOS, the app should be able to be relaunched to finish that transfer.</li>
</ol>
<p>In the rest of this post, we will build an image download system that will look to handle the 3 scenarios described above. This example will populate a collectionview using data retrieved from <a href="https://imgur.com/?ref=williamboles.com"><code>Imgur</code></a> with all image retrieval happening on a background session.</p>
<blockquote>
<p>This post will focus solely on downloading using a background session as it&apos;s much easier to find services that allow us to retrieve content from their system than services that allow us to upload content.</p>
</blockquote>
<p>This post will gradually build up to a working example however if you&apos;re on a tight deadline and/or there is murderous look creeping into your manager&apos;s eyes &#x1F621;, then head on over to the <a href="https://github.com/wibosco/BackgroundTransfer-Example?ref=williamboles.com">completed example</a> and take a look at <code>BackgroundDownloader</code>, <code>BackgroundDownloaderContext</code>, <code>BackgroundDownloadItem</code> and <code>AppDelegate</code> to see how things end up - in order to run this you will need to follow the instructions <a href="#runningtheexampleproject">below</a>.</p>
<h3 id="letsgetdownloading">Let&apos;s get downloading</h3>
<p>To support background-transfers we need to grant permission for <code>Background Modes</code>. This is done by opening the <code>Capabilities</code> tab in the target and switching the <code>Background Modes</code> toggle to <code>ON</code>.</p>
<p>As multiple viewcontrollers within the project could be retrieving images I decided to handle these download requests within their own class - <code>BackgroundDownloader</code>. <code>BackgroundDownloader</code> will be responsible for configuring our background session, scheduling download requests against that session and responding to any delegate callbacks.</p>
<p>Lets build the skeleton of the <code>BackgroundDownloader</code> class:</p>
<pre><code class="swift">class BackgroundDownloader: NSObject {

    private var session: URLSession!

    // MARK: - Singleton

    static let shared = BackgroundDownloader()

    // MARK: - Init

    private override init() {
        super.init()

        let configuration = URLSessionConfiguration.background(withIdentifier: &quot;background.download.session&quot;)
        session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
    }
}</code></pre>
<p>In the above code snippet, we create an instance of an <code>URLSessionConfiguration</code> instance and <code>URLSession</code> that will handle any background tasks. <code>configuration</code> is initialised using the <code>background</code> convenience init&apos;er to allow any <code>URLSession</code> instances to support background-transfers. Each <code>URLSessionConfiguration</code> instances needs to have an identifier. This identifier plays a critical role in allowing a background-transfer that started on one instance of <code>URLSession</code> to be completed on another. This happens when the app has been terminated and iOS <em>wakes</em> the terminated app up to finish the transfer - <code>configuration</code> uses a static string as an identifier so it can be recreated (we will see this in action later). It&apos;s important to note here that each session needs to have a <strong>unique</strong> identifier within that app&apos;s execution lifecycle. As <code>BackgroundDownloader</code> is a singleton, only one session will have this identifier.</p>
<p>An interesting difference between background sessions and the other session types is that with background sessions we can&apos;t use the <code>completionHandler</code> methods when creating a <code>URLSessionDownloadTask</code> task (doing so won&apos;t result in the compiler throwing an error, but the app will throw an exception at run time). Instead of using the closure approach, <code>BackgroundDownloader</code> will need to implement the <code>URLSessionDownloadDelegate</code> protocol (and eventually <code>URLSessionDelegate</code>) - this need to implement these delegates is the reason that <code>BackgroundDownloader</code> is a subclass of <code>NSObject</code>.</p>
<p>The more eagle-eyed among you will have noticed that the <code>session</code> property is implicitly unwrapped. I usually try and avoid implicitly unwrapped properties but as <code>session</code> is a private property and is set in the init&apos;er I felt making it implicitly unwrapped resulted in more readable code with very little danger of a crash. The reason that <code>session</code> can&apos;t be a <code>let</code> is that when creating the session, the <code>BackgroundDownloader</code> instance sets itself as the session&apos;s delegate so the <code>BackgroundDownloader</code> has to exist which means that the initialising of the <code>session</code> has to happen after the <code>super.init()</code> call is made.</p>
<p>So far, we have configured a session for performing background-transfers but don&apos;t yet have the ability to download anything so let&apos;s add that. A download needs 3 pieces of information:</p>
<ol>
<li><code>Remote URL</code> - URL of the asset to be downloaded.</li>
<li><code>File path URL</code> - URL of where the downloaded asset should be moved to on the local file system.</li>
<li><code>Completion handler</code> - a closure to be called when the download is complete.</li>
</ol>
<p>Rather than store these pieces of information about each download in separate arrays, we can create a model class to group these related pieces of information:</p>
<pre><code class="swift">typealias ForegroundDownloadCompletionHandler = ((_ result: DataRequestResult<url>) -&gt; Void)

class DownloadItem {

    let remoteURL: URL
    let filePathURL: URL
    var foregroundCompletionHandler: ForegroundDownloadCompletionHandler?

    // MARK: - Init

    init(remoteURL: URL, filePathURL: URL) {
        self.remoteURL = remoteURL
        self.filePathURL = filePathURL
    }
}</url></code></pre>
<p>An instance of <code>DownloadItem</code> will be created when a new download request is made against <code>BackgroundDownloader</code>. This <code>DownloadItem</code> instance can then be stored in a dictionary to be used once the download is complete:</p>
<pre><code class="swift">class BackgroundDownloader: NSObject {

    // Omitted properties

    private var downloadItems: [URL: DownloadItem] = [:]

    // Omitted methods

    func download(remoteURL: URL, filePathURL: URL, completionHandler: @escaping ForegroundDownloadCompletionHandler) {
        print(&quot;Scheduling download of: \(remoteURL)&quot;)

        let downloadItem = DownloadItem(remoteURL: remoteURL, filePathURL: filePathURL)
        downloadItem.foregroundCompletionHandler = completionHandler
        downloadItems[remoteURL] = downloadItem

        let task = session.downloadTask(with: remoteURL)
        task.resume()
    }
}</code></pre>
<blockquote>
<p>It&apos;s not unusual to have a scenario where before a download request is finished another request for that same download is made - think of a user scrolling a tableview up and down. There are many articles written on how to prevent these additional (unneeded) download requests so I won&apos;t complicate this example about background-transfers by implementing any of them here. However if you are really interested on how to prevent these additional download requests, check out my <a href="https://williamboles.com/removing-bolierplate-when-coalescing-nsoperations/">coalescing closures article</a> on this subject which shows one possbile approach.</p>
</blockquote>
<p>The <code>URLSessionDownloadDelegate</code> protocol has been mentioned a few times now so lets see how it&apos;s implemented:</p>
<pre><code class="swift">extension BackgroundDownloader: URLSessionDownloadDelegate {

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        guard let originalRequestURL = downloadTask.originalRequest?.url, let downloadItem = downloadItems[originalRequestURL] else {
            return
        }

        print(&quot;Downloaded: \(downloadItem.remoteURL)&quot;)

        do {
            try FileManager.default.moveItem(at: location, to: downloadItem.filePathURL)

            downloadItem.foregroundCompletionHandler?(.success(downloadItem.filePathURL))
        } catch {
            downloadItem.foregroundCompletionHandler?(.failure(APIError.invalidData))
        }

       downloadItems[originalRequestURL] = nil
    }
}</code></pre>
<p>If the download has been a success, the asset is moved from its <a href="https://developer.apple.com/documentation/foundation/urlsession/1411511-downloadtask?ref=williamboles.com">temporary location</a> to its permanent location on the file system. Then the <code>completionHandler</code> is triggered with the success case of the <code>DataRequestResult</code> enum containing the permanent location URL.</p>
<p>If the download has failed the <code>completionHandler</code> is triggered with the failure case containing an error.</p>
<p>(This method is deliberately kept as simple as possible but you can easily imagine how it and <code>DownloadItem</code> could be extended to e.g. update Core Data or perform image manipulation)</p>
<p>Finally we have been using <code>DataRequestResult</code> enum as a generic result enum so lets look at as well:</p>
<pre><code class="swift">enum DataRequestResult&lt;T&gt; {
    case success(T)
    case failure(Error)
}</code></pre>
<p>And that&apos;s all that&apos;s required for performing foreground transfers using a background configuration.</p>
<blockquote>
<p>If you want to see this version of <code>BackgroundDownloader</code> running, checkout out the <code>foreground_only_transfers</code> branch on the <a href="https://github.com/wibosco/BackgroundTransfer-Example?ref=williamboles.com">example project</a> - to run this you will need to follow the instructions <a href="#runningtheexampleproject">below</a>.</p>
</blockquote>
<h3 id="workingbehindtheusersback">Working behind the users back</h3>
<p>If you run the app in the foreground you will notice that everything works as expected and the app is populated with downloaded assets however as soon as the app is put into the background, a stream of errors will begin to appear in the Xcode console, similar to:</p>
<p><img src="https://williamboles.com/content/images/2018/06/completion-handler-never-called-error.png" alt="Keeping things going when the user leaves" loading="lazy"></p>
<p>So far <code>BackgroundDownloader</code> only works when the app is in the foreground, let&apos;s extend it to support downloads when the app enters a suspended/backgrounded state.</p>
<p>When an app is in a suspended/terminated state and a download is completed, iOS will wake the app up (in the background) by calling:</p>
<pre><code class="swift">class AppDelegate: UIResponder, UIApplicationDelegate {

    // Omitted properties and methods

    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -&gt; Void) {
        //TODO: Handle processing downloads
    }
}</code></pre>
<p>Regardless of how many downloads are in progress, <code>application(_:handleEventsForBackgroundURLSession:completionHandler:)</code> is only called <strong>once</strong> - when <strong>all</strong> downloads have been completed. If the user foregrounds the app before the above method is called, <code>urlSession(_:downloadTask:didFinishDownloadingTo:)</code> is called for those downloads that have been completed.</p>
<p>In the above method, iOS has provided the app with the identifier of the session that the task was scheduled on and also a completion-closure. As our background session always uses the same identifier, we don&apos;t need to do anything with <code>identifier</code> however we do need to pass <code>completionHandler</code> onto <code>BackgroundDownloader</code> so that it can be triggered once all download processing has been completed:</p>
<pre><code class="swift">class AppDelegate: UIResponder, UIApplicationDelegate {

    // Omitted properties and methods

    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -&gt; Void) {
        BackgroundDownloader.shared.backgroundCompletionHandler = completionHandler
    }
}</code></pre>
<p>As the <code>completionHandler</code> closure is passed to the shared <code>BackgroundDownloader</code> instance we need to add a new property to that class:</p>
<pre><code class="swift">class BackgroundDownloader: NSObject {

    var backgroundCompletionHandler: (() -&gt; Void)?

    // Omitted properties and methods
}</code></pre>
<p>When all the downloads are are complete, just like before <code>urlSession(_:downloadTask:didFinishDownloadingTo:)</code> is be called for <strong>each</strong> download to allow any post-download processing to occur. After all the calls to <code>urlSession(_:downloadTask:didFinishDownloadingTo:)</code> have been made, a special background-transfer only call is made:</p>
<pre><code class="swift">extension BackgroundDownloader: URLSessionDelegate {

    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        DispatchQueue.main.async {
            self.backgroundCompletionHandler?()
            self.backgroundCompletionHandler = nil
        }
    }
}</code></pre>
<p>The above method&apos;s main job is to allow <code>backgroundCompletionHandler</code> to be triggered. Triggering <code>backgroundCompletionHandler</code> instructs iOS to take a new snapshot of the app&apos;s UI for the <a href="https://support.apple.com/en-gb/HT202070?ref=williamboles.com">app switcher preview</a> - as such it needs to be called from the main queue (<code>urlSessionDidFinishEvents(forBackgroundURLSession:)</code> may be called from a background queue). It&apos;s important to ensure that your app does indeed call <code>backgroundCompletionHandler</code> - failure to do so will result in iOS treating the app as a <em>bad citizen</em> app; reducing the opportunity for any background processing in the future and resulting in the error that we seen at the start of this section.</p>
<blockquote>
<p>While it&apos;s possible to make additional network requests with the app running in the background, they are subject to a rate limiter to <a href="https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background/?ref=williamboles.com#3038438">prevent abuse of background sessions</a> - this rate limiter delays the start of those network requests. With each separate network request the delay increases - this delay is reset when the user brings the app to the foreground or if your app does not make any additional network requests during the delay.</p>
</blockquote>
<p>And that&apos;s all the changes required to continue network requests when the app is suspended/backgrounded.</p>
<blockquote>
<p>If you want to see this version of <code>BackgroundDownloader</code> running, checkout out the <code>foreground_and_suspended_transfers</code> branch on the <a href="https://github.com/wibosco/BackgroundTransfer-Example?ref=williamboles.com">example project</a> - to run this you will need to follow the instructions <a href="#runningtheexampleproject">below</a>. In order to better see the downloads happening in the background you may want to add a slight delay before the download actually starts so as to allow you to more leisurely put the app into the background: <pre><code class="swift">task.earliestBeginDate = Date().addingTimeInterval(5)</code></pre></p>
</blockquote>
<h3 id="comingbackfromthedead">Coming back from the dead &#x1F9DF;</h3>
<p>After an app has been terminated, all of the memory which was allocated to it is freed. This means that when iOS relaunches the app to complete a background-transfer we don&apos;t have the details in memory that we can use to determine how that background-transfer should be completed. Instead, these details need to be persisted in the file system between app executions. In our example, the only information that we really need to persist is <code>filePathURL</code> (as you will see the <code>remoteURL</code> is also persisted but this is primarily for informational purposes) so introducing something like <code>Core Data</code> to handle this would be overkill. Instead, let&apos;s stick with <a href="https://en.wikipedia.org/wiki/KISS_principle?ref=williamboles.com">KISS principles</a> and take advantage of the <a href="https://developer.apple.com/documentation/swift/codable?ref=williamboles.com"><code>Codable</code></a> protocol. By conforming <code>DownloadItem</code> to <code>Codeable</code> we can encode that <code>DownloadItem</code> object into its <code>Data</code> representation which can then be stored in <code>User Defaults</code>. The only issue with this approach is that the <code>foregroundCompletionHandler</code> property on <code>DownloadItem</code> can&apos;t conform to <code>Codable</code>, so the <code>DownloadItem</code> implementation of <code>Codable</code> will be slightly more involved than usual. Thankfully <code>Codable</code> has an easy way to declare which properties should and shouldn&apos;t be encoded.</p>
<pre><code class="swift">class DownloadItem: Codable {

    // Properties omitted

    private enum CodingKeys: String, CodingKey {
        case remoteURL
        case filePathURL
    }

    // Methods omitted
}</code></pre>
<p>With these changes, the <code>DownloadItem</code> class now has a dual nature:</p>
<ol>
<li>Freshly created.</li>
<li>Restored from <code>User Defaults</code>.</li>
</ol>
<p>This presents a problem with determining which class should be responsible for handling this dual nature - knowing how to save into, load from and clean up <code>User Defaults</code>. I toyed with placing this responsibility in <code>DownloadItem</code> but wanted to keep this class as simple as possible which ruled that approach out. Then I moved onto <code>BackgroundDownloader</code> but again felt that it wasn&apos;t the correct class for handling this - I didn&apos;t want to expose that some <code>DownloadItem</code> instances were <em>freshly</em> created while some had been <em>restored</em> from <code>User Defaults</code> to <code>BackgroundDownloader</code> whose primary responsibility centred on downloading rather than persistence. In the end, I drew inspiration from Core Data and decided to create a downloading context.</p>
<p>This context class needs to handle 3 different tasks:</p>
<ol>
<li>Loading.</li>
<li>Saving.</li>
<li>Deleting.</li>
</ol>
<p>Lets start building this <code>BackgroundDownloaderContext</code> class:</p>
<pre><code class="swift">class BackgroundDownloaderContext {

    private var inMemoryDownloadItems: [URL: DownloadItem] = [:]
    private let userDefaults = UserDefaults.standard

}</code></pre>
<p>Like before, a simple dictionary is being used as an in-memory store which will hold all <strong>active</strong> <code>DownloadItem</code> instances.</p>
<p>When it comes to loading/retrieving an existing <code>DownloadItem</code> instance, this can be from two possible stores:</p>
<ol>
<li>In-memory.</li>
<li><code>User Defaults</code>.</li>
</ol>
<p>Again, we don&apos;t want to expose these details so let&apos;s add a <em>load</em> method to <code>BackgroundDownloaderContext</code> that can load from either of the two possible stores:</p>
<pre><code class="swift">class BackgroundDownloaderContext {

    // Omitted properties

    func loadDownloadItem(withURL url: URL) -&gt; DownloadItem? {
        if let downloadItem = inMemoryDownloadItems[url] {
            return downloadItem
        } else if let downloadItem = loadDownloadItemFromStorage(withURL: url) {
             inMemoryDownloadItems[downloadItem.remoteURL] = downloadItem

            return downloadItem
        }

        return nil
    }

    private func loadDownloadItemFromStorage(withURL url: URL) -&gt; DownloadItem? {
        guard let encodedData = userDefaults.object(forKey: url.path) as? Data else {
            return nil
        }

        let downloadItem = try? JSONDecoder().decode(DownloadItem.self, from: encodedData)
        return downloadItem
    }
}</code></pre>
<p>First, the in-memory store is checked for an existing <code>DownloadItem</code> using the network request&apos;s URL as the key. If an existing <code>DownloadItem</code> instance isn&apos;t found in the in-memory store, the <code>User Defaults</code> store is checked. If an object is found in <code>User Defaults</code>, an attempt is made to decode it from its raw <code>Data</code> representation to a new <code>DownloadItem</code> instance. As you will have spotted, decoding can potentially throw an exception, if an exception is thrown it will be silently ignored due to the <code>try?</code> statement. I chose this approach as an error during decoding wouldn&apos;t be repairable, instead just returning <code>nil</code> would suffice.</p>
<p>In order to get <code>DownloadItem</code> instances into <code>User Defaults</code> we need to save them:</p>
<pre><code class="swift">class BackgroundDownloaderContext {

    // Omitted properties and methods

    func saveDownloadItem(_ downloadItem: DownloadItem) {
        inMemoryDownloadItems[downloadItem.remoteURL] = downloadItem

        let encodedData = try? JSONEncoder().encode(downloadItem)
        userDefaults.set(encodedData, forKey: downloadItem.remoteURL.path)
        userDefaults.synchronize()
    }
}</code></pre>
<p>In the above code snippet, the <code>DownloadItem</code> instance is first placed into the in-memory store before being saved into <code>User Defaults</code> as a <code>Data</code> object.</p>
<p>Finally when a download is complete the in-memory and <code>User Default</code> objects need to be cleaned away:</p>
<pre><code class="swift">class BackgroundDownloaderContext {

    // Omitted properties and methods

    func deleteDownloadItem(_ downloadItem: DownloadItem) {
      inMemoryDownloadItems[downloadItem.remoteURL] = nil
      userDefaults.removeObject(forKey: downloadItem.remoteURL.path)
      userDefaults.synchronize()
    }
}</code></pre>
<p>By implementing the <code>BackgroundDownloaderContext</code>, the <code>BackgroundDownloader</code> logic can be kept pretty much the same as before despite this significant introduction of new functionality. First we need to add a new property to hold an instance of <code>BackgroundDownloaderContext</code> and remove the existing <code>downloadItems</code> property</p>
<pre><code class="swift">class BackgroundDownloader: NSObject {

    private let context = BackgroundDownloaderContext()

    // Omitted properties and methods
}</code></pre>
<p>Now let&apos;s use that context when requesting a new download:</p>
<pre><code class="swift">class BackgroundDownloader: NSObject {

    // Omitted properties and methods

    func download(remoteURL: URL, filePathURL: URL, completionHandler: @escaping ForegroundDownloadCompletionHandler) {
        print(&quot;Scheduling download of: \(remoteURL)&quot;)

        let downloadItem = DownloadItem(remoteURL: remoteURL, filePathURL: filePathURL)
        downloadItem.foregroundCompletionHandler = completionHandler
        context.saveDownloadItem(downloadItem)

        let task = session.downloadTask(with: remoteURL)
        task.resume()
    }
}</code></pre>
<p>Then when a download is completed, the <code>DownloadItem</code> instance needs to be deleted from the context:</p>
<pre><code class="swift">extension BackgroundDownloader: URLSessionDownloadDelegate {

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        guard let originalRequestURL = downloadTask.originalRequest?.url, let downloadItem = context.loadDownloadItem(withURL: originalRequestURL) else {
            return
        }

        print(&quot;Downloaded: \(downloadItem.remoteURL)&quot;)

        do {
            try FileManager.default.moveItem(at: location, to: downloadItem.filePathURL)

            downloadItem.foregroundCompletionHandler?(.success(downloadItem.filePathURL))
        } catch {
            downloadItem.foregroundCompletionHandler?(.failure(APIError.invalidData))
        }

       context.deleteDownloadItem(downloadItem)
    }
}</code></pre>
<p>Again, here we have managed to avoid exposing to the <code>BackgroundDownloader</code> if that <code>downloadItem</code> was purely an in-memory object or if it also had a backing <code>User Defaults</code> entry.</p>
<p>As with the suspended/backgrounded code changes, when the app is awoken from a terminated state the app-delegate is called:</p>
<pre><code class="swift">class AppDelegate: UIResponder, UIApplicationDelegate {

    // Omitted properties and methods

    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -&gt; Void) {
      BackgroundDownloader.shared.backgroundCompletionHandler = completionHandler
    }
}</code></pre>
<p>This is actually the exact same code as in the suspended/backgrounded example above but I wanted to highlight that when the app is awoken from a terminated state, this method not only passes the <code>completionHandler</code> along but also sets up the <code>BackgroundDownloader</code> (by calling <code>shared</code>) so that it can respond to the session delegate calls.</p>
<p>An interesting point around terminating the app is that if you manually force quit the app then iOS will take this as definite confirmation that you are finished with the app and so cancel any scheduled background-transfers. This presents a problem when it comes to testing how our solution handles termination. The easiest way I found to overcome this issue was to add a manual <code>exit</code> call to cause the app to terminate without any user involvement:</p>
<pre><code class="swift">class AppDelegate: UIResponder, UIApplicationDelegate {

    // Omitted properties and methods

    func applicationDidEnterBackground(_ application: UIApplication) {
        //Exit app to test restoring app from a terminated state. Comment out to test restoring app from a suspended state.
        DispatchQueue.main.asyncAfter(deadline: .now()) {
            print(&quot;App is about to quit&quot;)

            if let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
                debugPrint(&quot;Gallery assets will be saved to: \(documentsPath)&quot;)
            }
            exit(0)
        }
    }
}</code></pre>
<p>In the above code snippet, the file path of the directory that the downloaded assets will end up in is printed to the console and then the app terminates. With this file path, you can then navigate to that directory and see the folder fill up with downloaded assets.</p>
<h3 id="downloadskeepgoing">Downloads keep going &#x1F92F;</h3>
<p>With background sessions, we have a powerful weapon in ensuring that we can meet our user&apos;s wants without having to trap them in the app. As we seen above adding support for background sessions isn&apos;t too tricky and what extra complexity there is, can mostly be hidden from the wider app.</p>
<p>Congratulations on making it to the end &#x1F389;.</p>
<hr>
<h3 id="runningtheexampleproject">Running the example project</h3>
<p>In <a href="https://williamboles.com/not-all-downloads-are-born-equal/">&quot;Don&apos;t throw anything away with pausable downloads&quot;</a> I used <a href="https://apidocs.imgur.com/?ref=williamboles.com">Imgur</a> as an online image database and I&apos;ve used it again in this post to power the example project. Imgur has a great JSON based API and an extensive library of freely available media. Imgur API, while being free, does require us to <a href="https://api.imgur.com/oauth2/addclient?ref=williamboles.com">register</a> our example project to get a client-id which needs to be sent with each request.</p>
<p>As the <a href="https://github.com/wibosco/BackgroundTransfer-Example?ref=williamboles.com">example project</a> does not require access to a user&apos;s personal data, when registering select the <code>Anonymous usage without user authorization</code> option for Authorization type. At the time of writing, you had to provide a URL in the <code>Authorization callback URL</code> field even for anonymous authentication - I found any URL value would work for this.</p>
<p>After you register, you should be given a unique <code>client-id</code> which will allow you access to Imgur&apos;s content. You will need to add your <code>client-id</code> as the value of the <code>clientID</code> property in <code>RequestConfig</code>. After you&apos;ve added your <code>client-id</code>, you should be able to run the app and see how our background-transfer solution works.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Improving my Zen with GitHub]]></title><description><![CDATA[It's 17:00 on a warm Friday evening, as you type the final characters into your PR description your mind drifts to the coming weekend. During that weekend you decide to work on that open-source you cloned a while back, so you head to GitHub to look through the open issues]]></description><link>https://williamboles.com/adding-multiple-git-users-to-same-machine/</link><guid isPermaLink="false">5aba4e29b358000022e69933</guid><category><![CDATA[git]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Mon, 23 Apr 2018 18:41:51 GMT</pubDate><media:content url="https://williamboles.com/content/images/2020/04/relaxing-due-to-better-work-life-balance-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2020/04/relaxing-due-to-better-work-life-balance-1.jpg" alt="Improving my Zen with GitHub"><p>It&apos;s 17:00 on a warm Friday evening, as you type the final characters into your PR description your mind drifts to the coming weekend. During that weekend you decide to work on that open-source you cloned a while back, so you head to GitHub to look through the open issues. Like many developers, you use the same GitHub account for both your personal and work projects so when you open GitHub, you can&apos;t avoid seeing that a colleague has spent some of their weekend leaving comments on your PR. You&apos;re now faced with a dilemma, do you:</p>
<p><strong>A.</strong> Open your PR and read those comments.</p>
<p>Or</p>
<p><strong>B.</strong> Try and ignore those comments.</p>
<p>Choosing <strong>A</strong> means that you spend a portion of your weekend completing work-related tasks when you should be relaxing and recharging. Do this too often and those out-of-work tasks you are completing will lead to your in-work <a href="https://www.coburgbanks.co.uk/blog/candidate-tips/importance-of-maintaining-work-life-balance/?ref=williamboles.com">performance dropping</a> and may even lead to you burning out.</p>
<p>Choosing <strong>B</strong> means that throughout the rest of the weekend, you repeatedly return to that PR in your thoughts wondering what issues someone found in your solution.  Not knowing what someone has commented on can be low-level stressful. This stress is because as humans, we are programmed to remind ourselves about any uncompleted tasks. Your colleague leaving comments means that your previously <em>completed</em> task is now <em>incomplete</em>. In psychology, this is known as the <a href="https://www.rightattitudes.com/2017/03/14/zeigarnik-effect/?ref=williamboles.com">Zeigarnik effect</a> and will stop you from leaving work behind to focus on whatever you are doing during the weekend.</p>
<p>As you can see choosing <strong>A</strong> or <strong>B</strong> leads to a negative outcome. The only way to truly enjoy your weekend is to leave work behind. In this case - won&apos;t you don&apos;t know can&apos;t hurt you.</p>
<p>I found that having my mixed GitHub account kept stressing me out. As I couldn&apos;t avoid seeing work-related updates on the GitHub homepage when doing some recreational programming. I don&apos;t want to stop the recreational programming because I love it, but I couldn&apos;t let my mental health continue to suffer. The only way I could think to prevent this unneeded stress was to disconnect my personal and work GitHub usage by creating multiple GitHub accounts.</p>
<p><img src="https://williamboles.com/content/images/2020/04/relaxing-due-to-better-work-life-balance.jpg" alt="Improving my Zen with GitHub" loading="lazy"></p>
<p>However, creating and using multiple GitHub accounts on the same computer proved to be slightly trickier than I thought it would be.</p>
<h3 id="addingmultipleaccounts">Adding multiple accounts</h3>
<p>After creating a work-only GitHub account, I needed to generate an SSH key so that I could access that account from my machine. The only difference from the <a href="https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh?ref=williamboles.com">documented Git setup</a> is that I couldn&apos;t use <code>id_rsa</code> as the file name to store my work account&apos;s SSH key (as it was storing my personal SSH key) instead I had to use a different name, i.e. <code>id_rsa_work</code>.</p>
<p>Now that I had both SSH keys on my Mac, I needed a way to inform Git on which one to use with which repo.</p>
<p>There are a number of detailed articles already written on this subject and I ended up with a SSH config (<code>~/.ssh/config</code>) of:</p>
<pre><code class="bash">Host personal
  HostName github.com
  User git
  AddKeysToAgent yes
  UseKeychain yes
  IdentitiesOnly yes
  IdentityFile ~/.ssh/id_rsa

Host work
  HostName github.com
  User git
  AddKeysToAgent yes
  UseKeychain yes
  IdentitiesOnly yes
  IdentityFile ~/.ssh/id_rsa_work</code></pre>
<p>The idea being that when cloning a repo that I would specify which account to use and any actions after on that repo would happen as that account i.e.</p>
<pre><code class="bash">git clone personal:wibosco/CoreDataServices.git</code></pre>
<p>or</p>
<pre><code class="bash">git clone git@personal:wibosco/CoreDataServices.git</code></pre>
<p>However, sadly, I wasn&apos;t able to make this work on my Mac (<a href="https://en.wikipedia.org/wiki/MacOS_Mojave?ref=williamboles.com">Mojave</a> or <a href="https://en.wikipedia.org/wiki/MacOS_Catalina?ref=williamboles.com">Catalina</a>). No matter how I manipulated the structure of my SSH config I ended up back at the same issue - Git always attempted to use the last key added to the SSH Agent regardless of which account I specified when cloning that repo.</p>
<p>Just as I started to despair that there was no way to get this working, the thought occurred to me:</p>
<p>&quot;Well, if SSH Agent is always serving the last key added I can just only tell it about the key for the account I want to use&quot;</p>
<p>If the SSH Agent only knew about one SSH key, that key was the one it would serve.</p>
<p>So the first thing to do is make the SSH Agent forget about my personal and work SSH keys by removing them:</p>
<pre><code class="bash">ssh-add -D</code></pre>
<p>Once all SSH keys were removed (from the Agent, not the system) I could then add the correct key for the repo I wanted to access, so if it were a personal repo I would add my personal key to the SSH Agent:</p>
<pre><code class="bash">ssh-add -K ~/.ssh/id_rsa</code></pre>
<p>Then when I used any Git commands, they would be executed as my personal account. If I wanted to use my work account, I would remove the personal key and add my work key. While it was annoying having to keep switching between accounts, it worked!</p>
<blockquote>
<p>Executing <code>ssh-add -l</code> shows you which keys are loaded.</p>
</blockquote>
<p>Even with the inconvenience of having to manually add and remove keys from the SSH Agent I was able to improve my work-life balance by removing that general sense of unease I would get by being able to avoid work-related updates during my personal time. I don&apos;t have any data on how this change impacted my productivity, but I imagine that this made me more productive.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Hosting ViewControllers in Cells]]></title><description><![CDATA[Recently, I've been experiencing the iOS equivalent of the movie Inception - putting a collection view inside a collection view]]></description><link>https://williamboles.com/hosting-viewcontrollers-in-cells/</link><guid isPermaLink="false">5ac5e5a9fdabb10022de7a4e</guid><category><![CDATA[ui]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Wed, 11 Apr 2018 20:09:59 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/11/stained-glass-spiral-min.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2019/11/stained-glass-spiral-min.jpg" alt="Hosting ViewControllers in Cells"><p>Recently, I&apos;ve been experiencing the iOS equivalent of the movie <a href="http://www.imdb.com/title/tt1375666/?ref=williamboles.com">Inception</a> - putting a collection view inside a collection view. While exploring possible solutions, I stumbled upon this very informative <a href="http://khanlou.com/2015/04/view-controllers-in-cells/?ref=williamboles.com">article</a> by <a href="https://twitter.com/khanlou?ref=williamboles.com">Soroush Khanlou</a> and his suggestion that the best way to implement a collection view inside a collection view was by using child view controllers - with each child view controller implementing its own collection view and having its view added as a subview on one of the parent view controller&apos;s cells.</p>
<p>If you haven&apos;t read that article, I recommend that you do as it sets out the argument for why you would want to put view controllers inside cells very well. And even if you don&apos;t need to be convinced I would still recommend as the rest of this article won&apos;t make sense if you don&apos;t &#x1F609;.</p>
<p><img src="https://williamboles.com/content/images/2019/11/stained-glass-spiral-min-1.jpg" alt="Hosting ViewControllers in Cells" loading="lazy"></p>
<p>The article itself is a few years old with the examples are in Objective-C, so I converted it over to Swift and plugged the solution into my project. I ended with the following collection view cell subclass:</p>
<pre><code>class HostedViewCollectionViewCell: UICollectionViewCell {

    // MARK: - HostedView

    // 1
    weak var hostedView: UIView? {
        didSet {
          // 2
          if let oldValue = oldValue {
              oldValue.removeFromSuperview()
          }

          // 3
          if let hostedView = hostedView {
              hostedView.frame = contentView.bounds
              contentView.addSubview(hostedView)
          }
        }
    }

    // MARK: - Reuse

    // 4
    override func prepareForReuse() {
        super.prepareForReuse()

        hostedView = nil
    }
}</code></pre>
<ol>
<li>Each cell holds a reference to a (child) view controller&apos;s view via the <code>hostedView</code>. Whenever that <code>hostedView</code> property is set, the above <code>didSet</code> observer code is triggered.</li>
<li>If <code>hostedView</code> was previously set that old <code>hostedView</code> is removed from the cells view hierarchy.</li>
<li>If the current <code>hostedView</code> value is non-nil, it is added as a subview of the cell&apos;s <code>contentView</code>.</li>
<li>To improve performance, a collection view will only create enough cells to fill the visible UI and then a few more to allow for smooth scrolling. When a cell scrolls off-screen, it is marked for reuse on a different index path. <code>prepareForReuse()</code> is called just before the cell is reused and gives us the opportunity to reset that cell. In the above <code>prepareForReuse()</code>, <code>hostedView</code> is set to <code>nil</code> so triggering its removal from the cells view hierarchy.</li>
</ol>
<p>To begin with, this solution worked well. However, I started noticing that occasionally a cell would <em>forget</em> its content. It was infrequent and would be resolved by scrolling the collection view. However, I was pretty dissatisfied by experience and wanted to understand what was causing this UI breakdown.</p>
<p>Looking over that <code>prepareForReuse()</code> method, you can see that <code>removeFromSuperview()</code> is called to do the removing - what&apos;s interesting about <code>removeFromSuperview()</code> is that it takes no arguments and instead uses the soon-to-be-removed view&apos;s <code>superview</code> value to determine what view to remove the caller from. As a view can only have one <code>superview</code> if a view which already has a superview is added as a subview to a different view that original connection to the first superview is broken and replaced with this new connection. For the most part, this <code>1-to-M</code> mapping between a superview and its subviews works just fine as most views once added as subviews do not tend to move around. However, cells are designed to be reused. The reusable nature of cells lies at the root of my cells <em>forgetfulness</em>. By using the solution above we end up the following unintended associations:</p>
<p><img src="https://williamboles.com/content/images/2020/05/hosting_viewcontrollers_in_cells.jpg" alt="Hosting ViewControllers in Cells" loading="lazy"></p>
<p>The diagram above shows how multiple cells can be associated (shown in purple) with the same view controller&apos;s view, but only one of those cells has the view of <code>ViewController</code> as a subview (shown in green). Because of the multiple references kept to the same view of <code>ViewController</code> it&apos;s possible for any of <code>Cell A</code>, <code>Cell B</code> or <code>Cell C</code> to remove the <code>hostedView</code> from <code>Cell C</code> by calling <code>removeFromSuperview()</code> in their own <code>prepareForReuse()</code> method. Of course, it was not intentional for multiple cells to have an active reference to a view controller&apos;s view if that view was no longer part of the cell&apos;s view hierarchy.</p>
<p>Once those unintended left-over <code>hostedView</code> references were spotted, the solution for the bug became straightforward - only remove the <code>hostedView</code> if it is still in the cell&apos;s view hierarchy:</p>
<pre><code class="swift">class HostedViewCollectionViewCell: UICollectionViewCell {
    var hostedView: UIView? {
        didSet {
            if let oldValue = oldValue {
                if oldValue.isDescendant(of: self) { //Make sure that hostedView hasn&apos;t been added as a subview to a different cell
                    oldValue.removeFromSuperview()
                }
            }

            //Omitted rest of observer
        }
    }

    //Omitted methods
}</code></pre>
<p>So now the cell will only remove the <code>hostedView</code> from its <code>superview</code> if that <code>superview</code> is part of the cell. This additional <code>if</code> statement addresses the <em>forgetfulness</em> that I was seeing. However, if you queried <code>hostedView</code> on <code>Cell A</code> or <code>Cell B</code> from the above diagram you would still get back a reference to the view that <code>Cell C</code>. With the above <code>if</code> statement, we have only resolved part of the bug. Let&apos;s make the cell only return a <code>hostedView</code> value if that <code>hostedView</code> is <em>actually</em> part of its view hierarchy:</p>
<pre><code class="swift">class HostedViewCollectionViewCell: UICollectionViewCell {

    // MARK: - HostedView

    // 1
    private weak var _hostedView: UIView? {
        didSet {
            if let oldValue = oldValue {
                if oldValue.isDescendant(of: self) { //Make sure that hostedView hasn&apos;t been added as a subview to a different cell
                    oldValue.removeFromSuperview()
                }
            }

            if let _hostedView = _hostedView {
                _hostedView.frame = contentView.bounds
                contentView.addSubview(_hostedView)
            }
        }
    }

    // 2
    weak var hostedView: UIView? {
        // 3
        get {
            guard _hostedView?.isDescendant(of: self) ?? false else {
                _hostedView = nil
                return nil
            }

            return _hostedView
        }
        //4
        set {
            _hostedView = newValue
        }
    }

    //Omitted methods
}</code></pre>
<ol>
<li>The private <code>_hostedView</code> property assumes the responsibilities of the previous <code>hostedView</code> property implementation and acts as <em>backing-store</em> property to the new <code>hostedView</code> property implementation. The <code>_hostedView</code> is what now actually holds a reference to the view controller&apos;s view even though the outside world still thinks it is <code>hostedView</code> that holds the reference. Just like before there the <code>didSet</code> observer which checks if the the <code>oldValue</code> of the <code>_hostedView</code> isn&apos;t <code>nil</code> and if it isn&apos;t, removes that view from the cell&apos;s view hierarchy if <code>_hostedView</code> is still a subview. If the current value of <code>_hostedView</code> is not nil, <code>_hostedView</code> is added as a subview of the current cell.</li>
<li>The externally accessible <code>hostedView</code> property now has a custom <code>get</code> and <code>set</code>.</li>
<li>The <code>get</code> only returns the <code>_hostedView</code> value if that view is still a subview of the cell. If <code>hostedView</code> isn&apos;t a subview, the <code>get</code> sets <code>_hostedView</code> to nil (which will cause it to be removed from this cell&apos;s view hierarchy) and returns <code>nil</code>. Dropping the reference once something tries to access <code>hostedView</code> feels a little strange. However, as the <code>superview</code> property of <code>UIView</code> isn&apos;t <a href="https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift?ref=williamboles.com">KVO compliant</a> (and we don&apos;t want to get involved in the dark-arts of <a href="https://nshipster.com/method-swizzling/?ref=williamboles.com">method swizzling</a> to make it so) there is no way for us to know that the <code>hostedView</code> has a new <code>superview</code> without querying the <code>hostedView</code> and there is no point in querying the <code>hostedView</code> until something tries to access it - so a little self-contained strangeness is the only viable option here.</li>
<li>The <code>set</code> takes the new value and assigns it to the backing-store property.</li>
</ol>
<blockquote>
<p>With a backing-store property it is essential that you practice good access hygiene - <code>_hostedView</code> should only be directly accessed in either <code>hostedView</code> or <code>_hostedView</code> everywhere else should use <code>hostedView</code>.</p>
</blockquote>
<p>You can see that by truthfully returned <code>hostedView</code> we have added a lot of complexity to our cell, but none of that complexity leaks out and we can have confidence that hosting a view controller&apos;s view won&apos;t lead to unintended consequences.</p>
<h3 id="seeingtheforyourself">Seeing the &#x1F41B; for yourself</h3>
<p>If you want to see this bug in the wild, you can download the example project from my <a href="https://github.com/wibosco/ViewControllerInCell-Example?ref=williamboles.com">repo</a>. In that example project, I&apos;ve added logging for when the view controller&apos;s view isn&apos;t a subview on that cell anymore so that you can easily see when that bug would have happened by watching the console. If you want to see the bug&apos;s impact on the UI, comment out the <code>isDescendant(of:_)</code> check, in <code>HostedViewCollectionViewCell</code>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Can Unit Testing and Core Data become BFFs?]]></title><description><![CDATA[Core Data and Unit Testing haven't always been the best of friends. Like two members of the same friend group who don't really know each other but really like their UIKit friend]]></description><link>https://williamboles.com/can-unit-testing-and-core-data-become-bffs/</link><guid isPermaLink="false">5a856edddeb9b300182825f9</guid><category><![CDATA[core data]]></category><category><![CDATA[testing]]></category><category><![CDATA[persistence]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Mon, 02 Apr 2018 17:31:28 GMT</pubDate><media:content url="https://williamboles.com/content/images/2018/03/BFFs-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2018/03/BFFs-1.jpg" alt="Can Unit Testing and Core Data become BFFs?"><p>Core Data and Unit Testing haven&apos;t always been the best of friends. Like two members of the same friend group who don&apos;t really know each other but really like their <code>UIKit</code> friend, Core Data and Unit Testing have in fact discovered that they have a lot in common and have gradually got more and more friendly with each other.</p>
<p><img src="https://williamboles.com/content/images/2018/03/BFFs.jpg" alt="Can Unit Testing and Core Data become BFFs?" loading="lazy"></p>
<p>But before we delve into how they can take it one step further and become firm friends, we need to understand what makes each of them tick.</p>
<h3 id="gettingtoknoweachother">Getting to know each other</h3>
<h6 id="coredata">Core Data</h6>
<p><a href="https://developer.apple.com/documentation/coredata?ref=williamboles.com">Core Data</a> is an object graph manager that helps you create the model layer of your app. An object graph is a collection of objects connected to each other through relationships. Core Data abstracts away the lifecycle of the objects it controls, providing tools to allow <a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete?ref=williamboles.com">CRUD</a> operations to be performed on those objects in its graph. In one configuration, Core Data&apos;s object graph can be persisted between executions of the app, with those objects being persisted in either XML, binary, or SQLite stores. However in another configuration, the object graph exists purely in-memory and dies when the app is killed. In fact, the range of configuration options available to Core Data makes it very difficult to define what Core Data actually is - in one configuration Core Data can resemble a SQL wrapper while in a different configuration it looks more like an <a href="https://en.wikipedia.org/wiki/Object-relational_mapping?ref=williamboles.com">ORM</a>. The truth is that Core Data sits somewhere between both, with the ability to <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html?ref=williamboles.com">intelligently</a> load a subset of its store&apos;s data into memory as custom domain objects that can be manipulated like any other Swift object before being &quot;saved&quot; back to the store where those changes are then persistent (be that across app executions or just in that app execution).</p>
<p>A consequence of this flexibility is it&apos;s not uncommon to see two apps using Core Data in significantly different ways. This has led to Core Data gaining a reputation as a <em>hard to use</em> framework (not helped by the significant changes that the framework has gone through since inception). To avoid any confusion around what configuration of Core Data we are going to use, in this post our setup will look like:</p>
<p><img src="https://williamboles.com/content/images/2019/11/Core-Data-Stack-Parent-Child.jpg" alt="Can Unit Testing and Core Data become BFFs?" loading="lazy"></p>
<ul>
<li><a href="https://developer.apple.com/documentation/coredata/nspersistentcontainer?ref=williamboles.com"><code>Persistent Container</code></a> is a fairly new member of the Core Data family. It was introduced in iOS 10 to help simplify the creation of the managed object model, persistent store coordinator and any managed object contexts.</li>
<li><a href="https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext?ref=williamboles.com"><code>Managed Object Context</code></a> is a temporary, in-memory record of all <a href="https://developer.apple.com/documentation/coredata/nsmanagedobject?ref=williamboles.com"><code>NSManagedObject</code></a> instances accessed, created or updated during its lifecycle. An app will typically have multiple contexts in existence at any one given time. These contexts will form a parent-child relationship. When a child context is saved it will push its changes to its parent&apos;s context which will then merge these changes into its own state. At the top of this parent-child hierarchy is the <code>main</code> context, this context upon being saved will push its changes into the persistent store.</li>
<li><a href="https://developer.apple.com/documentation/coredata/nspersistentstorecoordinator?ref=williamboles.com"><code>Persistent Store Coordinator</code></a> acts as an aggregator between the various different contexts to ensure the integrity of the persistent store(s). It does this by serialising read/write operations from the contexts to its store(s). There is only one coordinator per Core Data stack.</li>
<li><a href="https://developer.apple.com/library/content/documentation/DataManagement/Devpedia-CoreData/managedObjectModel.html?ref=williamboles.com###//apple_ref/doc/uid/TP40010398-CH30-SW1"><code>Managed Object Model</code></a> is a set of entities that define each <code>NSManagedObject</code> subclass. An entity can be thought of as a table in a database.</li>
<li><a href="https://developer.apple.com/library/content/documentation/DataManagement/Devpedia-CoreData/persistentObjectStore.html?ref=williamboles.com"><code>Persistent Object Store</code></a> is an abstraction over the actual storage of our data. Handles communication with that storage e.g. with SQLite storage, converts fetch requests into SQL statements.</li>
<li><a href="https://developer.apple.com/library/content/documentation/DataManagement/Devpedia-CoreData/persistentStore.html?ref=williamboles.com###//apple_ref/doc/uid/TP40010398-CH29-SW1"><code>Storage</code></a> it&apos;s common for this to be a SQLite store but the store could also be: XML, binary and in-memory.</li>
</ul>
<h6 id="unittesting">Unit Testing</h6>
<p><a href="https://en.wikipedia.org/wiki/Unit_testing?ref=williamboles.com">Unit Testing</a> is ensuring that the smallest part (unit) of testable code in your app behaves as expected in isolation (from the other parts of your app). In object-oriented programming, a unit is often a particular method, with the unit test testing one scenario (path) through that method. A unit test does this by providing a strict, written contract that the unit under test must satisfy in order to pass. If there are multiple paths through a unit i.e. an <code>if</code> and <code>else</code> branch, then more than one unit test would be required to cover each path.</p>
<p>Unit tests are then combined into a test suite within a test target/project, this suite can then be run to give an increased level of confidence in that the app classes are valid.</p>
<p>Each unit test should be executed in isolation from other unit tests to ensure that the failure of a previous test has no impact upon the next test. It is up to the unit test to ensure that any conditions (test data, user permissions, etc) that it depends on are present before the test is run and it has to tidy up after itself when it is finished. This helps to ensure that the unit test is repeatable and not dependent upon any state on the host environment. The unit test is also responsible for ensuring that the unit under test is isolated from other methods within the app and that any calls (relationship) it makes for information with other methods are mocked out. A mocked method will then return a known, preset value without performing any computation so that if a unit test fails we can have confidence that it has failed because of the code under test rather than having to hunt down the failure in its dependencies.</p>
<p>Each unit test should be as quick as possible to run to ensure that during development, the feedback loop between running the unit test and making code changes is as small as possible.</p>
<h3 id="buildingthatfriendship">Building that friendship</h3>
<p>From the above descriptions, we can see why Core Data and Unit Testing didn&apos;t instantly hit it off. Their differences centre on two issues:</p>
<p><strong>Treatment of data</strong></p>
<ul>
<li>Unit Testing treats data as ephemeral</li>
<li>The main use case of Core Data is persisting data between app executions</li>
</ul>
<p><strong>Tolerance for delays</strong></p>
<ul>
<li>Unit tests should be lightning quick to execute</li>
<li>Core Data typically has to communicate with a SQLite file on disk which is slow (when compared with pure memory operations)</li>
</ul>
<p>And like building any good relationship, all the changes will come from one side &#x1F61C; - Core Data.</p>
<p>(Ok ok, I&apos;m sketching a little bit now so please don&apos;t take that as genuine relationship advice)</p>
<h6 id="coredatamanager">CoreDataManager</h6>
<p>Let&apos;s build a typical Core Data stack together and unit test it as we go.</p>
<p>If you want to follow along, head over to my repo and download the completed <a href="https://github.com/wibosco/TestingWithCoreData-Example?ref=williamboles.com">project</a>.</p>
<p>Let&apos;s start with the manager that will handle setting up our Core Data stack:</p>
<pre><code class="swift">class CoreDataManager {

    // MARK: - Singleton

    static let shared = CoreDataManager()
}</code></pre>
<p>We could add a unit test here and assert that the same instance of <code>CoreDataManager</code> was always returned when <code>shared</code> was called however when unit testing we should only test code that we control and the logic behind creating a singleton is handled by Swift itself - so no need to create that test class yet.</p>
<blockquote>
<p>Unit tests while asserting the <em>correctness</em> of an implementation also act as a living form of documentation so if you did want to add a unit test to assert the same instance was returned, I won&apos;t put up too much of a fight.</p>
</blockquote>
<p>As our project is being developed with an iOS deployment target of iOS 11 we can use the persistent container to simplify the Core Data stack&apos;s setup.</p>
<pre><code class="swift">lazy var persistentContainer: NSPersistentContainer! = {
    let persistentContainer = NSPersistentContainer(name: &quot;TestingWithCoreData_Example&quot;)

    return persistentContainer
}()</code></pre>
<p>In the above code snippet, we added a lazy property to create an instance of <code>NSPersistentContainer</code>. Loading a Core Data stack can be a time-consuming task especially if migration is required. To handle this we need to add a dedicated asynchronous setup method that can handle any time-consuming tasks.</p>
<blockquote>
<p>While the below method doesn&apos;t actually show the migration itself, I think it&apos;s important to create and test a realistic Core Data stack and data migrations are very much a common task in iOS apps.</p>
</blockquote>
<pre><code class="swift">// MARK: - SetUp

 func setup(completion: (() -&gt; Void)?) {
     loadPersistentStore {
         completion?()
     }
 }

 // MARK: - Loading

 private func loadPersistentStore(completion: @escaping () -&gt; Void) {
     //handle data migration on a different thread/queue here
     persistentContainer.loadPersistentStores { description, error in
         guard error == nil else {
             fatalError(&quot;was unable to load store \(error!)&quot;)
         }

         completion()
     }
 }</code></pre>
<p>Ok so now we have something to test - does calling <code>setup</code> actually set up our stack. Let&apos;s create that unit test class:</p>
<pre><code class="swift">class CoreDataManagerTests: XCTestCase {

    // MARK: Properties

    var sut: CoreDataManager!

    // MARK: - Lifecycle

    override func setUp() {
        super.setUp()

        sut = CoreDataManager()
    }

    // MARK: - Tests

    // MARK: Setup

    func test_setup_completionCalled() {
        let setupExpectation = expectation(description: &quot;set up completion called&quot;)

        sut.setup() {
            setupExpectation.fulfill()
        }

        waitForExpectations(timeout: 1.0, handler: nil)
    }
}</code></pre>
<p>In the above class, we declare a property <code>sut</code> (<code>s</code>ubject <code>u</code>nder <code>t</code>est) which will hold a <code>CoreDataManager</code> instance that we will be testing. I prefer to use <code>sut</code> as it makes it immediately obvious which object we are testing and which objects are collaborators/dependencies. It&apos;s important to note that the <code>sut</code> property is an implicitly unwrapped optional - it&apos;s purely to make our unit tests more readable by avoiding having to handle it&apos;s optional nature elsewhere and is a technique that I would not recommend using too widely in production code. The test suite&apos;s <code>setUp</code> method is where the <code>CoreDataManager</code> instance is being created and assigned to <code>sut</code>.</p>
<p>Let&apos;s take a closer look at the unit test itself:</p>
<pre><code class="swift">func test_setup_completionCalled() {
    let setupExpectation = expectation(description: &quot;set up completion called&quot;)

    sut.setup() {
        setupExpectation.fulfill()
    }

    waitForExpectations(timeout: 1.0, handler: nil)
}</code></pre>
<p>When it comes to naming I follow the naming convention:</p>
<p><code>test_[unit under test]_[condition]_[expected outcome]</code></p>
<blockquote>
<p><code>[condition]</code> is optional.</p>
</blockquote>
<p>So the test method signature tells us that we are testing the <code>setup</code> method and that the <code>completion</code> closure should be triggered.</p>
<p>Now with this test, we are really jumping straight into the deeper end of unit testing by testing an asynchronous method but as we can see the code isn&apos;t actually that difficult to understand. The first thing we do is create an <code>XCTestExpectation</code> instance, it&apos;s important to note here that we are not directly creating an <code>XCTestExpectation</code> instance using <code>XCTestExpectation</code>&apos;s init method instead we are using the convenience method provided by <code>XCTestCase</code>. By creating it via <code>XCTestCase</code> we will tie both the <code>XCTestExpectation</code> and <code>XCTestCase</code> together which will allow us to use <code>waitForExpectations</code> and cut down on some of the boilerplate required with expectations. If you have never used expectations before, you can think of them as promising that an action will happen within a certain time frame. Sadly like actual promises, they can be broken and when they are - the test fails.</p>
<p>As I&apos;m sure you have noted, <code>test_setup_completionCalled</code> doesn&apos;t actually contain any <em>asserts</em>, this is because we are using the expectation as an implicit assert.</p>
<p>So we&apos;ve tested that the <code>completion</code> closure is called but we haven&apos;t actually checked that anything was set up. A successful set up should result in our persistent store being loaded so let&apos;s add a test to check that:</p>
<pre><code class="swift">func test_setup_persistentStoreCreated() {
   let setupExpectation = expectation(description: &quot;set up completion called&quot;)

    sut.setup() {
        setupExpectation.fulfill()
    }

    waitForExpectations(timeout: 1.0) { (_) in
        XCTAssertTrue(self.sut.persistentContainer.persistentStoreCoordinator.persistentStores.count &gt; 0)
    }
}</code></pre>
<p>As we can see <code>test_setup_persistentStoreCreated</code> contains an assert to check that the <code>persistentStoreCoordinator</code> has at least one <code>persistentStore</code>. It&apos;s important to note that as <code>persistentContainer</code> is lazy loaded merely checking that it&apos;s not nil wouldn&apos;t be a valid test as calling the property would result in creating the <code>persistentContainer</code>.</p>
<p>The two unit tests that we have added are very similar and as you can see it&apos;s possible for <code>test_setup_persistentStoreCreated</code> to fail for two reasons:</p>
<ol>
<li>Completion closure not triggered</li>
<li>Persistent store not created</li>
</ol>
<p>The first reason is actually being tested in <code>test_setup_completionCalled</code> so why have I created another test that&apos;s dependent on it here? The reason is that it&apos;s actually impossible not to check this condition as it&apos;s an implicit dependency on any test that uses this method. Now the argument could be made that these two tests should be one - effectively a <code>test_setup_stackCreated</code> test. I opted for two tests as I felt that it improved the readability in the event that one of those tests failed by providing a higher level of granularity for that happening. You sometimes hear people saying that a unit test should only ever have one assert and that any unit test that has more than one assert is <em>wrong</em>. IMHO, this is foolhardy. There are very few hard and fast rules in life, just about everything is context based - in this context having two asserts (one implicit, one explicit) in <code>test_setup_persistentStoreCreated</code> makes sense as both asserts are checking that the same unit of functionality is correct.</p>
<p>Now, the more eagled eyed &#x1F440; among you will have spotted that we are using the default storage type for our Core Data stack (<code>NSSQLiteStoreType</code>) in the above tests - this creates a SQLite file on the disk and as we know any I/O operation is going to be much slower than a pure in-memory operation. It would be great if we could tell the Core Data stack which storage type to use - <code>NSSQLiteStoreType</code> for production and <code>NSInMemoryStoreType</code> for testing:</p>
<pre><code class="swift">class CoreDataManager {

    private var storeType: String!

    lazy var persistentContainer: NSPersistentContainer! = {
        let persistentContainer = NSPersistentContainer(name: &quot;TestingWithCoreData_Example&quot;)
        let description = persistentContainer.persistentStoreDescriptions.first
        description?.type = storeType

        return persistentContainer
    }()

    // MARK: - Singleton

    static let shared = CoreDataManager()

    // MARK: - SetUp

    func setup(storeType: String = NSSQLiteStoreType, completion: (() -&gt; Void)?) {
        self.storeType = storeType

        loadPersistentStore {
            completion?()
        }
    }

    // MARK: - Loading

    private func loadPersistentStore(completion: @escaping () -&gt; Void) {
        persistentContainer.loadPersistentStores { description, error in
            guard error == nil else {
                fatalError(&quot;was unable to load store \(error!)&quot;)
            }

            completion()
        }
    }
}</code></pre>
<p>As a long-term Objective-C iOS developer, I still get a thrill about being able to provide a default value to a parameter (like we do with the <code>storeType</code> parameter above) without having to create a whole new method. This change will allow us to use the much quicker <code>NSInMemoryStoreType</code> storage type in our tests while keeping a simple interface in production. However, it&apos;s not <em>free</em> and because we have introduced a new way of setting up our Core Data stack we need to update our existing tests to test this new path:</p>
<pre><code class="swift">class CoreDataManagerTests: XCTestCase {

    // MARK: Properties

    var sut: CoreDataManager!

    // MARK: - Lifecycle

    override func setUp() {
        super.setUp()

        sut = CoreDataManager()
    }

    // MARK: - Tests

    // MARK: Setup

    func test_setup_completionCalled() {
        let setupExpectation = expectation(description: &quot;set up completion called&quot;)

        sut.setup(storeType: NSInMemoryStoreType) {
            setupExpectation.fulfill()
        }

        waitForExpectations(timeout: 1.0, handler: nil)
    }

    func test_setup_persistentStoreCreated() {
       let setupExpectation = expectation(description: &quot;set up completion called&quot;)

        sut.setup(storeType: NSInMemoryStoreType) {
            setupExpectation.fulfill()
        }

        waitForExpectations(timeout: 1.0) { (_) in
            XCTAssertTrue(self.sut.persistentContainer.persistentStoreCoordinator.persistentStores.count &gt; 0)
        }
    }

    func test_setup_persistentContainerLoadedOnDisk() {
        let setupExpectation = expectation(description: &quot;set up completion called&quot;)
        
        sut.setup {
            XCTAssertEqual(self.sut.persistentContainer.persistentStoreDescriptions.first?.type, NSSQLiteStoreType)
            setupExpectation.fulfill()
        }
        
        waitForExpectations(timeout: 1.0) { (_) in
            self.sut.persistentContainer.destroyPersistentStore()
        }
    }

    func test_setup_persistentContainerLoadedInMemory() {
        let setupExpectation = expectation(description: &quot;set up completion called&quot;)

        sut.setup(storeType: NSInMemoryStoreType) {
            XCTAssertEqual(self.sut.persistentContainer.persistentStoreDescriptions.first?.type, NSInMemoryStoreType)
            setupExpectation.fulfill()
        }

        waitForExpectations(timeout: 1.0, handler: nil)
    }
}</code></pre>
<p>If we run the above tests we are able to see the difference in running times between <code>test_setup_persistentContainerLoadedInMemory</code> and <code>test_setup_persistentContainerLoadedOnDisk</code>:</p>
<p><img src="https://williamboles.com/content/images/2018/03/Persistent-Store-Loading-Times.png" alt="Can Unit Testing and Core Data become BFFs?" loading="lazy"></p>
<p>As you can see on the above execution of both tests, loading the store on disk took <strong>17</strong> times longer than loading the store into memory - <code>0.001</code> vs <code>0.017</code> seconds.</p>
<p>In real terms, this speed increase isn&apos;t much on its own but once we start adding in tests that create, update and delete <code>NSManagedObject</code> instances dealing with an in-memory store will allow these tests to be executed faster than if we used an on-disk store.</p>
<p>So far, we have made great progress on producing a Core Data stack that is unit testable but a Core Data stack without a context (or two) isn&apos;t going to be very useful - let&apos;s add some:</p>
<pre><code class="swift">lazy var backgroundContext: NSManagedObjectContext = {
    let context = self.persistentContainer.newBackgroundContext()
    context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

    return context
}()

lazy var mainContext: NSManagedObjectContext = {
    let context = self.persistentContainer.viewContext
    context.automaticallyMergesChangesFromParent = true

    return context
}()</code></pre>
<p>And we need to add some unit tests for them:</p>
<pre><code class="swift">func test_backgroundContext_concurrencyType() {
    let setupExpectation = expectation(description: &quot;background context&quot;)

    sut.setup(storeType: NSInMemoryStoreType) {
        XCTAssertEqual(self.sut.backgroundContext.concurrencyType, .privateQueueConcurrencyType)
        setupExpectation.fulfill()
    }

    waitForExpectations(timeout: 1.0, handler: nil)
}

func test_mainContext_concurrencyType() {
    let setupExpectation = expectation(description: &quot;main context&quot;)

    sut.setup(storeType: NSInMemoryStoreType) {
        XCTAssertEqual(self.sut.mainContext.concurrencyType, .mainQueueConcurrencyType)
        setupExpectation.fulfill()
    }

    waitForExpectations(timeout: 1.0, handler: nil)
}</code></pre>
<p>Good news is that we have finished creating and testing our Core Data stack and I think it wasn&apos;t actually too difficult &#x1F389;.</p>
<h6 id="introducingcolorsdatamanager">Introducing ColorsDataManager</h6>
<p>There is no point in creating a Core Data stack if we don&apos;t actually use it. In the <a href="https://github.com/wibosco/TestingWithCoreData-Example?ref=williamboles.com">example project</a> we populate a collectionview with instances of a subclass of <code>NSManagedObject</code> - <code>Color</code>. To help us deal with these <code>Color</code> objects we will be using a <code>ColorsDataManager</code>:</p>
<pre><code class="swift">class ColorsDataManager {

    let backgroundContext: NSManagedObjectContext

    // MARK: - Init

    init(backgroundContext: NSManagedObjectContext = CoreDataManager.shared.backgroundContext) {
        self.backgroundContext = backgroundContext
    }

    // MARK: - Create

    func createColor() {
        backgroundContext.performAndWait {
            let color = NSEntityDescription.insertNewObject(forEntityName: Color.className, into: backgroundContext) as! Color
            color.hex = UIColor.random.hexString
            color.dateCreated = Date()

            try? backgroundContext.save()
        }
    }
    
    // MARK: - Deletion
    
    func deleteColor(color: Color) {
        let objectID = color.objectID
        backgroundContext.performAndWait {
            if let colorInContext = try? backgroundContext.existingObject(with: objectID) {
                backgroundContext.delete(colorInContext)
                
                try? backgroundContext.save()
            }
        }
    }
}</code></pre>
<p>In the above class, we have a simple manager that handles creating and deleting <code>Color</code> instances. As a responsible member of the Core Data community, our example app treats the <code>backgroundContext</code> as a read-write context and the <code>mainContext</code> as a read-only context - this is to ensure that any time-consuming tasks don&apos;t block the main (UI) thread. You may have noticed that the above class doesn&apos;t actually contain any mention of <code>CoreDataManager</code> instead this class only knows about the background context. By injecting this context into the class, we are able to decouple <code>ColorsDataManager</code> from <code>CoreDataManager</code> which should allow us to more easily test <code>ColorsDataManager</code> &#x1F609;.</p>
<p>Let&apos;s look at implementing our first test for <code>ColorsDataManager</code>:</p>
<pre><code class="swift">class ColorsDataManagerTests: XCTestCase {

    // MARK: Properties

    var sut: ColorsDataManager!

    var coreDataStack: CoreDataTestStack!

    // MARK: - Lifecycle

    override func setUp() {
        super.setUp()

        coreDataStack = CoreDataTestStack()

        sut = ColorsDataManager(backgroundContext: coreDataStack.backgroundContext)
    }

    // MARK: - Tests

    // MARK: Init

    func test_init_contexts() {
        XCTAssertEqual(sut.backgroundContext, coreDataStack.backgroundContext)
    }
}</code></pre>
<p>As we can see, the majority of the above class is taken up with setting up the test suite. The test itself, merely checks that the context that we pass into <code>ColorsDataManager</code> is the same context that is assigned to the <code>backgroundContext</code> property.</p>
<p>You may also have noticed that <code>coreDataStack</code> isn&apos;t a <code>CoreDataManager</code> instance but instead a <code>CoreDataTestStack</code> instance.</p>
<p>Let&apos;s go on a slight detour and have a look at <code>CoreDataTestStack</code>:</p>
<pre><code class="swift">class CoreDataTestStack {

    let persistentContainer: NSPersistentContainer
    let backgroundContext: NSManagedObjectContextSpy
    let mainContext: NSManagedObjectContextSpy

    init() {
        persistentContainer = NSPersistentContainer(name: &quot;TestingWithCoreData_Example&quot;)
        let description = persistentContainer.persistentStoreDescriptions.first
        description?.type = NSInMemoryStoreType

        persistentContainer.loadPersistentStores { description, error in
            guard error == nil else {
                fatalError(&quot;was unable to load store \(error!)&quot;)
            }
        }

        mainContext = NSManagedObjectContextSpy(concurrencyType: .mainQueueConcurrencyType)
        mainContext.automaticallyMergesChangesFromParent = true
        mainContext.persistentStoreCoordinator = persistentContainer.persistentStoreCoordinator

        backgroundContext = NSManagedObjectContextSpy(concurrencyType: .privateQueueConcurrencyType)
        backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        backgroundContext.parent = self.mainContext
    }
}</code></pre>
<p><code>CoreDataTestStack</code> is very similar to <code>CoreDataManager</code> but with its asynchronous setup behaviour stripped out and the <code>storeType</code> always set to <code>NSInMemoryStoreType</code>. This class allows us to more easily set up and tear down the stack between tests without having to wait on any asynchronous tasks to complete. Another difference between <code>CoreDataTestStack</code> and <code>CoreDataManager</code> is that the two contexts that are being created are not in fact standard <code>NSManagedObjectContext</code> instances but are actually <code>NSManagedObjectContextSpy</code> instances.</p>
<blockquote>
<p>If you are curious as to what a <code>spy</code> is, Martin Fowler has produced a very insightful <a href="https://martinfowler.com/articles/mocksArentStubs.html?ref=williamboles.com">article</a> on naming test objects - it&apos;s in the section titled: <code>The Difference Between Mocks and Stubs</code>.</p>
</blockquote>
<p><code>NSManagedObjectContextSpy</code> is a subclass of <code>NSManagedObjectContext</code> that adds special state tracking properties. System classes occupy a grey area when it comes to if you should use them in your tests or if you need to replace them with mock/stub instances. In this case, I felt that mocking out a context&apos;s functionality would be too much work and would actually be counter-productive to what the test is attempting to achieve so I&apos;m perfectly happy to use it directly.</p>
<pre><code class="swift">class NSManagedObjectContextSpy: NSManagedObjectContext {
    var expectation: XCTestExpectation?

    var saveWasCalled = false

    // MARK: - Perform

    override func performAndWait(_ block: () -&gt; Void) {
        super.performAndWait(block)

        expectation?.fulfill()
    }

    // MARK: - Save

    override func save() throws {
        save()

        saveWasCalled = true
    }
}</code></pre>
<p>Ok, detour over. Let&apos;s get back to testing the <code>ColorsDataManager</code> class:</p>
<pre><code class="swift">func test_createColor_colorCreated() {
    let performAndWaitExpectation = expectation(description: &quot;background perform and wait&quot;)
    coreDataStack.backgroundContext.expectation = performAndWaitExpectation

    sut.createColor()

    waitForExpectations(timeout: 1) { (_) in
        let request = NSFetchRequest<color>.init(entityName: Color.className)
        let colors = try! self.coreDataStack.backgroundContext.fetch(request)

        guard let color = colors.first else {
            XCTFail(&quot;color missing&quot;)
            return
        }

        XCTAssertEqual(colors.count, 1)
        XCTAssertNotNil(color.hex)
        XCTAssertEqual(color.dateCreated?.timeIntervalSinceNow ?? 0, Date().timeIntervalSinceNow, accuracy: 0.1)
        XCTAssertTrue(self.coreDataStack.backgroundContext.saveWasCalled)
    }
}</color></code></pre>
<p>There is a bit more happening here than in the previous test that we saw. We create an <code>XCTestExpectation</code> instance that we then assign to the <code>expectation</code> property on the context. As we have seen above this expectation should be <em>fulfilled</em> when <code>performAndWait</code> is called. Once that expectation has been triggered, we then check that the <code>Color</code> instance was created and <em>saved</em> into our persistent store.</p>
<p>Testing the deletion of a <code>Color</code> follows a similar pattern:</p>
<pre><code class="swift">func test_deleteColor_colorDeleted() {
    let performAndWaitExpectation = expectation(description: &quot;background perform and wait&quot;)
    coreDataStack.backgroundContext.expectation = performAndWaitExpectation
    
    let colorA = NSEntityDescription.insertNewObject(forEntityName: Color.className, into: self.coreDataStack.backgroundContext) as! Color
    let colorB = NSEntityDescription.insertNewObject(forEntityName: Color.className, into: self.coreDataStack.backgroundContext) as! Color
    let colorC = NSEntityDescription.insertNewObject(forEntityName: Color.className, into: self.coreDataStack.backgroundContext) as! Color
    
    sut.deleteColor(color: colorB)
    
    waitForExpectations(timeout: 1) { (_) in
        let request = NSFetchRequest<color>.init(entityName: Color.className)
        let backgroundContextColors = try! self.coreDataStack.backgroundContext.fetch(request)
        
        XCTAssertEqual(backgroundContextColors.count, 2)
        XCTAssertTrue(backgroundContextColors.contains(colorA))
        XCTAssertTrue(backgroundContextColors.contains(colorC))
        XCTAssertTrue(self.coreDataStack.backgroundContext.saveWasCalled)
    }
}</color></code></pre>
<p>We first populate our persistent (in-memory) store, call the <code>deleteColor</code> method and then check that the correct <code>Color</code> has been deleted. There is one special case - because we read on the  main context and delete on the background context, the <code>color</code> instance passed into this method may be from the main context, the above test is not covering this case so let&apos;s add another test that does:</p>
<pre><code class="swift">func test_deleteColor_switchingContexts_colorDeleted() {
    let performAndWaitExpectation = expectation(description: &quot;background perform and wait&quot;)
    coreDataStack.backgroundContext.expectation = performAndWaitExpectation
    
    let colorA = NSEntityDescription.insertNewObject(forEntityName: Color.className, into: self.coreDataStack.backgroundContext) as! Color
    let colorB = NSEntityDescription.insertNewObject(forEntityName: Color.className, into: self.coreDataStack.backgroundContext) as! Color
    let colorC = NSEntityDescription.insertNewObject(forEntityName: Color.className, into: self.coreDataStack.backgroundContext) as! Color
    
    let mainContextColor = coreDataStack.mainContext.object(with: colorB.objectID) as! Color
    
    sut.deleteColor(color: mainContextColor)
    
    waitForExpectations(timeout: 1) { (_) in
        let request = NSFetchRequest<color>.init(entityName: Color.className)
        let backgroundContextColors = try! self.coreDataStack.backgroundContext.fetch(request)
        
        XCTAssertEqual(backgroundContextColors.count, 2)
        XCTAssertTrue(backgroundContextColors.contains(colorA))
        XCTAssertTrue(backgroundContextColors.contains(colorC))
        XCTAssertTrue(self.coreDataStack.backgroundContext.saveWasCalled)
    }
}</color></code></pre>
<p>Pretty much the same as before with the only difference being that we retrieve the <code>color</code> to be deleted from the main context before passing that in.</p>
<p>An interesting point to note when adding those three tests is that we haven&apos;t had to add any code to clear our persistent store. This is because by using an <code>NSInMemoryStoreType</code> store and ensuring that we create a new stack before each test - we never actually persist data. Not only does this save us time having to write the tidy up code, it also removes a whole category of bugs where leftover state from one test affects the outcome of another due to faulty/missing clean up code.</p>
<p>To come back to the point about in-memory stores being quicker to use than on-disk stores, we can see a typical difference in running times for the above tests below:</p>
<p><strong>In-memory store</strong></p>
<p><img src="https://williamboles.com/content/images/2018/03/Timings-of-in-memory-store.png" alt="Can Unit Testing and Core Data become BFFs?" loading="lazy"></p>
<p><strong>SQLite store</strong></p>
<p><img src="https://williamboles.com/content/images/2018/03/Timings-with-SQLite-store.png" alt="Can Unit Testing and Core Data become BFFs?" loading="lazy"></p>
<h3 id="bestfriendsforever">Best Friends Forever?</h3>
<p>In the above code snippets, we have seen that unit testing with Core Data doesn&apos;t need to be that much more difficult than unit testing in general, with most of that added <em>difficulty</em> coming in the set up of the Core Data stack. And while Core Data and Unit Testing may not become BFFs (let&apos;s be honest <code>UIKit</code> has that position sealed down), we&apos;ve seen that they can become firm friends &#x1F46B;. That friendship is built on small alterations to our Core Data stack which allows its data to be more easily thrown away and the use of special subclasses to allow us to better track state.</p>
<p>You can download the example project for this post <a href="https://github.com/wibosco/TestingWithCoreData-Example?ref=williamboles.com">here</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Don't throw anything away with pausable downloads]]></title><description><![CDATA[Once upon a time, an app was just a stripped-down version of a website. Those days are long gone. Our users now expect to be able to do everything through the app that they can do through the website]]></description><link>https://williamboles.com/not-all-downloads-are-born-equal/</link><guid isPermaLink="false">5a856edddeb9b300182825ec</guid><category><![CDATA[networking]]></category><category><![CDATA[imgur-api]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Mon, 05 Mar 2018 20:19:18 GMT</pubDate><media:content url="https://williamboles.com/content/images/2020/05/lighted-up-alleyway-min-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2020/05/lighted-up-alleyway-min-1.jpg" alt="Don&apos;t throw anything away with pausable downloads"><p>Once upon a time, an app was just a stripped-down version of a website. Those days are long gone. Our users now expect to be able to do everything through the app that they can do through the website. This change in expectation has meant that our apps have become increasingly media hungry. Despite network speeds increasing year-on-year, network requests are still the most likely source of bottlenecks, especially media requests. Every time a user has to wait for a network request to complete before they can get on with their task, we risk losing that user.</p>
<p>A common approach to try and alleviate these bottlenecks is to cancel network requests as soon as possible. While this approach is valid in some scenarios, I have too often seen it used naively for all network request. When we cancel a network request, we throw away any progress that request has made in downloading the requested asset. If the app then goes on to request that asset again, the download starts at <strong>0%</strong> and then spend time redownloading data it previously had.</p>
<p><img src="https://williamboles.com/content/images/2020/05/lighted-up-alleyway-min.jpg" alt="Don&apos;t throw anything away with pausable downloads" loading="lazy"></p>
<p>In this post, I want to look at how we can build a better downloading approach that doesn&apos;t throw away data and merges duplicate download requests. All without requiring any extra effort from the consumer of the media layer.</p>
<blockquote>
<p>This post will build up to a working example however if you&apos;re just too excited to wait for that, then head on over to the <a href="https://github.com/wibosco/PausableDownloads-Example?ref=williamboles.com">completed example</a> and take a look at <code>AssetDownloadsSession</code>, <code>AssetDownloadItem</code> and <code>AssetDownloadItemDelegate</code> to see how things end up. To run the example project, follow the instructions <a href="#runningtheexampleproject">below</a>.</p>
</blockquote>
<h3 id="timeforasmalldetour">Time for a small detour &#x1F5FA;&#xFE0F;</h3>
<p>Just because we want to resume a cancelled download does not mean that we can. For resumption to be possible, we need the following to be true:</p>
<ul>
<li>The resource has not changed since it was first requested.</li>
<li>The task is an <code>HTTP</code> or <code>HTTPS</code> <code>GET</code> request.</li>
<li>The server provides either the <code>ETag</code> or <code>Last-Modified</code> header (or both) in its response.</li>
<li>The server supports <a href="https://www.keycdn.com/support/byte-range-requests?ref=williamboles.com">byte-range requests</a>.</li>
</ul>
<p>If all of the above is true then congratulations you are ready for the rest of this article; if the above isn&apos;t true, then you have some work to do before you can implement the below solution.</p>
<p>Now, before we start building our downloading layer, let&apos;s look (briefly) at how iOS handles supporting resuming downloads via <a href="https://developer.apple.com/documentation/foundation/urlsession?ref=williamboles.com"><code>URLSession</code></a>. To resume a download, we need to use a special init&apos;er on <a href="https://developer.apple.com/documentation/foundation/urlsessiondownloadtask?ref=williamboles.com"><code>URLSessionDownloadTask</code></a> - <a href="https://developer.apple.com/documentation/foundation/urlsession/1411598-downloadtask?ref=williamboles.com"><code>downloadTask(withResumeData:completionHandler:)</code></a> (this example is only going to show the completion handler approach if you want to see a delegation based approach <a href="https://twitter.com/a_grebenyuk?ref=williamboles.com">Alexander Grebenyuk</a> has a great article on that approach <a href="https://kean.github.io/post/resumable-downloads?ref=williamboles.com">here</a>). Rather than taking a <code>URL</code> or <code>URLRequest</code>, <code>downloadTask(withResumeData:completionHandler:)</code> takes a <code>Data</code> instance. This <code>Data</code> instance, among other things, tells the <code>URLSessionDownloadTask</code> instance where the already downloaded media data <em>should</em> be on the file system and what parts of the media asset have already been downloaded. We get this <code>Data</code> instance by calling <a href="https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel?ref=williamboles.com"><code>cancel(byProducingResumeData:)</code></a> when cancelling the original <code>URLSessionDownloadTask</code> instance.</p>
<p>If the above introduction to resuming downloads feels too short, don&apos;t fret we will cover the topic in a lot more detail as we build up the media download layer below.</p>
<h3 id="letsgetbuilding">Let&apos;s get building &#x1F477;</h3>
<p>Our media download layer has 5 primary responsibilities:</p>
<ol>
<li>Scheduling downloads.</li>
<li>Pausing downloads.</li>
<li>Resuming paused downloads.</li>
<li>Removing unwanted paused downloads (freeing memory).</li>
<li>Downloading the requested media.</li>
</ol>
<p>These responsibilities together produce the following class structure:</p>
<p><img src="https://williamboles.com/content/images/2020/05/Pausable_downloads.jpg" alt="Don&apos;t throw anything away with pausable downloads" loading="lazy"></p>
<ul>
<li><code>AssetDataManager</code> is the manager for accessing assets, e.g. downloading an asset or locally retrieving a cached asset.</li>
<li><code>AssetDownloadsSession</code> is the controller for scheduling, pausing, resuming, cancelling and deleting download requests.</li>
<li><code>AssetDownloadItem</code> is a wrapper around an <code>URLSessionDownloadTask</code> instance - adding easy-to-use coalescing and pausing functionality around it.</li>
<li><code>AssetDownloadItemDelegate</code> is a protocol to allow an <code>AssetDownloadItem</code> instance to communicate with the <code>AssetDownloadsSession</code> instance.</li>
</ul>
<p>Both <code>AssetDownloadItem</code> and <code>AssetDownloadItemDelegate</code> are private and only visible to <code>AssetDownloadsSession</code>.</p>
<blockquote>
<p>We won&apos;t see the implementation of <code>AssetDataManager</code> as it is the consumer of pausable downloads rather than a part of it. I included it in the above diagram to show how the media download layer can be used.</p>
</blockquote>
<p>Before we start adding in the ability to download an asset, let&apos;s look at the possible lifecycle states an <code>AssetDownloadItem</code> instance can be in:</p>
<ol>
<li><code>Ready</code> indicates that our <code>AssetDownloadItem</code> instance hasn&apos;t started downloading yet.</li>
<li><code>Downloading</code> indicates that our <code>AssetDownloadItem</code> instance is currently downloading.</li>
<li><code>Paused</code> indicates that our <code>AssetDownloadItem</code> instance isn&apos;t actively downloading but has in the past and has kept the previously downloaded data.</li>
<li><code>Cancelled</code> indicates that our <code>AssetDownloadItem</code> instance isn&apos;t actively downloading and has thrown away any previously downloaded data.</li>
<li><code>Completed</code> indicates that our <code>AssetDownloadItem</code> instance download has finished (either successfully or due to a failure) and the work of this <code>AssetDownloadItem</code> instance is over.</li>
</ol>
<p>The lifecycle of an <code>AssetDownloadItem</code> instance looks like:</p>
<p><img src="https://williamboles.com/content/images/2020/05/AssetDownloadItem_lifeycle.jpg" alt="Don&apos;t throw anything away with pausable downloads" loading="lazy"></p>
<p>So a download that starts and completes without pausing or cancelling would look like:</p>
<p><code>Ready</code> -&gt; <code>Downloading</code> -&gt; <code>Completed</code></p>
<p>Whereas another download that has been paused would look more like:</p>
<p><code>Ready</code> -&gt; <code>Downloading</code> -&gt; <code>Paused</code> -&gt; <code>Downloading</code> -&gt; <code>Completed</code></p>
<p>These states are best represented as an enum:</p>
<pre><code class="swift">fileprivate enum State: String {
    case ready
    case downloading
    case paused
    case cancelled
    case completed
}</code></pre>
<p>Now that we have the possible lifecycle states represented, let&apos;s add in the basic structure of <code>AssetDownloadItem</code> to move between those states:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {

    //Omitted other properties

    private(set) var state: State = .ready

    //Omitted other methods

    func resume() {
        state = .downloading
    }

    func cancel() {
        state = .cancelled
    }

    func pause() {
        state = .paused
    }

    private func complete() {
        state = completed
    }
}</code></pre>
<blockquote>
<p>These methods don&apos;t do much yet - we will gradually fill them out as we build up the example.</p>
</blockquote>
<p>Now that we have the lifecyle in, let&apos;s add in the ability to download an asset:</p>
<pre><code class="swift">typealias DownloadCompletionHandler = ((_ result: Result&lt;Data, Error&amp;rt;) -&gt; ())

fileprivate class AssetDownloadItem { // 1
    //Omitted other properties

    private let session: URLSession
    private var downloadTask: URLSessionDownloadTask?

    let url: URL
    var completionHandler: DownloadCompletionHandler? // 2

    // MARK: - Init

    init(session: URLSession, url: URL) {
        self.session = session
        self.url = url
    }

    // MARK: - Lifecycle

    // 3
    func resume() {
        state = .downloading

        os_log(.info, &quot;Creating a new download task&quot;)
        downloadTask = session.downloadTask(with: url, completionHandler: downloadTaskCompletionHandler)

        downloadTask?.resume()
    }

    // 4
    private func downloadTaskCompletionHandler(_ fileLocationURL: URL?, _ response: URLResponse?, _ error: Error?) {
        var result: Result<data, error>
        defer {
            downloadCompletionHandler?(result)

            if state != .paused {
                complete()
            }

            cleanup()
        }

        guard let fileLocationURL = fileLocationURL else {
            result = .failure(NetworkingError.retrieval(underlayingError: error))
            return
        }

        do {
            let data = try Data(contentsOf: fileLocationURL)
            result = .success(data)
        } catch let error {
            result = .failure(NetworkingError.invalidData(underlayingError: error))
        }
    }

    // 5
    private func cleanup() {
        downloadTask = nil
        completionHandler = nil
    }

    //Omitted other methods
}</data,></code></pre>
<blockquote>
<p>If you have cloned the example project, you will notice that I make extensive use of protocols that are not shown above. The protocols are used to allow me to better unit test this solution; I&apos;ve omitted them from this post to make it more readable.</p>
</blockquote>
<p>Here&#x2019;s what we did:</p>
<ol>
<li><code>AssetDownloadItem</code> is a simple wrapper around creating and resuming an <code>URLSessionDownloadTask</code> instance.</li>
<li>The download completion closure that will be used when the download completes.</li>
<li>Mirroring <code>URLSessionDownloadTask</code>,  <code>AssetDownloadItem</code> has its own <code>resume()</code> method that when called causes the download to start.</li>
<li>The completion handler for the download task. Depending on how the download progresses, the <code>completionHandler</code> is triggered either with the requested asset as a <code>Data</code> instance or an error detailing what happened. Only non-paused downloads are considered complete when the closure is called.</li>
<li>Regardless of how a download is completed, we need to clean up this <code>AssetDownloadItem</code> instance by setting its <code>downloadTask</code>  and <code>completionHandler</code> to nil so that they can&apos;t accidentally be reused.</li>
</ol>
<p>The possible errors that can be returned from download request are:</p>
<pre><code class="swift">enum NetworkingError: Error {
    case unknown
    case retrieval(underlayingError: Error?)
    case invalidData(underlayingError: Error?)
}</code></pre>
<p>Now that we can start a download let&apos;s look at how we can cancel a download. <code>URLSessionDownloadTask</code> has two methods for cancellation:</p>
<ol>
<li><a href="https://developer.apple.com/documentation/foundation/urlsessiontask/1411591-cancel?ref=williamboles.com"><code>cancel()</code></a> - download is stopped and any data downloaded so far is discarded.</li>
<li><a href="https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel?ref=williamboles.com"><code>cancel(byProducingResumeData: @escaping (Data?) -&gt; Void)</code></a> - download is stopped and any data downloaded so far is stored in a temporary filesystem location.  Details of the partially completed download are passed back as a <code>Data</code> instance. It&apos;s important to note that the <code>Data</code> instance returned in the completion closure of the cancel method, is <strong>not</strong> the data that has been downloaded so far but is instead data that will allow the download to be resumed from its partial downloaded state.</li>
</ol>
<p>In the context of strengthening our media download system, we can think of these cancel methods, respectively as:</p>
<ol>
<li>Cancel (<code>cancel()</code>)</li>
<li>Pause (<code>cancel(byProducingResumeData:)</code>)</li>
</ol>
<p>Lets add the ability to cancel a download:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {

    //Omitted properties and other methods

    // 3
    func cancel() {
        state = .cancelled

        os_log(.info, &quot;Cancelling download&quot;)
        downloadTask?.cancel()

        cleanup()
    }
}</code></pre>
<p><code>cancel()</code> forwards the cancel instruction onto the wrapped <code>URLSessionDownloadTask</code> instance.</p>
<p>Now let&apos;s add the ability to pause a download:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {

    //Omitted other properties

    // 1
    private var resumptionData: Data?

    //Omitted other methods

    // 2
    func pause() {
        state == .paused

        os_log(.info, &quot;Pausing download&quot;)
        downloadTask?.cancel(byProducingResumeData: { [weak self] (data) in
            guard let data = data else {
                return
            }

            os_log(.info, &quot;Cancelled download task has produced resumption data of: %{public}@ for %{public}@&quot;, data.description, self?.url.absoluteString ?? &quot;unknown url&quot;)
            self?.resumptionData = data
        })

        cleanup()
    }
}</code></pre>
<p>With the above changes here&#x2019;s what we did:</p>
<ol>
<li>Added a property to store any resumption <code>Data</code> instance.</li>
<li><code>pause()</code> cancels the wrapped <code>URLSessionDownloadTask</code> instance and sets <code>resumptionData</code> with the <code>Data</code> instance returned in the closure.</li>
</ol>
<blockquote>
<p>You may be thinking &quot;Why not just call <code>suspend()</code> on the download?&quot;. It&apos;s a good idea however calling <code>suspend()</code> on an active download doesn&apos;t actually stop that download (even though the <code>URLSessionDownloadTask</code> instance will report that it&apos;s stopped downloading). You can see this in action if you use <a href="https://www.charlesproxy.com/?ref=williamboles.com">Charles Proxy</a> to snoop on a supposedly suspended download.</p>
</blockquote>
<p>Now that we have some resumption data, let&apos;s use it by refactoring our <code>resume()</code> method:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {

    //Omitted properties

    func resume() {
        //Omitted start of method

        if let resumptionData = resumptionData {
            os_log(.info, &quot;Attempting to resume download task&quot;)
            downloadTask = session.downloadTask(withResumeData: resumptionData, completionHandler: downloadTaskCompletionHandler)
        } else {
            os_log(.info, &quot;Creating a new download task&quot;)
            downloadTask = session.downloadTask(with: url, completionHandler: downloadTaskCompletionHandler)
        }

        downloadTask?.resume()
    }

    //Omitted other methods
}</code></pre>
<p>With the above changes, we now have two ways to create a <code>URLSessionDownloadTask</code> instance: with and without resumption data.</p>
<p>It&apos;s not uncommon for the same asset to be requested for download multiple times. Making multiple requests for the same asset is wasteful. While caching assets is outside of the scope of the media download layer, coalescing (or merging) active download requests for the same asset isn&apos;t:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {

    //Omitted properties and other methods

    //MARK: - Coalesce

    func coalesceDownloadCompletionHandler(_ otherDownloadCompletionHandler: @escaping DownloadCompletionHandler) {
        let initalDownloadCompletionHandler = downloadCompletionHandler

        downloadCompletionHandler = { result in
            initalDownloadCompletionHandler?(result)
            otherDownloadCompletionHandler(result)
        }
    }
}</code></pre>
<p>In <code>coalesceDownloadCompletionHandler(_:)</code> the existing completion handler (<code>initalDownloadCompletionHandler</code>) and the completion handler (<code>otherDownloadCompletionHandler</code>) for the new download are wrapped together in a third completion handler (<code>downloadCompletionHandler</code>). This third completion handler is then set as this <code>AssetDownloadItem</code>  instance&apos;s <code>downloadCompletionHandler</code> value. This technique means that when this download completes the completion handler for both download requests will be triggered.</p>
<blockquote>
<p>It is possible to recursively wrap any number of <code>DownloadCompletionHandler</code> closures using this approach.</p>
</blockquote>
<p>Lets add a few connivence properties for interpreting the <code>state</code> property:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {
    //Omitted other properties

    // 1
    var isCoalescable: Bool {
        return (state == .ready) ||
            (state == .downloading) ||
            (state == .paused)
    }

    // 2
    var isResumable: Bool {
        return (state == . ready) ||
            (state == .paused)
    }

    // 3
    var isPaused: Bool {
        return state == .paused
    }

    // 4
    var isCompleted: Bool {
        return state == .completed
    }

    //Omitted methods
}</code></pre>
<ol>
<li>We only want to be able to coalesce <code>ready</code>, <code>downloading</code> or <code>paused</code>.</li>
<li>We can only resume <code>AssetDownloadItem</code> instances that are not currently downloading or have been completed.</li>
<li>Wrapper around a check for if the <code>state</code> is <code>paused</code> so that it reads better.</li>
<li>Wrapper around a check for if the <code>state</code> is <code>completed</code> so that it reads better.</li>
</ol>
<blockquote>
<p>We will see how these are used in <code>AssetDownloadsSession</code>.</p>
</blockquote>
<p>Before we leave <code>AssetDownloadItem</code> lets add a <code>description</code> property to aid our debugging:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {
    //Omitted other properties

    var description: String {
        return url.absoluteString
    }

    //Omitted other methods
}</code></pre>
<p>Our <code>AssetDownloadItem</code> instances description will now show the URL of the asset that it is downloading.</p>
<p>So far we have been building up <code>AssetDownloadItem</code> and while we are not yet done with, <code>AssetDownloadItem</code> now has enough functionality to allow us to turn our attention to <code>AssetDownloadsSession</code>.</p>
<p>As mentioned above <code>AssetDownloadsSession</code> has 4 tasks with regards to download requests:</p>
<ul>
<li>Scheduling.</li>
<li>Pausing.</li>
<li>Resuming.</li>
<li>Cancelling.</li>
</ul>
<p>However, not all 4 need to be exposed. Only scheduling and cancelling need to be public. Resuming and pausing can private. Resuming a download is just a special case of scheduling and pausing is just a special case of cancellation. By hiding the ability to resume and pause a download, we can keep the interface of <code>AssetDownloadsSession</code> minimal.</p>
<p>First, lets look at how we can schedule a download:</p>
<pre><code class="swift">class AssetDownloadsSession {

    // 1
    static let shared = AssetDownloadsSession()

    // 2
    private var assetDownloadItems = [AssetDownloadItem]()

    private var session: URLSession

    // MARK: - Init

    // 3
    init(urlSessionFactory: URLSessionFactory = URLSessionFactory()) {
        self.session = urlSessionFactory.defaultSession()
    }

    // MARK: - Schedule

    // 4
    func scheduleDownload(url: URL, completionHandler: @escaping DownloadCompletionHandler) {
        let assetDownloadItem = AssetDownloadItem(session: session, url: url)
        assetDownloadItem.downloadCompletionHandler = downloadCompletionHandler

        os_log(.info, &quot;Adding new download: %{public}@&quot;, assetDownloadItem.description)

        assetDownloadItems.append(assetDownloadItem)

        assetDownloadItem.resume()
    }
}</code></pre>
<p>Here&#x2019;s what we did:</p>
<ol>
<li><code>AssetDownloadsSession</code> is a singleton as we want all asset downloads to go through the same component which will allow any duplicate download requests to be spotted and coalesced.</li>
<li>An array of the <code>AssetDownloadItem</code> instances that are either downloading, paused or cancelled.</li>
<li><code>URLSessionFactory</code> is a factory that handles the creation of <code>URLSession</code> instances.</li>
<li>In <code>scheduleDownload(url:completionHandler:)</code> a new <code>AssetDownloadItem</code> instance is created, added to the <code>assetDownloadItems</code> array and the download is started.</li>
</ol>
<p><code>URLSessionFactory</code> looks like:</p>
<pre><code class="swift">class URLSessionFactory {

    // MARK: - Default

    func defaultSession(delegate: URLSessionDelegate? = nil, delegateQueue queue: OperationQueue? = nil) -&gt; URLSession {
        let configuration = URLSessionConfiguration.default

        //For demonstration purposes disable caching
        configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
        configuration.urlCache = nil

        let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: queue)

        return session
    }
}</code></pre>
<p>Now that we can schedule a download, let&apos;s look at how to pause a download.</p>
<blockquote>
<p>As mentioned above, outside of <code>AssetDownloadsSession</code> there is no concept of pausing a download so our pause method will be presented as a cancel method.</p>
</blockquote>
<pre><code class="swift">class AssetDownloadsSession {
    //Omitted properties and other methods

    // MARK: - Cancel

    func cancelDownload(url: URL) {
        guard let assetDownloadItem = assetDownloadItems.first(where: { $0.url == url }) else {
            return
        }

        os_log(.info, &quot;Download: %{public}@ going to be paused&quot;, assetDownloadItem.description)
        assetDownloadItem.pause()
    }
}</code></pre>
<p>In the above method, we first determine whether an existing <code>AssetDownloadItem</code> instance exists for the URL passed in and if it does <code>pause()</code> is called on it.</p>
<p>The only time that we really want to be <em>actually</em> cancelling downloads is when the system is under strain, and we need to free up memory. When this happens iOS posts a <code>UIApplication.didReceiveMemoryWarningNotification</code> notification that we can listen for:</p>
<pre><code class="swift">class AssetDownloadsSession {
    //Omitted properties

    // MARK: - Init

    // 1
    init(urlSessionFactory: URLSessionFactory = URLSessionFactory(), notificationCenter: NotificationCenter = NotificationCenter.default) {
        self.session = urlSessionFactory.defaultSession(delegate: self)
        registerForNotifications(on: notificationCenter)
    }

    // MARK: - Notification

    // 2
    private func registerForNotifications(on notificationCenter: NotificationCenter) {
        notificationCenter.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: .main) { [weak self] _ in
            self?.purgePausedDownloads()
        }
    }

    // 3
    private func purgePausedDownloads() {
        accessQueue.sync {
           os_log(.info, &quot;Cancelling paused items&quot;)

           assetDownloadItems = assetDownloadItems.filter { (assetDownloadItem) -&gt; Bool in
               let isPaused = assetDownloadItem.isPaused
               if isPaused {
                   assetDownloadItem.cancel()
               }

               return !isPaused
           }
        }
    }

    //Omitted other methods
}</code></pre>
<p>With the above changes here&#x2019;s what we did:</p>
<ol>
<li>Inject the default <code>NotificationCenter</code> instance into the <code>AssetDownloadsSession</code> init&apos;er.</li>
<li>Register for the <code>UIApplication.didReceiveMemoryWarningNotification</code> notification.</li>
<li>Loop through the <code>AssetDownloadItem</code> instances, find those that are <code>paused</code> and the cancel those instances. Finally, filter out those now-cancelled <code>AssetDownloadItem</code> instances to remove them from the <code>assetDownloadItems</code> array.</li>
</ol>
<p>So far we are able to schedule, pause and cancel downloads so let&apos;s add in the ability to resume a download:</p>
<pre><code class="swift">class AssetDownloadsSession {
    //Omitted properties and other methods

    func scheduleDownload(url: URL, completionHandler: @escaping DownloadCompletionHandler) {
        if let assetDownloadItem = assetDownloadItems.first(where: { $0.url == url &amp;&amp; $0.isCoalescable }) {
            os_log(.info, &quot;Found existing %{public}@ download so coalescing them for: %{public}@&quot;, assetDownloadItem.state.rawValue, assetDownloadItem.description)

            assetDownloadItem.coalesceDownloadCompletionHandler(completionHandler)

            if assetDownloadItem.isResumable {
                assetDownloadItem.resume()
            }
        } else {
            //Omitted
        }
    }
}</code></pre>
<p>With the changes above when a URL is passed in, a check is made to see if there is an existing <code>AssetDownloadItem</code> instance with that URL that can be coalesced. If there is an existing <code>AssetDownloadItem</code> instance, then the new download request is coalesced with it. If that coalesced download was <code>paused</code>, it is resumed.</p>
<p>Lets add a delegate so that our <code>AssetDownloadItem</code> instances can inform <code>AssetDownloadsSession</code> that a download has been completed so that it can be removed from the <code>assetDownloadItems</code> array:</p>
<pre><code class="swift">fileprivate protocol AssetDownloadItemDelegate {
    func assetDownloadItemCompleted(_ assetDownloadItem: AssetDownloadItem)
}

fileprivate class AssetDownloadItem: Equatable {
    //Omitted other properties

    var delegate: AssetDownloadItemDelegate?

    //Omitted other methods

    private func complete() {
        state = .completed

        delegate?.assetDownloadItemCompleted(self)
    }
}</code></pre>
<p><code>AssetDownloadsSession</code> needs to implement <code>AssetDownloadItemDelegate</code> so that the completed download can be removed:</p>
<pre><code class="swift">class AssetDownloadsSession: AssetDownloadItemDelegate // 1 {
    //Omitted properties and other methods

    func scheduleDownload(url: URL, completionHandler: @escaping DownloadCompletionHandler) {
        if let existingCoalescableAssetDownloadItem = assetDownloadItems.first(where: { $0.url == url &amp;&amp; $0.isCoalescable }) {
            //Omitted
        } else {
            let assetDownloadItem = AssetDownloadItem(session: session, url: url)
            assetDownloadItem.downloadCompletionHandler = completionHandler
            assetDownloadItem.delegate = self // 2

            os_log(.info, &quot;Created a new download: %{public}@&quot;, assetDownloadItem.description)

            assetDownloadItems.append(assetDownloadItem)

            assetDownloadItem.resume()
        }
    }

    // MARK: - AssetDownloadItemDelegate

    // 3
    func assetDownloadItemCompleted(_ assetDownloadItem: AssetDownloadItem) {
        os_log(.info, &quot;Completed download of: %{public}@&quot;, assetDownloadItem.description)

        if let index = assetDownloadItems.firstIndex(where: { $0.url == assetDownloadItem.url &amp;&amp; $0.isCompleted }) {
            assetDownloadItems.remove(at: index)
        }
    }
}</code></pre>
<p>Here&#x2019;s what we did:</p>
<ol>
<li><code>AssetDownloadsSession</code> now conforms to <code>AssetDownloadItemDelegate</code>.</li>
<li>Added the current <code>AssetDownloadsSession</code> instance as delegate to the new <code>AssetDownloadItem</code> instance.</li>
<li>When the delegate <code>assetDownloadItemCompleted(_:)</code> method is triggered, we remove that completed download from <code>assetDownloadItems</code>.</li>
</ol>
<p>We are almost there, but we need to fix a big gotcha in the solution.  Most of what <code>AssetDownloadsSession</code> does is manipulate the <code>assetDownloadItems</code> array. This array is updated from multiple locations, and there is currently no guarantee that the same thread will be used in all updates potentially leading to a race condition where thread A has triggered an  <code>AssetDownloadItem</code> instance to be removed from the <code>assetDownloadItems</code> array just as thread B is attempting to coalesce that same instance. We can avoid these types of scenarios by using a serial dispatch queue to wrap all <code>assetDownloadItems</code> array updates:</p>
<pre><code class="swift">class AssetDownloadsSession: AssetDownloadItemDelegate {
    //Omitted other properties

    private let accessQueue = DispatchQueue(label: &quot;com.williamboles.downloadssession&quot;)

    //Omitted other methods

    private func registerForNotifications(on notificationCenter: NotificationCenter) {
        notificationCenter.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: .main) { [weak self] _ in
            self?.accessQueue.sync {
                //Omitted rest of method
            }
        }
    }

    // MARK: - Schedule

    func scheduleDownload(url: URL, completionHandler: @escaping DownloadCompletionHandler) {
        accessQueue.sync {
            //Omitted rest of method
        }
    }

    // MARK: - Cancel

    func cancelDownload(url: URL) {
        accessQueue.sync {
            //Omitted rest of method
        }
    }

    // MARK: - AssetDownloadItemDelegate

    fileprivate func assetDownloadItemCompleted(_ assetDownloadItem: AssetDownloadItem) {
        accessQueue.sync {
            //Omitted rest of method
        }
    }
}</code></pre>
<p>And that&apos;s the media download layer complete &#x1F973;.</p>
<h3 id="howdoweknowitactuallyworks">How do we know it actually works? &#x1F575;&#xFE0F;</h3>
<p>If you&apos;ve run the example project, you will notice that downloads start, pause and finish but how we know the download is <em>actually</em> resuming after it&apos;s paused and isn&apos;t just starting again from 0%?</p>
<p>Well we can add in some more logging to get that information. <code>URLSessionDownloadDelegate</code> has a special method for downloads that are resumed. Lets add it into <code>AssetDownloadsSession</code>:</p>
<pre><code class="swift">class AssetDownloadsSession: NSObject, AssetDownloadItemDelegate, URLSessionDownloadDelegate  // 1 {
    //Omitted properties

    // MARK: - Init

    init(urlSessionFactory: URLSessionFactory = URLSessionFactory(), notificationCenter: NotificationCenter = NotificationCenter.default) {
        super.init() // 2

        self.session = urlSessionFactory.defaultSession(delegate: self) // 3
        registerForNotifications(on: notificationCenter)
    }

    //Omitted other methods

    // MARK: - URLSessionDownloadDelegate

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { /*no-op*/ }

    // 4
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
        guard let url = downloadTask.currentRequest?.url else {
            return
        }
        let resumptionPercentage = (Double(fileOffset)/Double(expectedTotalBytes)) * 100
        os_log(.info, &quot;Resuming download: %{public}@ from: %{public}.02f%%&quot;, url.absoluteString, resumptionPercentage)
    }
}</code></pre>
<p>Here&#x2019;s what we did:</p>
<ol>
<li><code>AssetDownloadsSession</code> now conforms to <code>URLSessionDownloadDelegate</code>. As <code>URLSessionDownloadDelegate</code> inherits from <code>NSObjectProtocol</code>, <code>AssetDownloadsSession</code> now needs to be an <code>NSObject</code> subclass.</li>
<li>As <code>AssetDownloadsSession</code> is now an <code>NSObject</code> subclass, a call to <code>super</code> must be made in its init&apos;er.</li>
<li>When creating the <code>URLSession</code> instance, we need to set the <code>AssetDownloadsSession</code> instance as the <code>delegate</code> of that session.</li>
<li>Implemented the <code>urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)</code> to log the percentage of when a download is resumed.</li>
</ol>
<blockquote>
<p>It&apos;s interesting to note that when creating <code>URLSessionDownloadTask</code> instances, this solution is now using both the completion handler and the delegate.</p>
</blockquote>
<p>As well as logging at what percentage a download is resumed, it is also useful to know that downloads percentage when it was paused:</p>
<pre><code class="swift">fileprivate class AssetDownloadItem {
    //Omitted other properties

    private var observation: NSKeyValueObservation? // 1

    //Omitted other

    deinit {
        observation?.invalidate()  // 2
    }

    //Omitted other methods

    func resume() {
        //Omitted rest of method

        // 3
        observation = downloadTask?.progress.observe(\.fractionCompleted, options: [.new]) { [weak self] (progress, change) in
            os_log(.info, &quot;Downloaded %{public}.02f%% of %{public}@&quot;, (progress.fractionCompleted * 100), self?.url.absoluteString ?? &quot;&quot;)
        }
    }

    //Omitted other methods

    // 4
    private func cleanup() {
        observation?.invalidate()
        downloadTask = nil
    }
}</code></pre>
<p>Here&#x2019;s what we did:</p>
<ol>
<li>Added a <a href="https://developer.apple.com/documentation/objectivec/nsobject/nskeyvalueobserving?ref=williamboles.com"><code>NSKeyValueObservation</code></a> property so that it can outlive the method it will be created in.</li>
<li>When this <code>AssetDownloadItem</code> instance is deinit&apos;ed, the <code>NSKeyValueObservation</code> instance is invalidated so it will stop observing.</li>
<li>Add an observe onto the <code>progress</code> property of the <code>URLSessionDownloadTask</code> instance. As it changes, a  log is made detailing what the new download percentage is.</li>
<li>During cleanup, we invalidate the <code>NSKeyValueObservation</code> instance. N.B. cleanup is called when pausing a download as well as when completing a download so we need both <code>observation?.invalidate()</code> in both <code>cleanup()</code> and <code>deinit()</code>.</li>
</ol>
<p>With this additional logging, it is now possible to get a really good understanding of what is happening with our downloads.</p>
<h3 id="onlyhalfthestory">Only half the story &#x1F4D6;</h3>
<p>This has been a fairly long post so if you&apos;ve made it here, take a moment to breathe out and enjoy it &#x1F44F;.</p>
<p>To recap, we built a download media layer that:</p>
<ol>
<li>Allows for scheduling, cancelling, pausing and resuming of download requests.</li>
<li>Allows for coalescing multiple download requests for the same asset.</li>
</ol>
<p>And does this without exposing the complexity of pausing, resuming or coalescing.</p>
<p>With this new media download layer, we have improved download performance by enhancing the mechanics of downloading, but we can improve download performance further by:</p>
<ol>
<li>Downloading the smallest possible asset required for the UI.</li>
<li>Predicting where the user is going and prefetching those assets.</li>
<li>Caching offline previously downloaded assets.</li>
</ol>
<p>In the end, our users are going to have to wait for media to download, all that we can do is ensure that they are waiting due to their desire for ever-increasingly media-rich apps rather than our choices over how downloads are handled.</p>
<p>You can download the example project for this post <a href="https://github.com/wibosco/PausableDownloads-Example?ref=williamboles.com">here</a>.</p>
<hr>
<h3 id="runningtheexampleproject">Running the example project &#x1F3C3;</h3>
<p>In the <a href="https://github.com/wibosco/PausableDownloads-Example?ref=williamboles.com">example project</a> I used <a href="https://apidocs.imgur.com/?ref=williamboles.com">Imgur</a> as an online image database to populate the app. Imgur has a great JSON based API and an extensive library of freely available media. Imgur API, while being free, does require us to <a href="https://api.imgur.com/oauth2/addclient?ref=williamboles.com">register</a> our example project to get a client-id which needs to be sent with each request.</p>
<p>As the <a href="https://github.com/wibosco/PausableDownloads-Example?ref=williamboles.com">example project</a> does not require access to a user&apos;s personal data, when registering select the <code>Anonymous usage without user authorization</code> option for Authorization type. At the time of writing, you had to provide a URL in the <code>Authorization callback URL</code> field even for anonymous authentication - I found any URL value would work for this.</p>
<p>After you register, you should be given a unique <code>client-id</code> which will allow you access to Imgur&apos;s content. You will need to add your <code>client-id</code> as the value of the <code>clientID</code> property in <code>RequestConfig</code>. After you&apos;ve added your <code>client-id</code>, you should be able to run the app and see how our background-transfer solution works.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Step-by-step Core Data Migration]]></title><description><![CDATA[People really care about their possessions. Nowhere do you see this more than on public transport. It's not unusual to see bags occupying seats while people stand. As a Brit, we have developed a powerful non-verbal based form of communication to indicate that you want someone to move their bag]]></description><link>https://williamboles.com/step-by-step-core-data-migration/</link><guid isPermaLink="false">5a856ededeb9b30018282611</guid><category><![CDATA[core data]]></category><category><![CDATA[migration]]></category><category><![CDATA[persistence]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Thu, 30 Nov 2017 12:51:00 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/11/machine-modern-min.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<img src="https://williamboles.com/content/images/2019/11/machine-modern-min.jpg" alt="Step-by-step Core Data Migration"><p>This post is now out-of-date, please instead see: <a href="https://williamboles.com/progressive-core-data-migration/">&quot;Progressive Core Data Migration&quot;</a>.</p>
</blockquote>
<p>People really care about their possessions. Nowhere do you see this more than on public transport. It&apos;s not unusual to see bags occupying seats while people stand. As a Brit, we have developed a powerful non-verbal based form of communication to indicate that you want someone to move their bag - maybe a slight shuffle, eye contact with other standing commuters and tutting. Even with these <em>clear</em> signs some people have the audacity to ignore them and force you into the doing the unthinkable - speaking to a stranger on public transport.</p>
<p>&quot;Excuse me, could you please move your bag so that I can sit down&quot;</p>
<p>Surprisingly often, you are met with disdain as the other person consigns their bag to the indignity of the floor. As you settle into your seat and (pretend to) read, you begin thinking how oddly connected we are to our possessions rather than the needs other humans.</p>
<p>But it turns out that it&apos;s not just physical items that we really care about, we also feel the same way about our data. Especially if the data has been earned somehow - think about the sense of betrayal you feel when a game crashes and takes your just unlocked <em><a href="http://www.thefreedictionary.com/thingamabob?ref=williamboles.com">thingamabob</a></em> with it.</p>
<p>In our iOS apps we often store these <em>thingamabobs</em> in <a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/index.html?ref=williamboles.com">Core Data</a>. The structure of which is defined by a model/schema - a set of entities with attributes and relationships. A common situation in development is that over time our model changes. In order to allow these changes to happen we need to migrate our user&apos;s data from the old structure to the new structure. In this post, we are going to build a simple system to handle Core Data migrations.</p>
<p><img src="https://williamboles.com/content/images/2019/11/machine-modern-min-1.jpg" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<p>If you want to follow along at home you can download the example project we will be working through from my <a href="https://github.com/wibosco/CoreDataMigration-Example?ref=williamboles.com">GitHub repo</a>. We are going to work through 4 different Models changes and see how our migration approach can handle each unique change.</p>
<h3 id="greenfieldprojects">Green field projects &#x1F33F;</h3>
<p>On a green field project we don&apos;t have to care about migrating because we don&apos;t have anything to migrate. So lets see our first Core Data stack:</p>
<pre><code class="swift">class CoreDataManager {

    lazy var persistentContainer: NSPersistentContainer! = {
        let persistentContainer = NSPersistentContainer(name: &quot;CoreDataMigration_Example&quot;)

        return persistentContainer
    }()

    lazy var backgroundContext: NSManagedObjectContext = {
        let context = self.persistentContainer.newBackgroundContext()
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

        return context
    }()

    lazy var mainContext: NSManagedObjectContext = {
        let context = self.persistentContainer.viewContext
        context.automaticallyMergesChangesFromParent = true

        return context
    }()

    // MARK: - Singleton

    static let shared = CoreDataManager()

    // MARK: - SetUp

    func setup(completion: @escaping () -&gt; Void) {
        loadPersistentStore {
            completion()
        }
    }

    // MARK: - Loading

    private func loadPersistentStore(completion: @escaping () -&gt; Void) {
        self.persistentContainer.loadPersistentStores { description, error in
            guard error == nil else {
                fatalError(&quot;was unable to load store \(error!)&quot;)
            }

            completion()
        }
    }
}</code></pre>
<p><code>CoreDataManager</code> is a singleton who&apos;s responsibility is to setup the Core Data stack and provide access to various different contexts. If you have ever seen a Core Data stack setup before, you will instantly notice how little code the above manager contains. Over the years Core Data has evolved and become more developer friendly. Above, we are taking advantage of a relatively new piece of the Core Data family - <a href="https://developer.apple.com/documentation/coredata/nspersistentcontainer?ref=williamboles.com"><code>NSPersistentContainer</code></a> which was introduced in iOS 10. The <code>NSPersistentContainer</code> simplifies the creation of the managed object model, persistent store coordinator and the managed object contexts:</p>
<pre><code class="swift">lazy var persistentContainer: NSPersistentContainer! = {
    let persistentContainer = NSPersistentContainer(name: &quot;CoreDataMigration_Example&quot;)

    return persistentContainer
}()</code></pre>
<blockquote>
<p>Our example project is called <code>CoreDataMigration-Example</code> - see Apple&apos;s <a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html?ref=williamboles.com###//apple_ref/doc/uid/TP40014216-CH10-ID138">documentation</a> on why the <code>-</code> has become a <code>_</code></p>
</blockquote>
<p>As we will see later we can still get access to our <code>NSManagedModel</code>, <code>NSPersistentStoreCoordinator</code> and <code>NSManagedObjectContext</code> instances via this container but we no longer have to copy and paste in their set-up code.</p>
<p>With loading a persistent store(s) being is an asynchronous operation, our setup needs to also be asynchronous:</p>
<pre><code class="swift">func setup(completion: @escaping () -&gt; Void) {
    loadPersistentStore {
        completion()
    }
}

// MARK: - Loading

private func loadPersistentStore(completion: @escaping () -&gt; Void) {
    persistentContainer.loadPersistentStores { description, error in
        guard error == nil else {
            fatalError(&quot;was unable to load store \(error!)&quot;)
        }

        completion()
    }
}</code></pre>
<p>As Core Data is so central to what we do in our apps, if we are unable to load the store the above method throws a fatal exception - this will allow us to <em>fail fast</em> during development.</p>
<p>Our model consists of a single Entity <code>Post</code> that consists of 3 properties:</p>
<ul>
<li><code>postID</code></li>
<li><code>color</code></li>
<li><code>date</code></li>
</ul>
<p><img src="https://williamboles.com/content/images/2017/11/Screen-Shot-2017-11-16-at-14.43.09.png" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<h3 id="fromv1tov2">From v1 to v2</h3>
<p>So we release the app (with the <code>version1</code> of the model) and our users &#x2764;&#xFE0F; it.</p>
<p>Of course - you did a great job!</p>
<p>Due to this success we hire a new developer. In their first week they mistake the information stored in the <code>color</code> property on <code>Post</code> to be a representation of a color as an RGB string (when in fact it is represented as a hex string) which leads to the app crashing &#x1F61E;. To avoid this issue happening when we hire more developers we decide to rename that property to <code>hexColor</code>. Now this is a change to the model which means a new model version which will result in a migration.</p>
<p>Before we delve straight into the migration itself, let&apos;s look more in depth at the migration process works.</p>
<h6 id="themigrationprocess">The Migration Process</h6>
<p>Core Data supports evolving the model over time. It does this by allowing us to create new versions of the model so that we end up with something like:</p>
<p><img src="https://williamboles.com/content/images/2017/10/Screen-Shot-2017-10-17-at-11.41.12.png" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<p>(The green tick indicating which version is currently being developed against.)</p>
<p>To change the current version you would switch the <code>Model Version</code> value shown below:</p>
<p><img src="https://williamboles.com/content/images/2017/10/core-data-versions.png" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<p>With this ability to evolve the model over time, we also need to handle migrating the user&apos;s data from one model version to another another. This is handled by creating a mapping model between those two versions. By default in iOS the migration process is completed in 1 step from <code>source</code> to <code>destination</code> models so if we support 4 versions, mapping models would exist for <code>1 to 4</code>, <code>2 to 4</code> and <code>3 to 4</code>. While this approach is the most efficient (in terms of processing and migration time), it is also the most developer and tester heavy. With each new destination model we need to redo all the paths between those models. So if we introduce version <code>5</code> we now need to be handle <code>1 to 5</code>, <code>2 to 5</code>, <code>3 to 5</code> and <code>4 to 5</code> - as you can see there no reuse from the previous migration paths. For each new version you must add <code>n-1</code> new mappings. This can lead to a lot of work (and potential for &#x1F41E;s) for each new version of the model we introduce or convince us to drop support for migrating from certain versions and so result in corrupted data for any users on those dropped versions &#x1F61E;.</p>
<p>Instead of following the default migration process, we will look at a system that performs migration over multiple steps i.e <code>1 to 2</code>, <code>2 to 3</code>, <code>3 to 5</code> and <code>4 to 5</code> - this means that for each new version of the model we need only add <code>1</code> additional step.</p>
<p>But before we get into get into the technical details of our migration implementation, lets look at our migration options.</p>
<p>The first question to ask yourself before engaging in the migration process is:</p>
<p><strong>&quot;Do I need to bother with migration?&quot;</strong></p>
<p>Seriously, just because you use Core Data in your project does not mean that you need to care about migrating the data stored there. If you only use Core Data as a local cache and always override it with the content you get from an API response, you probably don&apos;t need to go through the effort of migrating from one version to another. Just delete the local <code>.sqlite</code> files and recreate your Core Data stack, populating that new model with calls to the API. If that applies to you, you can stop reading now if you want or continue on with a certain smugness knowing that the  difficulties being described below do not relate to you &#x1F61C;.</p>
<p>Now in iOS you have <strong>2</strong> migration options:</p>
<ol>
<li><a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html?ref=williamboles.com">Lightweight</a></li>
<li><a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmMappingOverview.html?ref=williamboles.com###//apple_ref/doc/uid/TP40004399-CH5-SW1">Standard</a></li>
</ol>
<p><strong>Lightweight</strong></p>
<p>Lightweight migration is where Core Data is able to infer how to migrate from source model to the destination model.</p>
<p><strong>Standard</strong></p>
<p>Standard migration is where Core Data isn&apos;t able to infer how to migrate and we have to detail the path by providing a custom mapping model <code>*.xcmappingmodel</code> and/or <code>NSEntityMigrationPolicy</code> subclasses.</p>
<p>It&apos;s important to note that both <code>Lightweight</code> and <code>Standard</code> migration techniques will produce a mapping model, it&apos;s just that in <code>Standard</code> the mapping model is explicitly created and lives in the project as a <code>*.xcmappingmodel</code> file.</p>
<p>Both <code>Lightweight</code> and <code>Standard</code> migration techniques can be achieved automatically or manually. By default with <code>NSPersistentContainer</code> Core Data will attempt to perform <code>Standard</code> and then fall back to <code>Lightweight</code> if it can&apos;t find the needed mapping model. For the rest of this post will be focused on handling all migrations manually - this allows us to specify the step-by-step approach mentioned above (automatic migrations try to migrate in <code>1</code> step). To disable automatic migrations we need to set the <code>shouldInferMappingModelAutomatically</code> on the <code>NSPersistentStoreDescription</code> to false - we will see this happening later.</p>
<h3 id="fromv1tov2cont">From v1 to v2 cont.</h3>
<p>So back to the task at hand. As we seen above we need to introduce a new version of the Core Data model.</p>
<blockquote>
<p>This can be achieved by highlighting the <code>xcdatamodel</code> (it may be called <code>xcdatamodeld</code>) and going to <code>Editor-&gt;Add Model Version...</code>. We then just need to name it, I would suggest keeping it the same name and adding a number to it so <code>CoreDataMigration_Example 2</code>.</p>
</blockquote>
<p><img src="https://williamboles.com/content/images/2017/11/Screen-Shot-2017-11-30-at-12.07.04.png" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<p>So now we have two models and we can freely edit the model&apos;s structure within <code>CoreDataMigration_Example 2</code>.</p>
<p>When it comes to renaming <code>color</code> to <code>hexColor</code> we have two options:</p>
<ul>
<li><code>canonical name</code></li>
<li><code>*.xcmappingmodel</code></li>
</ul>
<p>A <code>canonical name</code> effectively acts a bridge between what that property used to be called and what it now called. You would set the <code>canonical name</code> in the renaming identifier of that property. However here we are going to use <code>*.xcmappingmodel</code>.</p>
<p><img src="https://williamboles.com/content/images/2017/10/Screen-Shot-2017-10-17-at-14.34.49.png" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<p>As you see in the above screenshot we have created a custom mapping between <code>CoreDataMigration_Example</code> (effectively version1) and <code>CoreDataMigration_Example 2</code>. As mentioned above we have created a custom mapping because we have renamed a property <code>color</code> to <code>hexColor</code> and Core Data isn&apos;t able to infer that these properties are the same. With this mapping we are telling Core Data that the new <code>hexColor</code> property should be mapped to old <code>color</code> property by: <code>$source.color</code>.</p>
<p>So if we were not doing the step-by-step migration approach but rather the standard Core Data migration approach, that would be our job finished. However as we discussed the standard Core Data migration approach does not scale well so we need to create our custom migrator.</p>
<h6 id="stepbystepmigrations">Step-by-step Migrations</h6>
<p>In order to build our stack, we have to answer a few questions:</p>
<ol>
<li>What is a Step?</li>
<li>How do we group steps?</li>
<li>What&apos;s responsible for triggering the migration?</li>
</ol>
<p>Let&apos;s start answering them.</p>
<p><strong>1. What is a Step?</strong></p>
<pre><code class="swift">struct CoreDataMigrationStep {

    let source: NSManagedObjectModel
    let destination: NSManagedObjectModel
    let mapping: NSMappingModel
}</code></pre>
<p>A <code>CoreDataMigrationStep</code> is a migration between two versions of the model: <code>source</code> and <code>destination</code> and the actual mapping model itself.</p>
<blockquote>
<p>It&apos;s possible to have multiple mapping models between versions, (this can be especially useful when migrating large data sets) in this post in an attempt to keep things simple I assume only one mapping model but if you need to support multiple mappings you would transform the <code>mapping</code> property into an array.</p>
</blockquote>
<p><strong>2. How do we group steps?</strong></p>
<p>First, lets create a representation of what a model version is:</p>
<pre><code class="swift">enum CoreDataVersion: Int {
    case version1 = 1
    case version2

    // MARK: - Accessors

    var name: String {
        if rawValue == 1 {
            return &quot;CoreDataMigration_Example&quot;
        } else {
            return &quot;CoreDataMigration_Example \(rawValue)&quot;
        }
    }

    static var all: [CoreDataVersion] {
        var versions = [CoreDataVersion]()

        for rawVersionValue in 1...1000 { // A bit of a hack here to avoid manual mapping
            if let version = CoreDataVersion(rawValue: rawVersionValue) {
                versions.append(version)
                continue
            }

            break
        }

        return versions.reversed()
    }

    static var latest: CoreDataVersion {
        return all.first!
    }
}</code></pre>
<p><code>CoreDataVersion</code> is an enum backed by an <code>Int</code> that should mirror the versions available in <code>*.xcdatamodeld</code> package. It provides a nice abstraction for what a version is and allows us to ask questions on a type such as:</p>
<ul>
<li>&quot;Which is the latest version?&quot;</li>
<li>&quot;What&apos;s the name of that model?&quot;</li>
</ul>
<p>With both <code>CoreDataMigrationStep</code> and <code>CoreDataVersion</code> abstraction we can create a migration path from the source model to the destination model:</p>
<pre><code class="swift">class CoreDataMigrationModel {

    let version: CoreDataVersion

    var modelBundle: Bundle {
        return Bundle.main
    }

    var modelDirectoryName: String {
        return &quot;CoreDataMigration_Example.momd&quot;
    }

    static var all: [CoreDataMigrationModel] {
        var migrationModels = [CoreDataMigrationModel]()

        for version in CoreDataVersion.all {
            migrationModels.append(CoreDataMigrationModel(version: version))
        }

        return migrationModels
    }

    static var current: CoreDataMigrationModel {
        return CoreDataMigrationModel(version: CoreDataVersion.latest)
    }

    /**
     Determines the next model version from the current model version.

     NB: the next version migration is not always the next actual version. With
     this solution we can skip &quot;bad/corrupted&quot; versions.
     */
    var successor: CoreDataMigrationModel? {
        switch self.version {
        case .version1:
            return CoreDataMigrationModel(version: .version2)
        case .version2:
            return nil
        }
    }

    // MARK: - Init

    init(version: CoreDataVersion) {
        self.version = version
    }

    // MARK: - Model

    func managedObjectModel() -&gt; NSManagedObjectModel {
        let omoURL = modelBundle.url(forResource: version.name, withExtension: &quot;omo&quot;, subdirectory: modelDirectoryName) // optimized model file
        let momURL = modelBundle.url(forResource: version.name, withExtension: &quot;mom&quot;, subdirectory: modelDirectoryName)

        guard let url = omoURL ?? momURL else {
            fatalError(&quot;unable to find model in bundle&quot;)
        }

        guard let model = NSManagedObjectModel(contentsOf: url) else {
            fatalError(&quot;unable to load model in bundle&quot;)
        }

        return model
    }

    // MARK: - Mapping

    func mappingModelToSuccessor() -&gt; NSMappingModel? {
        guard let nextVersion = successor else {
            return nil
        }

        switch version {
        case .version1: //manual mapped versions
            guard let mapping = customMappingModel(to: nextVersion) else {
                return nil
            }

            return mapping
        default:
            return inferredMappingModel(to: nextVersion)
        }
    }

    func inferredMappingModel(to nextVersion: CoreDataMigrationModel) -&gt; NSMappingModel {
        do {
            let sourceModel = managedObjectModel()
            let destinationModel = nextVersion.managedObjectModel()
            return try NSMappingModel.inferredMappingModel(forSourceModel: sourceModel, destinationModel: destinationModel)
        } catch {
            fatalError(&quot;unable to generate inferred mapping model&quot;)
        }
    }

    func customMappingModel(to nextVersion: CoreDataMigrationModel) -&gt; NSMappingModel? {
        let sourceModel = managedObjectModel()
        let destinationModel = nextVersion.managedObjectModel()
        guard let mapping = NSMappingModel(from: [modelBundle], forSourceModel: sourceModel, destinationModel: destinationModel) else {
            return nil
        }

        return mapping
    }

    // MARK: - MigrationSteps

    func migrationSteps(to version: CoreDataMigrationModel) -&gt; [CoreDataMigrationStep] {
        guard self.version != version.version else {
            return []
        }

        guard let mapping = mappingModelToSuccessor(), let nextVersion = successor else {
            return []
        }

        let sourceModel = managedObjectModel()
        let destinationModel = nextVersion.managedObjectModel()

        let step = CoreDataMigrationStep(source: sourceModel, destination: destinationModel, mapping: mapping)
        let nextStep = nextVersion.migrationSteps(to: version)

        return [step] + nextStep
    }

    // MARK: - Metadata

    static func migrationModelCompatibleWithStoreMetadata(_ metadata: [String : Any]) -&gt; CoreDataMigrationModel? {
        let compatibleMigrationModel = CoreDataMigrationModel.all.first {
            $0.managedObjectModel().isConfiguration(withName: nil, compatibleWithStoreMetadata: metadata)
        }

        return compatibleMigrationModel
    }
}</code></pre>
<p><code>CoreDataMigrationModel</code> this is where the real magic happens.</p>
<pre><code class="swift">func migrationSteps(to version: CoreDataMigrationModel) -&gt; [CoreDataMigrationStep] {
    guard self.version != version.version else {
        return []
    }

    guard let mapping = mappingModelToSuccessor(), let nextVersion = successor else {
        return []
    }

    let sourceModel = managedObjectModel()
    let destinationModel = nextVersion.managedObjectModel()

    let step = CoreDataMigrationStep(source: sourceModel, destination: destinationModel, mapping: mapping)
    let nextStep = nextVersion.migrationSteps(to: version)

    return [step] + nextStep
}</code></pre>
<p>The above method recursively builds an array containing all the steps required to perform a migration from the current version (as defined by the <code>version</code> property) to the latest version.</p>
<p>For each of these steps, the model determines if the mapping should be inferred or needs to explicitly defined by the developer:</p>
<pre><code class="swift">func mappingModelToSuccessor() -&gt; NSMappingModel? {
    guard let nextVersion = successor else {
        return nil
    }

    switch version {
    case .version1: //custom mapped versions
        guard let mapping = customMappingModel(to: nextVersion) else {
            return nil
        }

        return mapping
    default:
        return inferredMappingModel(to: nextVersion)
    }
}</code></pre>
<blockquote>
<p>While strictly speaking we didn&apos;t need to include the inferred branch for migrating form <code>version1</code> to <code>version2</code> (and normally I try to avoid using <code>default</code>), we will need an inferred mapping branch for future migrations so I&apos;ve included it here for completeness.</p>
</blockquote>
<p>As you can see we determine whether to create a custom or inferred mapping model by using a <code>switch</code> statement to check which version is current being accessed. You will know if you need to custom a mapping model if you break the rules as defined in the <a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html?ref=williamboles.com">Lightweight documentation</a> or more likely by attempting to perform an inferred migration and having the app crash on you during development.</p>
<pre><code class="swift">func inferredMappingModel(to nextVersion: CoreDataMigrationModel) -&gt; NSMappingModel {
    do {
        let sourceModel = managedObjectModel()
        let destinationModel = nextVersion.managedObjectModel()
        return try NSMappingModel.inferredMappingModel(forSourceModel: sourceModel, destinationModel: destinationModel)
    } catch {
        fatalError(&quot;unable to generate inferred mapping model&quot;)
    }
}

func customMappingModel(to nextVersion: CoreDataMigrationModel) -&gt; NSMappingModel? {
    let sourceModel = managedObjectModel()
    let destinationModel = nextVersion.managedObjectModel()
    guard let mapping = NSMappingModel(from: [modelBundle], forSourceModel: sourceModel, destinationModel: destinationModel) else {
        return nil
    }

    return mapping
}</code></pre>
<p>With <code>inferredMappingModel</code> we ask <code>NSMappingModel</code> to produce the mapping model by <em>figuring</em> out the differences and how to map between them. Again we follow the <em>fail fast</em> approach by catching the exception and then throwing a more meaningful fatal error.</p>
<p><code>customMappingModel</code> is very similar but instead of getting an <code>NSMappingModel</code> instance based on Core Data figuring out the mapping, we ask it to search the app bundle and find a <code>*.xcmappingmodel</code> which has a matching <code>source</code> and <code>destination</code> model.</p>
<p>The next important part of <code>CoreDataMigrationModel</code> is to look at how the <code>successor</code> version of the current model is determined.</p>
<pre><code class="swift">var successor: CoreDataMigrationModel? {
    switch self.version {
    case .version1:
        return CoreDataMigrationModel(version: .version2)
    case .version2:
        return nil
    }
}</code></pre>
<p>As we only have two models we only need to handle migrating from <code>version1</code> to <code>version2</code>. <code>version2</code> being the current model, doesn&apos;t require a mapping model. You may be thinking that this is overkill and we could simplify this by always getting the next version up as the successor but sadly real-life isn&apos;t always so perfect and it&apos;s possible that we released a model version that contains issues and want to skip migrating any unaffected users to that version. With the above approach it would be possible to define a custom path. So if we pretend that we actually have four versions, it would be possible to skip a version altogether (in this case <code>version3</code>) which would give us the following structure:</p>
<pre><code class="swift">var successor: CoreDataMigrationModel? {
    switch self.version {
    case .version1:
        return CoreDataMigrationModel(version: .version2)
    case .version2:
        return CoreDataMigrationModel(version: .version4) //skipping version3
    case .version3:
        return CoreDataMigrationModel(version: .version4)
    case .version4:
        return nil
    }
}</code></pre>
<p>In order to perform we need to create an initial <code>CoreDataMigrationModel</code> instance based on the currently installed model version:</p>
<pre><code class="swift">class CoreDataMigrationSourceModel: CoreDataMigrationModel {

    // MARK: - Init

    init?(storeURL: URL) {
        guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL) else {
            return nil
        }

        let migrationVersionModel = CoreDataMigrationModel.all.first {
            $0.managedObjectModel().isConfiguration(withName: nil, compatibleWithStoreMetadata: metadata)
        }

        guard migrationVersionModel != nil else {
            return nil
        }

        super.init(version: migrationVersionModel!.version)
    }
}</code></pre>
<p><code>CoreDataMigrationSourceModel</code> is a convenience subclass of <code>CoreDataMigrationModel</code>. We will see later how this is used.</p>
<p><strong>3. What&apos;s responsible for triggering the migration?</strong></p>
<p>Ok, so we&apos;ve looked at the how the steps are created and how each step knows which mapping model will move it to it&apos;s successor, below we are going to look at how those steps are called and how we prepare the app for a migration to occur.</p>
<pre><code class="swift">class CoreDataMigrator {

    // MARK: - Check

    func requiresMigration(at storeURL: URL, currentMigrationModel: CoreDataMigrationModel = CoreDataMigrationModel.current) -&gt; Bool {
        guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL) else {
            return false
        }

        return !currentMigrationModel.managedObjectModel().isConfiguration(withName: nil, compatibleWithStoreMetadata: metadata)
    }

    // MARK: - Migration

    func migrateStore(at storeURL: URL) {
        migrateStore(from: storeURL, to: storeURL, targetVersion: CoreDataMigrationModel.current)
    }

    func migrateStore(from sourceURL: URL, to targetURL: URL, targetVersion: CoreDataMigrationModel) {
        guard let sourceMigrationModel = CoreDataMigrationSourceModel(storeURL: sourceURL as URL) else {
            fatalError(&quot;unknown store version at URL \(sourceURL)&quot;)
        }

        forceWALCheckpointingForStore(at: sourceURL)

        var currentURL = sourceURL
        let migrationSteps = sourceMigrationModel.migrationSteps(to: targetVersion)

        for step in migrationSteps {
            let manager = NSMigrationManager(sourceModel: step.source, destinationModel: step.destination)
            let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString)

            do {
                try manager.migrateStore(from: currentURL, sourceType: NSSQLiteStoreType, options: nil, with: step.mapping, toDestinationURL: destinationURL, destinationType: NSSQLiteStoreType, destinationOptions: nil)
            } catch let error {
                fatalError(&quot;failed attempting to migrate from \(step.source) to \(step.destination), error: \(error)&quot;)
            }

            if currentURL != sourceURL {
                //Destroy intermediate step&apos;s store
                NSPersistentStoreCoordinator.destroyStore(at: currentURL)
            }

            currentURL = destinationURL
        }

        NSPersistentStoreCoordinator.replaceStore(at: targetURL, withStoreAt: currentURL)

        if (currentURL != sourceURL) {
            NSPersistentStoreCoordinator.destroyStore(at: currentURL)
        }
    }

    // MARK: - WAL

    func forceWALCheckpointingForStore(at storeURL: URL) {
        guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL), let migrationModel = CoreDataMigrationModel.migrationModelCompatibleWithStoreMetadata(metadata)  else {
            return
        }

        do {
            let model = migrationModel.managedObjectModel()
            let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)

            let options = [NSSQLitePragmasOption: [&quot;journal_mode&quot;: &quot;DELETE&quot;]]
            let store = persistentStoreCoordinator.addPersistentStore(at: storeURL, options: options)
            try persistentStoreCoordinator.remove(store)
        } catch let error {
            fatalError(&quot;failed to force WAL checkpointing, error: \(error)&quot;)
        }
    }
}</code></pre>
<p><code>CoreDataMigrator</code> is undertaking <strong>3</strong> tasks:</p>
<ol>
<li>Determining if migration is necessary</li>
<li>Setting up a consistent state</li>
<li>Performing the migration</li>
</ol>
<pre><code class="swift">func requiresMigration(at storeURL: URL, currentMigrationModel: CoreDataMigrationModel = CoreDataMigrationModel.current) -&gt; Bool {
    guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL) else {
        return false
    }

    return !currentMigrationModel.managedObjectModel().isConfiguration(withName: nil, compatibleWithStoreMetadata: metadata)
}</code></pre>
<p>In the above method, we are loading the meta data for the persistent store that is currently on the device and determining if it is compatible with latest version&apos;s meta data - returning either <code>true</code> or <code>false</code> based on the outcome of that comparison.</p>
<pre><code class="swift">func forceWALCheckpointingForStore(at storeURL: URL) {
    guard let metadata = NSPersistentStoreCoordinator.metadata(at: storeURL), let migrationModel = CoreDataMigrationModel.migrationModelCompatibleWithStoreMetadata(metadata)  else {
        return
    }

    do {
        let model = migrationModel.managedObjectModel()
        let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)

        let options = [NSSQLitePragmasOption: [&quot;journal_mode&quot;: &quot;DELETE&quot;]]
        let store = persistentStoreCoordinator.addPersistentStore(at: storeURL, options: options)
        try persistentStoreCoordinator.remove(store)
    } catch let error {
        fatalError(&quot;failed to force WAL checkpointing, error: \(error)&quot;)
    }
}</code></pre>
<p>Since iOS 7 Core Data has used Write-Ahead Logging (WAL) journalling by default on it&apos;s SQLite database. In WAL mode Core Data appends transactions to a <code>-wal</code> file and uses a <code>-shm</code> shared memory file, both in the same location as the main <code>sqlite</code> file. While this results in improved performance I noticed when testing that sometimes during migrations, the changes contained in the <code>-wal</code> file were not migrated. This would then result in a crash when anything in the new model format was then written to the old model formatted <code>-wal</code> file. The above method is forcing the changes contained in the <code>-wal</code> file to be committed to the main <code>sqlite</code> file.</p>
<blockquote>
<p>Please note that in order for the <code>-wal</code> commit to be successful, it is necessary to load the model of the sqlite file on disk rather than the latest model.</p>
</blockquote>
<p>As we will see below this forced committing of the WAL transactions will happen just before we perform the migration.</p>
<pre><code class="swift">func migrateStore(at storeURL: URL) {
    migrateStore(from: storeURL, to: storeURL, targetVersion: CoreDataMigrationModel.current)
}

func migrateStore(from sourceURL: URL, to targetURL: URL, targetVersion: CoreDataMigrationModel) {
    guard let sourceMigrationModel = CoreDataMigrationSourceModel(storeURL: sourceURL as URL) else {
        fatalError(&quot;unknown store version at URL \(sourceURL)&quot;)
    }

    forceWALCheckpointingForStore(at: sourceURL)

    var currentURL = sourceURL
    let migrationSteps = sourceMigrationModel.migrationSteps(to: targetVersion)

    for step in migrationSteps {
        let manager = NSMigrationManager(sourceModel: step.source, destinationModel: step.destination)
        let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString)

        do {
            try manager.migrateStore(from: currentURL, sourceType: NSSQLiteStoreType, options: nil, with: step.mapping, toDestinationURL: destinationURL, destinationType: NSSQLiteStoreType, destinationOptions: nil)
        } catch let error {
            fatalError(&quot;failed attempting to migrate from \(step.source) to \(step.destination), error: \(error)&quot;)
        }

        if currentURL != sourceURL {
            //Destroy intermediate step&apos;s store
            NSPersistentStoreCoordinator.destroyStore(at: currentURL)
        }

        currentURL = destinationURL
    }

    NSPersistentStoreCoordinator.replaceStore(at: targetURL, withStoreAt: currentURL)

    if (currentURL != sourceURL) {
        NSPersistentStoreCoordinator.destroyStore(at: currentURL)
    }
}</code></pre>
<p>In the above method we iterate through each of the migration steps using an instance of <code>NSMigrationManager</code>.</p>
<p>The more alert among you will have noticed that we store the user&apos;s data into a temporary <code>sqlite</code> file rather than override the starting <code>sqlite</code> file. This is a safety precaution incase an error happens during migration. We only overwrite the starting <code>sqlite</code> file once we know that the migration has been a success - this can be extremely useful during development.</p>
<p>In the above class we&apos;ve seen a number of methods used that are not part of the standard <code>NSPersistentStoreCoordinator</code> API so I&apos;ve included the extension that contains these methods below. As with most extensions, the methods are used to reduce boilerplate code.</p>
<pre><code class="swift">extension NSPersistentStoreCoordinator {

    // MARK: - Destroy

    static func destroyStore(at storeURL: URL) {
        do {
            let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: NSManagedObjectModel())
            try persistentStoreCoordinator.destroyPersistentStore(at: storeURL, ofType: NSSQLiteStoreType, options: nil)
        } catch let error {
            fatalError(&quot;failed to destroy persistent store at \(storeURL), error: \(error)&quot;)
        }
    }

    // MARK: - Replace

    static func replaceStore(at targetURL: URL, withStoreAt sourceURL: URL) {
        do {
            let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: NSManagedObjectModel())
            try persistentStoreCoordinator.replacePersistentStore(at: targetURL, destinationOptions: nil, withPersistentStoreFrom: sourceURL, sourceOptions: nil, ofType: NSSQLiteStoreType)
        } catch let error {
            fatalError(&quot;failed to replace persistent store at \(targetURL) with \(sourceURL), error: \(error)&quot;)
        }
    }

    // MARK: - Meta

    static func metadata(at storeURL: URL) -&gt; [String : Any]?  {
        return try? NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, at: storeURL, options: nil)
    }

    // MARK: - Add

    func addPersistentStore(at storeURL: URL, options: [AnyHashable : Any]) -&gt; NSPersistentStore {
        do {
            return try addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
        } catch let error {
            fatalError(&quot;failed to add persistent store to coordinator, error: \(error)&quot;)
        }

    }
}</code></pre>
<p><em>I won&apos;t go into their purpose as I think that is self-evident.</em></p>
<h6 id="wrappingitallintothemanager">Wrapping it all into the manager</h6>
<p>At the start of this post we looked at our simple, migration free Core Data stack, it&apos;s now time to go back to that manager and look at how supporting migrations will affect it.</p>
<pre><code class="swift">class CoreDataManager {

    let migrator: CoreDataMigrator

    lazy var persistentContainer: NSPersistentContainer! = {
        let persistentContainer = NSPersistentContainer(name: &quot;CoreDataMigration_Example&quot;)
        let description = persistentContainer.persistentStoreDescriptions.first
        description?.shouldInferMappingModelAutomatically = false //inferred mapping will be handled else where

        return persistentContainer
    }()

    lazy var backgroundContext: NSManagedObjectContext = {
        let context = self.persistentContainer.newBackgroundContext()
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

        return context
    }()

    lazy var mainContext: NSManagedObjectContext = {
        let context = self.persistentContainer.viewContext
        context.automaticallyMergesChangesFromParent = true

        return context
    }()

    // MARK: - Singleton

    static let shared = CoreDataManager()

    // MARK: - Init

    init(migrator: CoreDataMigrator = CoreDataMigrator()) {
        self.migrator = migrator
    }

    // MARK: - SetUp

    func setup(completion: @escaping () -&gt; Void) {
        loadPersistentStore {
            completion()
        }
    }

    // MARK: - Loading

    private func loadPersistentStore(completion: @escaping () -&gt; Void) {
        migrateStoreIfNeeded {
            self.persistentContainer.loadPersistentStores { description, error in
                guard error == nil else {
                    fatalError(&quot;was unable to load store \(error!)&quot;)
                }

                completion()
            }
        }
    }

    private func migrateStoreIfNeeded(completion: @escaping () -&gt; Void) {
        guard let storeURL = persistentContainer.persistentStoreDescriptions.first?.url else {
            fatalError(&quot;persistentContainer was not set up properly&quot;)
        }

        if migrator.requiresMigration(at: storeURL) {
            DispatchQueue.global(qos: .userInitiated).async {
                self.migrator.migrateStore(at: storeURL)

                DispatchQueue.main.async {
                    completion()
                }
            }
        } else {
            completion()
        }
    }
}</code></pre>
<p>As you can see, it&apos;s not too different to what we seen before. But let&apos;s look at what has changed:</p>
<pre><code class="swift">let migrator: CoreDataMigrator</code></pre>
<p>We store an instance <code>CoreDataMigrator</code> as a property which we pass in during init&apos;ing the <code>CoreDataManager</code> instance:</p>
<pre><code class="swift">init(migrator: CoreDataMigrator = CoreDataMigrator()) {
    self.migrator = migrator
}</code></pre>
<p>As we will see in our unit testing by injecting the migrator we will be able to more easily test different scenarios by overriding it&apos;s methods.</p>
<p>As we are going to override the default migration process we next need to take control of it:</p>
<pre><code class="swift">lazy var persistentContainer: NSPersistentContainer! = {
    let persistentContainer = NSPersistentContainer(name: &quot;CoreDataMigration_Example&quot;)
    let description = persistentContainer.persistentStoreDescriptions.first
    description?.shouldInferMappingModelAutomatically = false //inferred mapping will be handled else where

    return persistentContainer
}()</code></pre>
<p>Here is set <code>shouldInferMappingModelAutomatically</code> to false as our <code>CoreDataMigrationModel</code> class will handle setting the correct mapping model approach on each <code>CoreDataMigrationStep</code> step.</p>
<pre><code class="swift">private func loadPersistentStore(completion: @escaping () -&gt; Void) {
    migrateStoreIfNeeded {
        self.persistentContainer.loadPersistentStores { description, error in
            guard error == nil else {
                fatalError(&quot;was unable to load store \(error!)&quot;)
            }

            completion()
        }
    }
}

private func migrateStoreIfNeeded(completion: @escaping () -&gt; Void) {
    guard let storeURL = persistentContainer.persistentStoreDescriptions.first?.url else {
        fatalError(&quot;persistentContainer was not set up properly&quot;)
    }

    if migrator.requiresMigration(at: storeURL) {
        DispatchQueue.global(qos: .userInitiated).async {
            self.migrator.migrateStore(at: storeURL)

            DispatchQueue.main.async {
                completion()
            }
        }
    } else {
        completion()
    }
}</code></pre>
<p>The <code>migrateStoreIfNeeded</code> method continues with our <em>fail fast</em> approach by throwing a fatal error if the persistent container doesn&apos;t meet our expectations. Next we ask the question:</p>
<p><strong>&quot;Do we need to perform a migration here?&quot;</strong></p>
<p>If the answer is <code>yes</code> - the migrator then attempts to perform the migration on a background queue/thread before triggering it&apos;s completion closure once the migration is complete and allowing the Core Data stack to contine being set up.</p>
<p>If the answer is <code>no</code> - the completion block is called and setting up the Core Data stack continues unabated.</p>
<h3 id="migrationcompleted">Migration completed! &#x1F389; &#x1F389; &#x1F389;</h3>
<p>Woohoo! That&apos;s us at the end of migrating from <code>version1</code> to <code>version2</code> of our model. It was a lot to take in but trust me, it will get much easier to perform future migrations now that we implemented this step-by-step approach.</p>
<p>As a recap, let&apos;s see a lovely diagram of what we just created:</p>
<p><img src="https://williamboles.com/content/images/2019/11/CoreDataMigrator.jpg" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<h3 id="fromv2tov3">From v2 to v3</h3>
<p>So far we have migrated from model <code>version1</code> to <code>version2</code> but the new <code>hexColor</code> is still causing issues so instead we decide to extract it out into it&apos;s own Entity: <code>Color</code>. This change will required us to create a new version <code>CoreDataMigration_Example 3</code> which looks like:</p>
<p><img src="https://williamboles.com/content/images/2017/11/Screen-Shot-2017-11-10-at-16.36.29.png" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<p>Now this migration is slightly trickier than from <code>version1</code> to <code>version2</code> and will require us to create not only a <code>xcmappingmodel</code> but also a custom <code>NSEntityMigrationPolicy</code> subclass.</p>
<p><img src="https://williamboles.com/content/images/2017/10/Screen-Shot-2017-10-17-at-14.51.59.png" alt="Step-by-step Core Data Migration" loading="lazy"></p>
<p>As you see in the above screenshot we have created a custom mapping between <code>CoreDataMigration_Example 2</code> and <code>CoreDataMigration_Example 3</code>. In <code>version3</code> we have introduced a new entity <code>Color</code> which has taken the place of the previous <code>color</code> property on the <code>Post</code> entity - you can see in the &apos;Custom Policy&apos; field we are using <code>Post2ToPost3MigrationPolicy</code> to handle migrating from this property to an entity.</p>
<pre><code class="swift">final class Post2ToPost3MigrationPolicy: NSEntityMigrationPolicy {

    override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
        try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)

        guard let dInstance = manager.destinationInstances(forEntityMappingName: mapping.name, sourceInstances: [sInstance]).first else {
            fatalError(&quot;was expecting a post&quot;)
        }

        let color = NSEntityDescription.insertNewObject(forEntityName: &quot;Color&quot;, into: dInstance.managedObjectContext!)
        color.setValue(UUID().uuidString, forKey: &quot;colorID&quot;)
        color.setValue(sInstance.value(forKey: &quot;hexColor&quot;), forKey: &quot;hex&quot;)

        dInstance.setValue(color, forKey: &quot;color&quot;)
    }
}</code></pre>
<p>One interesting point to note, is that we are dealing with <code>NSManagedObject</code> rather than our custom <code>NSManagedObject</code> subclasses. This is because Core Data wouldn&apos;t know which version of <code>Post</code> to load. So to work with both representations of the data we need to use <code>KVC</code> to get and set properties.</p>
<p>Next we need to make some changes to our <code>CoreDataMigrationModel</code> class by introducing a <code>version3</code> case and handling migrating from <code>version2</code> to <code>version3</code>:</p>
<pre><code class="swift">var successor: CoreDataMigrationModel? {
    switch self.version {
    case .version1:
        return CoreDataMigrationModel(version: .version2)
    case .version2:
        return CoreDataMigrationModel(version: .version3)
    case .version3:
        return nil
    }
}</code></pre>
<p>and as migrating to <code>version3</code> will require a custom mapping model:</p>
<pre><code class="swift">func mappingModelToSuccessor() -&gt; NSMappingModel? {
    guard let nextVersion = successor else {
        return nil
    }

    switch version {
    case .version1, .version2: //manual mapped versions
        guard let mapping = customMappingModel(to: nextVersion) else {
            return nil
        }

        return mapping
    default:
        return inferredMappingModel(to: nextVersion)
    }
}</code></pre>
<h3 id="fromv3tov4">From v3 to v4</h3>
<p>The success of our app knows no bounds and we decide to add the ability to hide posts that have been seen. We want to store this <code>hidden</code> value in our model. So we need to add a new version <code>CoreDataMigration_Example 4</code>. The good news here is that as this change involves adding properties to our model rather than transforming an existing Entity we can use the <code>inferred</code> mapping approach. This means that we need to make even fewer code changes than when we migrated from <code>version2</code> to <code>version3</code>:</p>
<pre><code class="swift">var successor: CoreDataMigrationModel? {
    switch self.version {
    case .version1:
        return CoreDataMigrationModel(version: .version2)
    case .version2:
        return CoreDataMigrationModel(version: .version3)
    case .version3:
        return CoreDataMigrationModel(version: .version4)
    case .version4:
        return nil
    }
}</code></pre>
<p>And that&apos;s it.</p>
<h3 id="makingsureitactuallymigrates">Making sure it actually migrates</h3>
<p>With something as important as data migrations we need to be sure that it actually works. While I won&apos;t post all of the unit tests here, it&apos;s important to touch on those unit tests as creating a Core Data stack that was unit testable played a major role in helping to shape the above approach. So let&apos;s look at how we test that it&apos;s possible to migrate from one version of the model to another:</p>
<pre><code class="swift">func test_individualStepMigration_3to4() {
    let sourceURL = CoreDataMigratorTests.moveFileFromBundleToTmpDirectory(fileName: &quot;CoreDataMigration_Example_3.sqlite&quot;)
    let targetURL = sourceURL

    let modifiedDateBeforeMigration = try! FileManager.default.attributesOfItem(atPath: sourceURL.path)[FileAttributeKey.modificationDate] as! Date

    migrator.migrateStore(from: sourceURL, to: targetURL, targetVersion: CoreDataMigrationModel(version: .version4))

    let modifiedDateAfterMigration = try! FileManager.default.attributesOfItem(atPath: targetURL.path)[FileAttributeKey.modificationDate] as! Date

    XCTAssertTrue(FileManager.default.fileExists(atPath: targetURL.path))
    XCTAssertTrue(modifiedDateAfterMigration.timeIntervalSince(modifiedDateBeforeMigration) &gt; 0)
}</code></pre>
<p>So in the test target we have a number of <code>sqlite</code> files filled with data for each version of our model, we then load the appropriate <code>sqlite</code> file and attempt to migrate checking that such a migration has in fact occurred and been successful.</p>
<p>Please see the <a href="https://github.com/wibosco/CoreDataMigration-Example?ref=williamboles.com">GitHub repo</a> for more unit tests.</p>
<h3 id="wegotthere">We got there &#x1F3C1;</h3>
<p>Core Data migration can often seem like a very dense and difficult to understand process but hopefully with the above example you can see that by breaking it down into small steps and getting creative with how you then connect those steps together, you can actually take a lot of the difficulty out migrations.</p>
<p>And remember if that&apos;s how someone on the bus reacts to having to move their bag, we should try and spare this person the trauma that losing their unlocked <em>thingamabobs</em> would surely cause &#x1F609;.</p>
<hr>
<p>I would like to acknowledge that I leaned on the most excellent <code>Core Data</code> book by <a href="https://twitter.com/floriankugler?lang=en&amp;ref=williamboles.com">Florian Kugler</a> and <a href="https://twitter.com/danielboedewadt?lang=en&amp;ref=williamboles.com">Daniel Eggert</a> which you can get <a href="https://www.objc.io/books/core-data/?ref=williamboles.com">here</a>. I would highly recommend that you give that book a read as it&apos;s a treasure trove of Core Data knowledge.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Roadblocks to repaying tech debt]]></title><description><![CDATA[I've found that talking about tech debt is a favourite subject among developers (especially if the developer responsible for introducing that tech debt is no longer on the project 😉). However, often talk is all that happens]]></description><link>https://williamboles.com/common-roadblocks-when-thinking-about-repaying-tech-debt/</link><guid isPermaLink="false">5a856ededeb9b30018282607</guid><category><![CDATA[teamwork]]></category><category><![CDATA[management]]></category><dc:creator><![CDATA[William Boles]]></dc:creator><pubDate>Wed, 30 Aug 2017 12:49:00 GMT</pubDate><media:content url="https://williamboles.com/content/images/2019/11/traffic-lights-min.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://williamboles.com/content/images/2019/11/traffic-lights-min.jpg" alt="Roadblocks to repaying tech debt"><p>I&apos;ve found that talking about <a href="https://martinfowler.com/bliki/TechnicalDebt.html?ref=williamboles.com">tech debt</a> is a favourite subject among developers (especially if the developer responsible for introducing that tech debt is no longer on the project &#x1F609;). However, often talk is all that happens. As with any potentially ambiguous and daunting task it is easier to talk about it than to actually do it.</p>
<p>In this post I want to explore some of the roadblocks that can come up during these tech debt discussions, what the motivations for those roadblocks could be and how we can overcome them to get on with actually reducing the tech debt in our projects.</p>
<p><img src="https://williamboles.com/content/images/2017/08/tech_debt.jpg" alt="Roadblocks to repaying tech debt" loading="lazy"></p>
<p>In no particular order, let&apos;s look at some.</p>
<h3 id="1wecouldbebuildingfeaturesinsteadofrefactoring">#1. We could be building features instead of refactoring</h3>
<h6 id="motivation">Motivation</h6>
<p>Any time spent addressing tech debt won&apos;t directly benefit the end user.</p>
<h6 id="solution">Solution</h6>
<p><strong>TL;DR</strong>: Keep tech debt refactoring small.</p>
<p>This is a valid concern and one that actually shows that the person raising it cares deeply about the experience the end user has. However it is also flawed as a good tech debt repayment plan involves making frequent small refactoring improvements when the pressure is off rather than big payments when the debt is stopping you from implementing a new feature that the company needs implemented <em>now</em>. This refactoring tasks if undertaken frequently enough will become part of your team&apos;s culture and as such tech debt payments will lose some of their <em>importance</em> and begin to look like any other development task. Not only will doing smaller refactoring reduce the stress of the refactoring, it will/can also improve the quality of unwritten code. It does this by setting the overall quality level/bar of the project higher - this is perfectly described by the <a href="https://blog.codinghorror.com/the-broken-window-theory/?ref=williamboles.com">Broken Window Theory</a> (please note that I&apos;m using the programming version of the Broken Window Theory as my inspiration above - not the actual New York City Police policy). I find it much easier to maintain a certain quality in a project than to try and achieve that quality.</p>
<h3 id="2weneedtosneakpayingbacktechdebtintofeaturedevelopment">#2. We need to <em>sneak</em> paying back tech debt into feature development</h3>
<h6 id="motivation">Motivation</h6>
<p>That tech debt is solely the responsibility of the developers.</p>
<h6 id="solution">Solution</h6>
<p><strong>TL;DR</strong>: Promote tech debt repayments to a wider company level so that everyone is aware of the issues and the benefits from refactoring work.</p>
<p>Discuss how tech debt affects the wider company and agree with the other stakeholders in the project that a percentage of the weekly development effort will be spent addressing tech debt (or generally anything the tech feels is important). We may often feel that we work in <em>feature factories</em> but we are also responsible for the quality of project that we work on and if something is important to us we need to ensure that the wider company knows this. It&apos;s important to note that when describing these concerns to the wider company that we phrase the problem in a way that affects <a href="https://svsg.co/technical-debt-killing-company/?ref=williamboles.com">them</a> - discuss how tech debt slows down development of new features, slows down development of extending features, leads to more bugs and can result in higher developer turn over (compromise for too long and you stop taking pride in your work).</p>
<p>In the past on joining a new team I&apos;ve insisted on having a <code>Captain</code> for each iteration - this <code>Captain</code> is responsible for answering general project questions, uploading the app, releasing the approved version and resolving general bugs (not related to a feature under development). But the most important aspect of the <code>Captain</code> is that they are not assigned any feature (or Product) work, that way when they are not addressing the tasks listed above the <code>Captain</code> is focused on tackling tech debt with the total agreement of the wider company.</p>
<h3 id="3weshouldonlyberefactoringfeaturex">#3. We should only be refactoring feature <em>&quot;X&quot;</em></h3>
<h6 id="motivation">Motivation</h6>
<p>That paying back tech debt on any other feature than <em>&quot;X&quot;</em> is a waste of time and that everyone should focus on feature <em>&apos;&quot;X&quot;</em>.</p>
<blockquote>
<p>Of course someone else feels the same way around feature <em>&quot;Y&quot;</em>.</p>
</blockquote>
<h6 id="solution">Solution</h6>
<p><strong>TL;DR</strong>: Create objectives for an extended period of time (e.g. a quarter) and use those objectives to guide your tech debt refactoring work.</p>
<p>Since the start of 2017 as a team we&apos;ve been experimenting with <a href="https://medium.com/startup-tools/okrs-5afdc298bc28?ref=williamboles.com">OKRs</a> and have found that having team wide objectives (that are connected to wider company objectives) to be a really useful filter when deciding on which tech debt item to address. As a team we can discuss if refactoring feature <em>&quot;X&quot;</em> or feature <em>&quot;Y&quot;</em> is connected to one of our objectives. If the refactoring isn&apos;t connected to an objective we can instantly discard it - it&apos;s not that it&apos;s not important, it&apos;s just that it&apos;s in an area of the app that we have chosen not to focus on.</p>
<p>The more cynical among you dear readers will be thinking:</p>
<p><code>All that does is shift the debate from the tech debt to OKR setting</code></p>
<p>and my response to that is:</p>
<p><code>...kind of</code></p>
<p>Team OKRs should be set based on the wider company objectives which should act as a limiting factor on debate and provide clear boundaries which allow us to better detect when we go off topic. For example if our objective was &quot;Increase revenue by 25%&quot; and we began discussing refactoring our profile screen, it becomes clear that we have moved off topic.</p>
<p>Even if you use OKRs it&apos;s still possible to have multiple different possible tech debt payment options. Here each member of the team gets a certain number of votes that they can spend on the possibilities - so if we have 5 possible options each person gets 2 votes. The option with the smallest number of votes is eliminated until we end up with just one option. That way everyone is involved in the decision making process and while they may not agree with output they can&apos;t deny that they had a say on what it was we should be doing. Often I find people don&apos;t mind not getting their own way but they really want to feel involved in the process and that their views were taken into consideration so the voting system is always great for overall team morale.</p>
<h3 id="4wecantrefactorfeaturexbecauseitworks">#4. We can&apos;t refactor feature <em>&quot;X&quot;</em> because it works</h3>
<h6 id="motivation">Motivation</h6>
<p>That re-opening a feature that is stable is unacceptable risk.</p>
<h6 id="solution">Solution</h6>
<p><strong>TL;DR</strong>: Don&apos;t give into the fear of bugs, instead use tech debt refactoring to introduce automated testing into your project.</p>
<p>This one is tricky as it&apos;s a very valid concern. However you need to discuss what happens if you don&apos;t refactor it and something breaks anyway - would you prefer to refactor a feature when it is impacting your end users and your manager is demanding hourly progress updates or when you can take your time and unit test those features to death? Fear of introducing bugs isn&apos;t a valid argument for not tackling tech debt as  the environments that we work in don&apos;t stand still, the feature may work in one version of the OS but be broken in the version just released. As developers we are solely responsible for introducing bugs into our apps - the only perfect app is the one that exists 100% in your head. A good tech debt repayment should include a form of automated testing to make bugs harder to be introduced in future and to better detail why you chose the solution you did. I believe that a lot of tech debt comes about because the intention of the original developer wasn&apos;t adhered to by subsequent developers - unit tests help to better express that intention by acting as a form of living documentation. Having to fix an unexpected failing test forces us to <em>really</em> think if our change makes sense.</p>
<h3 id="5wecantrefactorfeaturexuntilweagreeonthesolution">#5. We can&apos;t refactor feature <em>&quot;X&quot;</em> until we agree on the solution</h3>
<h6 id="motivation">Motivation</h6>
<p>To not create future tech debt by utilising group thinking.</p>
<h6 id="solution">Solution</h6>
<p><strong>TL;DR</strong>: Accept that every solution we implement will be imperfect and empower the developer to make their own decisions.</p>
<p>It&apos;s important in any team situation to try and seek an agreed approach however you also need to move fast and know when to break <em>rules</em>. A good solution today is better than a perfect one tomorrow. So where that agreed common solution can not be found the developer who is actually going to undertake the work has the final say in which solution they implement. The caveat here is that autonomy is only used here where both approaches are <em>better</em> than the current solution but optimise on different principles e.g. solution A on testability and solution B on readability.</p>
<h3 id="6wedonthavetimeforthat">#6. We don&apos;t have time for that</h3>
<h6 id="motivation">Motivation</h6>
<p>To focus on other development tasks.</p>
<h6 id="solution">Solution</h6>
<p><strong>TL;DR</strong>: Explain the hidden cost of tech debt and make space for repaying it on the product backlog sooner rather than later.</p>
<p><img src="https://williamboles.com/content/images/2017/08/tech_debt_payback.jpg" alt="Roadblocks to repaying tech debt" loading="lazy"></p>
<h3 id="committingtorepayments">Committing to repayments</h3>
<p>Each day we get better as developers and discover new solutions to problems we have already solved, <em>tech debt</em> is the name that we give to those past decisions. Tech debt is unavoidable in development, it&apos;s the cold reminders of our past choices which can lead to very heated debates around future choices. Hopefully the above list of possible issues and how to overcome them will allow you to more quickly move onto making new tech debt for the future rather than just discussing old tech debt.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>