<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Peter Friese</title><description>Firebase, Swift, SwiftUI, and Combine articles and tips by Peter Friese</description><link>https://peterfriese.dev/</link><item><title>Agentic Coding in Xcode with Gemini CLI</title><link>https://peterfriese.dev/blog/2026/agentic-coding-xcode-geminicli/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2026/agentic-coding-xcode-geminicli/</guid><description>Learn how to use Gemini CLI for agentic coding in Xcode 26.3 via the MCP bridge. This post covers setup, configuration, and a practical example of building an emoji physics playground.</description><pubDate>Wed, 11 Feb 2026 12:13:50 GMT</pubDate><content:encoded>&lt;p&gt;With Xcode 26.3 - &lt;a href=&quot;https://www.apple.com/newsroom/2026/02/xcode-26-point-3-unlocks-the-power-of-agentic-coding/&quot;&gt;released on February 3rd, 2026&lt;/a&gt; - Apple enters the era of agentic coding. Welcome, Apple - siriously!&lt;/p&gt;
&lt;p&gt;In the initial release, Claude Code (Anthropic) and Codex (OpenAI) are directly integrated, allowing developers to experience agentic coding directly inside of Xcode. Apple&apos;s documentation provides a good &lt;a href=&quot;https://developer.apple.com/documentation/Xcode/setting-up-coding-intelligence&quot;&gt;overview of how to use&lt;/a&gt; these features - they even created a &lt;a href=&quot;https://www.youtube.com/watch?v=oV6mC8Rt1kY&quot;&gt;walkthrough video&lt;/a&gt;, linked right from the splash screen (that&apos;s a first).&lt;/p&gt;
&lt;p&gt;In addition to using these coding agents within Xcode, you can also use any coding agent with Xcode 26.3 via the MCP bridge Apple provides. The documentation explains &lt;a href=&quot;https://developer.apple.com/documentation/xcode/giving-agentic-coding-tools-access-to-xcode&quot;&gt;how to connect Claude Code and Codex&lt;/a&gt;, but it doesn&apos;t show how you can connect other agents.&lt;/p&gt;
&lt;p&gt;In this article, I&apos;m going to show you how you can connect Gemini CLI to Xcode 26.3 and use agentic coding to build your apps.&lt;/p&gt;
&lt;h3&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;To use agentic coding in Xcode via Gemini CLI, you need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Xcode 26.3&lt;/li&gt;
&lt;li&gt;Gemini CLI v0.27 or later&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My favorite way of installing Xcode is &lt;a href=&quot;https://www.xcodes.app/&quot;&gt;Xcodes&lt;/a&gt; - it provides a graphical UI for installing and managing multiple versions of Xcode, and it downloads much faster than the download via the App Store.&lt;/p&gt;
&lt;p&gt;To install Gemini CLI, run this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -g @google/gemini-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure you have Gemini CLI 0.27.0 or above installed, as it contains a &lt;a href=&quot;https://github.com/google-gemini/gemini-cli/pull/18376&quot;&gt;patch&lt;/a&gt; that works around a &lt;a href=&quot;https://github.com/google-gemini/gemini-cli/issues/18371&quot;&gt;known issue&lt;/a&gt; in the response format Xcode&apos;s MCP bridge returns.&lt;/p&gt;
&lt;p&gt;Run this command to see the version of Gemini CLI you&apos;ve got installed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gemini --version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Enabling Xcode&apos;s MCP server&lt;/h3&gt;
&lt;p&gt;Before you can connect any coding agent to Xcode, you need to enable Xcode&apos;s MCP server.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Launch Xcode 26.3&lt;/li&gt;
&lt;li&gt;Open the settings dialog (&lt;code&gt;CMD + ,&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;em&gt;Intelligence&lt;/em&gt; section&lt;/li&gt;
&lt;li&gt;In the &lt;em&gt;Model Context Protocol&lt;/em&gt; section, enable &lt;em&gt;Xcode Tools&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Connecting Gemini CLI to Xcode&lt;/h3&gt;
&lt;p&gt;To enable Gemini to use Xcode&apos;s MCP server, you need to add it to Gemini&apos;s configuration. You can either do this globally, or per project. I recommend doing this on a per-project basis - you will see why in a minute.&lt;/p&gt;
&lt;p&gt;At the root folder of your project, run the following command to add Xcode&apos;s MCP server configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gemini mcp add xcode-tools xcrun mcpbridge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will add the following configuration to &lt;code&gt;.gemini/settings.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;mcpServers&quot;: {
    &quot;xcode-tools&quot;: {
      &quot;command&quot;: &quot;xcrun&quot;,
      &quot;args&quot;: [&quot;mcpbridge&quot;]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Starting an agentic coding session&lt;/h3&gt;
&lt;p&gt;You&apos;re all set to use Xcode and Gemini CLI for agentic coding:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, launch Xcode&lt;/li&gt;
&lt;li&gt;Then, launch Gemini CLI in the root of your project folder&lt;/li&gt;
&lt;li&gt;Xcode will ask for your permission to allow Gemini CLI to access its MCP server&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This dialog will pop up every time you run Gemini CLI with the Xcode MCP server enabled, and this is the reason why I recommend configuring Xcode&apos;s MCP server on a per-project basis. No need to have Xcode ask for your permission if you&apos;re working on a project that doesn&apos;t even contain an Xcode project.&lt;/p&gt;
&lt;p&gt;To see the list of available tools, you can use the &lt;code&gt;/mcp list&lt;/code&gt; slash command:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;For a detailed description of the tools, you can use &lt;code&gt;/mcp desc&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, Xcode&apos;s MCP server doesn&apos;t support creating new projects, so you&apos;ll have to do that manually from inside Xcode.&lt;/p&gt;
&lt;h3&gt;Let&apos;s build something cool!&lt;/h3&gt;
&lt;p&gt;Here is a project you can try out:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Xcode, select &lt;em&gt;File &amp;gt; Project...&lt;/em&gt; to create a new project&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;iOS &amp;gt; App&lt;/em&gt; , and click &lt;em&gt;Next&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Choose a product name for your app (I chose &lt;code&gt;Newtons Apple&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Next&lt;/em&gt; and save the project&lt;/li&gt;
&lt;li&gt;Now, open your terminal and navigate to the root folder of the Xcode project:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;cd ~/Documents/Newtons\ Apple
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Connect Gemini CLI:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;gemini mcp add xcode-tools xcrun mcpbridge
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Launch Gemini CLI:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;gemini
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Confirm the Xcode MCP server permission&lt;/li&gt;
&lt;li&gt;Paste the following prompt:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;Replace the current view with a SpriteKit scene integrated into SwiftUI. I want to create a physics playground. The text &apos;Hello World&apos; should be a static rigid body in the center. When I tap the screen, spawn random fruit emojis (🍎, 🍌, 🍇) that fall from the top and bounce off the text. Add a button at the bottom labeled &apos;Antigravity&apos; that, when held, reverses gravity so the fruits float upward.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now, observe Gemini CLI communicating with Xcode via the MCP bridge to build this emoji physics playground. The first version of the code might still contain bugs, but the agent will figure this out by building the app and fixing any compile errors that Xcode reports.&lt;/p&gt;
&lt;p&gt;The result should look similar to this:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can interact with the app inside the Xcode Preview (or run it on a Simulator or a physical device) - just tap somewhere on the screen to spawn a new emoji to drop down. You can use the &lt;em&gt;Antigravity&lt;/em&gt; button to cancel gravity and make the emojis fly up in the air.&lt;/p&gt;
&lt;p&gt;Go give it a try, and play around with it! If you built something cool, let me know!&lt;/p&gt;
</content:encoded></item><item><title>Turn Your Photos Into Miniature Magic with Nano Banana</title><link>https://peterfriese.dev/blog/2025/gemini-nano-banana-ios/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2025/gemini-nano-banana-ios/</guid><description>Learn how to use Nano Banana (Gemini 2.5 Flash Image Generation) and Firebase AI Logic to add powerful text-to-image and image-to-image generation capabilities to your iOS applications with just a few lines of Swift code.</description><pubDate>Fri, 12 Sep 2025 13:48:15 GMT</pubDate><content:encoded>&lt;p&gt;Google just released Nano Banana, and you&apos;ve probably seen the amazing images you can generate with it. One of Nano Banana&apos;s standout features is image editing, image compositing, and multi-turn image manipulation.&lt;/p&gt;
&lt;p&gt;In this blog post, I am going to show you how you can use Nano Banana in your iOS apps to create amazing images from text, and from other images, with just a few lines of code.&lt;/p&gt;
&lt;p&gt;An easy way to try out Nano Banana is the Gemini chat app - it contains a couple of cool prompts that help you get started. For example, there is one to turn you into a mini action figure!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;First ask me to upload an image and then create a 1/7 scale
commercialized figurine of the characters in the picture,
in a realistic style, in a real environment. The figurine
is placed on a computer desk. The computer is a MacBook Pro 16”,
the figurine has a round transparent acrylic base, with no text on
the base. The content on the computer screen is a 3D modeling
process of this figurine. Next to the computer screen is a toy
packaging box, designed in a style reminiscent of high-quality
collectible figures, printed with original artwork. The packaging
features two-dimensional flat illustrations.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Inspired by this, I created a prompt for generating cozy miniature dioramas - something I&apos;ve always wanted of my own office.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is great, but you know what&apos;s better than a cozy miniature diorama of your room?&lt;/p&gt;
&lt;p&gt;That&apos;s right - an entire collection of them!&lt;/p&gt;
&lt;p&gt;So I built an app just for that, and in this blog post I&apos;m going to show you how I built it. If you want to follow along, you can check out the source code from this GitHub repository.&lt;/p&gt;
&lt;h3&gt;Setting up your project&lt;/h3&gt;
&lt;p&gt;Before you can use Nano Banana in your iOS app, you have to set up your project:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new Firebase project on the &lt;a href=&quot;https://console.firebase.google.com/&quot;&gt;Firebase console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Enable Firebase AI Logic&lt;/li&gt;
&lt;li&gt;Download the Firebase configuration file&lt;/li&gt;
&lt;li&gt;and add it to your Xcode project&lt;/li&gt;
&lt;li&gt;Add the Firebase SDK to your project&apos;s dependencies (&lt;em&gt;File&lt;/em&gt; &amp;gt; &lt;em&gt;Add Package Dependencies&lt;/em&gt;, then insert &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk&quot;&gt;&lt;code&gt;https://github.com/firebase/firebase-ios-sdk&lt;/code&gt;&lt;/a&gt; into the search bar and click &lt;em&gt;Add Package&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;FirebaseAI&lt;/code&gt; from the list of products and add it to your main target, then click &lt;em&gt;Add Package&lt;/em&gt; once more.&lt;/li&gt;
&lt;li&gt;In your app&apos;s &lt;code&gt;init()&lt;/code&gt; method, &lt;code&gt;import Firebase&lt;/code&gt; and call &lt;code&gt;FirebaseApp.configure()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Setting up the model&lt;/h3&gt;
&lt;p&gt;Before you can use Gemini, you need to create an instance of the model. Firebase AI Logic allows you to communicate with Gemini models in a secure way - at no time do you have to keep a Gemini API key in your client app. This greatly reduces the risk for exfiltrating your API key. In addition, you can also use &lt;a href=&quot;https://firebase.google.com/docs/ai-logic/app-check&quot;&gt;App Check&lt;/a&gt; to ensure that only your legitimate app can access Gemini via your Firebase project.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var model: GenerativeModel = {
  let ai = FirebaseAI.firebaseAI()
  let config = GenerationConfig(responseModalities: [.text, .image])
  let modelName = &quot;gemini-2.5-flash-image-preview&quot;

  return ai.generativeModel(
    modelName: modelName,
    generationConfig: config
  )
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nano Banana is just the public name of the model, the technical name is &lt;code&gt;gemini-2.5-flash-image-preview&lt;/code&gt;, and it is safe to assume the &lt;code&gt;-preview&lt;/code&gt; suffix might be dropped at a future point in time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would recommend using Remote Config so you can easily switch to a new model without having to ship a new version of your app to the App Store. I covered this in detail in my livestream &lt;a href=&quot;https://www.youtube.com/watch?v=u_ZYmX1fbeI&quot;&gt;Never Ship a Bad AI Prompt Again: Dynamic iOS AI with Firebase Remote Config&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When using Nano Banana, you need to tell the SDK that this is a multimodal model. This is done by setting up the &lt;code&gt;GenerationConfig&lt;/code&gt; using the &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;image&lt;/code&gt; response modalities. Multimodal models return their responses as a structured list of parts which can contain interleaved text and image data. This is pretty useful when asking for a cooking recipe, as the model can return a matching image for each of the preparation steps.&lt;/p&gt;
&lt;h3&gt;Creating images from text&lt;/h3&gt;
&lt;p&gt;Nano Banana is capable of generating stunning images just from text descriptions. Based on the prompt I mentioned earlier, here is how you can call Gemini via Firebase AI Logic to generate images:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func generateRoom() async throws -&amp;gt; UIImage {
  let prompt = &quot;&quot;&quot;
  Create a detailed 3D illustration of a miniature, cozy workspace in a cube. \
  The perspective should be isometric, looking down into the room from a diagonal \
  angle. The room should be filled with tiny, charming details that reflect a \
  person&apos;s hobbies and personality. Include a wooden desk with a laptop, a \
  comfortable chair, shelves with books and plants, and warm, soft lighting.
  &quot;&quot;&quot;

  let response = try await model.generateContent(prompt)

  guard let candidate = response.candidates.first else {
    throw ImageGenerationError.modelResponseInvalid
  }

  for part in candidate.content.parts {
    if let inlineDataPart = part as? InlineDataPart {
      if let image = UIImage(data: inlineDataPart.data) {
        return image
      }
    }
  }

  throw ImageGenerationError.missingGeneratedImage
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example&quot;&amp;gt;&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;We use the &lt;code&gt;for&lt;/code&gt; loop to iterate over all the multimodal parts included in the model&apos;s response. Once we&apos;ve found an &lt;code&gt;InlineDataPart&lt;/code&gt;, we convert it to an image and return it. If no image is returned, the &lt;code&gt;generateRoom&lt;/code&gt; method throws an error.&lt;/p&gt;
&lt;h3&gt;Creating images from image and text&lt;/h3&gt;
&lt;p&gt;Generating images from text is quite impressive, but generating images based on a source image is even more amazing. To make this happen, all we need to do is pass an image to the model, alongside the text instructions. The code is remarkably similar to the one in the previous section:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func generateRoom(from image: UIImage) async throws -&amp;gt; UIImage {
  let prompt = &quot;&quot;&quot;
  Create a detailed 3D illustration of a miniature, cozy workspace in a cube. \
  The perspective should be isometric, looking down into the room from a diagonal \
  angle. The room should be filled with tiny, charming details that reflect a \
  person&apos;s hobbies and personality. Include a wooden desk with a laptop, a \
  comfortable chair, shelves with books and plants, and warm, soft lighting.
  &quot;&quot;&quot;

  let response = try await model.generateContent(image, prompt)

  guard let candidate = response.candidates.first else {
    throw ImageGenerationError.modelResponseInvalid
  }

  for part in candidate.content.parts {
    if let inlineDataPart = part as? InlineDataPart {
      if let image = UIImage(data: inlineDataPart.data) {
        return image
      }
    }
  }

  throw ImageGenerationError.missingGeneratedImage
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example&quot;&amp;gt;


&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Here is how this turned out for a snapshot of a corner of the music corner in my room. Notice how the instructions in the prompt to generate a workspace and include a laptop, chair, and books are very strong - even though those elements aren&apos;t visible in the photo, the model includes them in the output image.&lt;/p&gt;
&lt;h3&gt;Analysing images&lt;/h3&gt;
&lt;p&gt;Wouldn&apos;t it be nice if the generated images looked more like the source image? For example, if I took a photo of a nice bowl of fruit on my dining table, I&apos;d prefer the result to not include an office chair and a laptop...&lt;/p&gt;
&lt;p&gt;You might also have noticed that the descriptions of the generated images were hardcoded, so let&apos;s try to generate titles and descriptions that are based on the output image!&lt;/p&gt;
&lt;p&gt;Nano Banana isn&apos;t just great at generating images, it can also analyse images - for example, you can ask it to provide a detailed list of items that can be seen in an image.&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at the prompt that makes this possible!&lt;/p&gt;
&lt;p&gt;First, we want the model to analyse the input image and determine which kind of room this is (kitchen, study, bedroom, etc.):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;**Task 1: Generate Image**
Analyze the provided image to determine the primary room type
(e.g., kitchen, study, bedroom), identify its key furniture,
objects, and decor elements, and grasp the overall mood or style.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we&apos;ll want the model to generate the image based on this room type and the source image:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Then, create a single, highly detailed 3D digital illustration of a *miniature,
cozy version* of that room. It should be meticulously crafted within an
isometric cube, viewed from a diagonal top-down perspective, ensuring all
depicted elements maintain **consistent and realistic miniature proportions
relative to each other within the diorama**, regardless of their apparent
scale in the input image.

The miniature room must incorporate charming, tiny details inspired by
the elements observed in the input image, enhancing its unique personality
and atmosphere. Focus on creating a coherent, aesthetically pleasing
miniature scene where all objects appear in their correct, downscaled
relationships. The lighting should be warm, soft, and inviting, creating
a cozy ambiance.

For the background, use a neutral backdrop that extends into the
distance, making the miniature room the only thing that stands out. The
background should be minimal and out of focus to ensure the miniature room
is the sole focal point.

* **Art Style:** Diorama, claymation aesthetic, whimsical, highly detailed
miniature, octane render, soft focus, depth of field.
* **Negative Prompt:** empty, sterile, messy, dark, distorted proportions,
unrealistic scale, blurry, direct photorealistic copy of input image.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, the model should analyse the result image and return a title, description, and a list of key items that it included from the source image:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;**Task 2: Analyze and Describe**
Immediately after generating the miniature room image, analyze its contents and
provide a response formatted *exactly* as follows, with no additional commentary:

Title: [A 3-5 word catchy title based on the *generated image&apos;s* theme]
Description: [A heartwarming description of the *rendered miniature image*,
focusing on its cozy details, the soft lighting, and the personality
reflected in its objects.]
Detected Items: [A comma-separated list of the key elements you successfully
identified and incorporated from the *provided input image* into the miniature.]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The generated result will now contain two modalities: &lt;code&gt;.text&lt;/code&gt; (with the title, description, and detected elements), and &lt;code&gt;.image&lt;/code&gt; for the generated image.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func generateRoom(from image: UIImage) async throws -&amp;gt; (name: String, description: String, image: UIImage, detectedItems: [String], prompt: String) {
  let prompt = &quot;&quot;&quot;
  **Task 1: Generate Image**
  ... (rest of the prompt as above)
  &quot;&quot;&quot;

  let response = try await model.generateContent(image, prompt)

    var generatedText: String?
    var generatedImage: UIImage?

    guard let candidate = response.candidates.first else {
      throw ImageGenerationError.modelResponseInvalid
    }

    for part in candidate.content.parts {
      if let textPart = part as? TextPart {
        if generatedText == nil { // Only take the first text part
          generatedText = textPart.text
        }
      } else if let inlineDataPart = part as? InlineDataPart {
        if generatedImage == nil, let image = UIImage(data: inlineDataPart.data) { // Only take the first valid image part
          generatedImage = image
        }
      }
    }

    guard let text = generatedText else {
      throw ImageGenerationError.parsingFailed(missingParts: [&quot;Title&quot;, &quot;Description&quot;, &quot;Detected Items&quot;])
    }
    guard let image = generatedImage else {
      throw ImageGenerationError.missingGeneratedImage
    }

    let (title, description, detectedItems) = try parseGeneratedContent(from: text)

    return (name: title, description: description, image: image, detectedItems: detectedItems, prompt: prompt)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;And now, we get a much better result based on the same input image.&lt;/p&gt;
&lt;h3&gt;Resources&lt;/h3&gt;
&lt;p&gt;I&apos;ve compiled a couple of resources that will help you create amazing apps using Firebase AI Logic and Nano Banana:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://ai.google.dev/gemini-api/docs/image-generation&quot;&gt;Gemini API documentation&lt;/a&gt; provides a good overview of the image generation capabilities of Gemini / Nano Banana&lt;/li&gt;
&lt;li&gt;Refer to the &lt;a href=&quot;https://firebase.google.com/docs/ai-logic/generate-images-gemini?api=dev&quot;&gt;Firebase AI Logic documentation&lt;/a&gt; for more details about how to use Firebase AI Logic to call Nano Banana.&lt;/li&gt;
&lt;li&gt;There&apos;s also a great section that discusses &lt;a href=&quot;https://firebase.google.com/docs/ai-logic/generate-images-gemini?api=dev#limitations-and-best-practices&quot;&gt;limitations and best practices&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Also check out this blog post for some great tips and tricks for &lt;a href=&quot;https://developers.googleblog.com/en/how-to-prompt-gemini-2-5-flash-image-generation-for-the-best-results/&quot;&gt;getting the best results&lt;/a&gt; when generating images with Nano Banana.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tips and Tricks&lt;/h3&gt;
&lt;p&gt;Here are some tips and tricks you might find useful when working with Firebase AI Logic and Gemini models&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When using the Vertex AI backend, make sure to set your region to &lt;code&gt;global&lt;/code&gt; if you use preview models (whose name ends in &lt;code&gt;-preview&lt;/code&gt;): &lt;code&gt;let ai = FirebaseAI.firebaseAI(backend: .vertexAI(location: &quot;global&quot;))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;-FIRDebugEnabled&lt;/code&gt; to your target&apos;s launch arguments to turn on debug logging in Firebase. Once you activate this, Firebase AI Logic will print out cURL statements for each call you make, including the API key. You can then run these via the terminal, which is super useful for quickly iterating and debugging. Just make sure to turn this off when shipping to production to avoid exposing your API key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What&apos;s next?&lt;/h3&gt;
&lt;p&gt;You now have the fundamental knowledge to create and edit amazing images with Gemini 2.5 Flash Image Generation (Nano Banana) in your iOS apps.&lt;/p&gt;
&lt;p&gt;What will you build next? &lt;a href=&quot;https://bsky.app/profile/peterfriese.dev&quot;&gt;Let me know&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Also, go and &lt;a href=&quot;https://firebase.google.com/docs/ai-logic/app-check?api=dev&quot;&gt;turn on App Check for your project&lt;/a&gt;, for real.&lt;/p&gt;
</content:encoded></item><item><title>Reverse-Engineering Xcode&apos;s Coding Intelligence prompt</title><link>https://peterfriese.dev/blog/2025/reveng-xcode-coding-intelligence/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2025/reveng-xcode-coding-intelligence/</guid><description>Ever wondered how Xcode&apos;s Coding Intelligence prompt works? In this blog post, we&apos;ll take a deep dive into the prompt&apos;s inner workings and explore how it helps developers write code more efficiently.</description><pubDate>Sat, 21 Jun 2025 09:41:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the most anticipated features of Xcode 26 is Coding Intelligence. Previewed as &lt;a href=&quot;https://developer.apple.com/news/?id=a693fazi&quot;&gt;Swift Assist last year at WWDC 2024&lt;/a&gt;, it never materialised, and &lt;a href=&quot;https://dimillian.medium.com/where-is-swift-assist-6ea348767cf3&quot;&gt;some even suspected&lt;/a&gt; it might never ship.&lt;/p&gt;
&lt;p&gt;But this year, Apple delivered: Xcode 26 includes a fully functional version of Coding Intelligence, an AI-powered coding companion for a wide variety of coding tasks. Yes - you can vibe code with Xcode now - no more third party tools required.&lt;/p&gt;
&lt;p&gt;Coding Intelligence is not a single feature, but a collection of features that allow you to write code, understand unfamiliar code bases (like your own code you wrote 2 weeks ago ;-)), refactor existing code, generate new code, and generate documentation for your code (this is not as stupid as it might sound, as we&apos;ll discuss further down in this blog post).&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To use Coding Intelligence, you need Xcode 26 (beta) and macOS 26 Tahoe (beta), and you need to have Apple Intelligence turned on in your system preferences.&lt;/p&gt;
&lt;h2&gt;A problem - and a solution&lt;/h2&gt;
&lt;p&gt;Support for ChatGPT is built in - at least, that&apos;s what the &lt;a href=&quot;https://developer.apple.com/documentation/xcode/writing-code-with-intelligence-in-xcode&quot;&gt;documentation&lt;/a&gt; says. Unfortunately, I wasn&apos;t able to make it work on my machine (yes, I did &lt;a href=&quot;https://www.youtube.com/watch?v=5UT8RkSmN4k&quot;&gt;turn it off and on again&lt;/a&gt;, as &lt;a href=&quot;https://developer.apple.com/documentation/xcode-release-notes/xcode-26-release-notes#Coding-Intelligence&quot;&gt;recommended&lt;/a&gt; by the Xcode release notes).&lt;/p&gt;
&lt;p&gt;The good news is that Xcode supports BYOM (bring your own models) via a Model Provider approach.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/simonbs&quot;&gt;Simon B. Støvring&lt;/a&gt; wrote an excellent article showing &lt;a href=&quot;https://simonbs.dev/posts/using-claude-with-coding-assistant-in-xcode-26/&quot;&gt;how to set up a model provider for Claude&lt;/a&gt;, but I wanted to use Gemini.&lt;/p&gt;
&lt;p&gt;Since Gemini - in addition to its own API - provides an &lt;a href=&quot;https://ai.google.dev/gemini-api/docs/openai&quot;&gt;OpenAI compatible API endpoint&lt;/a&gt;, it should be easy enough to set up a model provider for Gemini. Unfortunately, it&apos;s not as easy as that, and (at least for now), we need to jump through a hoop to connect Xcode&apos;s Coding Intelligence to Gemini. &lt;a href=&quot;https://bsky.app/profile/zottmann.dev&quot;&gt;Carlo Zottmann&lt;/a&gt; wrote an excellent blog post explaining in detail how to do this: &lt;a href=&quot;https://zottmann.org/2025/06/13/how-to-use-google-gemini.html&quot;&gt;How to use Google Gemini in Xcode 26 beta&lt;/a&gt;. It&apos;s really well written, so I won&apos;t reproduce it here.&lt;/p&gt;
&lt;p&gt;Carlo&apos;s approach makes use of request rewriting using Proxyman, and this leads us to...&lt;/p&gt;
&lt;h2&gt;Reverse engineering Xcode&apos;s system instructions&lt;/h2&gt;
&lt;p&gt;Since all requests to the model are now routed through &lt;a href=&quot;https://proxyman.com/&quot;&gt;Proxyman&lt;/a&gt;, we can now inspect the communication between Xcode&apos;s Coding Intelligence feature and the LLM in plain text, which gives us a unique insight into how this feature works.&lt;/p&gt;
&lt;p&gt;For example, here is Coding Intelligence in action in the moderately large code base of Sofia, the personal knowledge management app I am working on (join my &lt;a href=&quot;https://www.youtube.com/playlist?list=PLsnLd2esiGRTmfGZcZMnEy6hkBHXBH_en&quot;&gt;weekly livestreams&lt;/a&gt; to follow along as I build this app):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;And here is the corresponding request and response pair in Proxyman:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The request contains a multi-part prompt, consisting of the system instructions and the user prompt. You can also see that Xcode asks the model to stream its response, which is why the response is sent as SSE (&lt;a href=&quot;https://en.wikipedia.org/wiki/Server-sent_events&quot;&gt;Server Sent Events&lt;/a&gt;). Proxyman has built-in support for inspecting SSE responses, which you can see on the right hand side of the screen.&lt;/p&gt;
&lt;p&gt;To make it easier to analyse the system instructions, I&apos;ll paste them below, section by section.&lt;/p&gt;
&lt;h3&gt;Preamble&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;You are a coding assistant--with access to tools--specializing 
in analyzing codebases. Below is the content of the file the 
user is working on. Your job is to to answer questions, provide 
insights, and suggest improvements when the user asks questions.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a typical preamble, providing basic information about the role the model is to take on. Note the mention of tools - this is one of the the key aspects that makes Coding Intelligence work.&lt;/p&gt;
&lt;h3&gt;Think before acting&lt;/h3&gt;
&lt;p&gt;Some code-editing agents are known to be overly eager to write code. To prevent this from happening, the next part of the system instructions explicitly tells the model to only answer with code snippets once it has all the required information:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Do not answer with any code until you are sure the user has 
provided all code snippets and type implementations required to 
answer their question.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next section is a variation of Chain-of-thought (COT) prompting, making sure the model generates an answer that helps itself reason deeper about the user&apos;s code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Briefly--in as little text as possible--walk through the solution 
in prose to identify types you need that are missing from the files 
that have been sent to you.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Search Tool&lt;/h3&gt;
&lt;p&gt;To help the model identify the relevant parts of the user&apos;s code, the next part of the system instruction tells the model that it can use the &lt;code&gt;SEARCH&lt;/code&gt; tool to retrieve more context about interesting-looking types in the code it is presented with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Search the project for these types and wait for them to be 
provided to you before continuing. Use the following search 
syntax at the end of your response, each on a separate line:

##SEARCH: TypeName1 // [!code highlight]
##SEARCH: a phrase or set of keywords to search for // [!code highlight]
and so on...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;ll dig deeper into how this works later in this blog post.&lt;/p&gt;
&lt;h3&gt;Preferred languages and platforms&lt;/h3&gt;
&lt;p&gt;In the next few paragraphs, the system instructions lay down preferences for the programming language(s) to be used, the platforms to be considered, and the spelling of Apple&apos;s products.&lt;/p&gt;
&lt;p&gt;First, the model is asked to prefer Swift, unless the user specifies otherwise:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Whenever possible, favor Apple programming languages and 
frameworks or APIs that are already available on Apple devices. 
Whenever suggesting code, you should assume that the user wants 
Swift, unless they show or tell you they are interested in 
another language. 

Always prefer Swift, Objective-C, C, and C++ over alternatives. // [!code highlight]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model is then instructed to make sure it generates code for the correct platform:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Pay close attention to the platform that this code is for. 
For example, if you see clues that the user is writing a Mac 
app, avoid suggesting iOS-only APIs.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next section makes sure Apple&apos;s platforms are spelled correctly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Refer to Apple platforms with their official names, like iOS, 
iPadOS, macOS, watchOS and visionOS. Avoid mentioning specific 
products and instead use these platform names.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Testing&lt;/h3&gt;
&lt;p&gt;The next few sections provide guidelines for how to generate code. It&apos;s quite interesting to note that the system instructions talk about testing before any other coding aspects:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In most projects, you can also provide code examples using the new 
Swift Testing framework that uses Swift Macros. An example of this 
code is below:

```swift

import Testing

// Optional, you can also just say `@Suite` with no parentheses.
@Suite(&quot;You can put a test suite name here, formatted as normal text.&quot;)
struct AddingTwoNumbersTests {

    @Test(&quot;Adding 3 and 7&quot;)
    func add3And7() async throws {
        let three = 3
        let seven = 7

        // All assertions are written as &quot;expect&quot; statements now.
        #expect(three + seven == 10, &quot;The sums should work out.&quot;)
    }

    @Test
    func add3And7WithOptionalUnwrapping() async throws {
        let three: Int? = 3
        let seven = 7

        // Similar to `XCTUnwrap`
        let unwrappedThree = try #require(three)

        let sum = three + seven

        #expect(sum == 10)
    }

}
```
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Swift Concurrency &amp;gt;= Combine&lt;/h3&gt;
&lt;p&gt;In case we all didn&apos;t get the memo, here it is black on white (or white on black, if you prefer dark mode):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In general, prefer the use of Swift Concurrency (async/await, 
actors, etc.) over tools like Dispatch or Combine, but if the 
user&apos;s code or words show you they may prefer something else, 
you should be flexible to this preference.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All new code should use Swift Concurrency rather than Combine. Loud and clear.&lt;/p&gt;
&lt;h3&gt;Dealing with the user&apos;s code snippets&lt;/h3&gt;
&lt;p&gt;Next, the system instructions talk about how to handle any code snippets the user provides (e.g. by selecting chunks of code, or by pasting code snippets into the chat window):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Sometimes, the user may provide specific code snippets for your 
use. These may be things like the current file, a selection, other 
files you can suggest changing, or 
code that looks like generated Swift interfaces — which represent // [!code highlight]
things you should not try to change. // [!code highlight]

However, this query will start without any additional context.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By explicitly calling out &lt;code&gt;that looks like generated Swift interfaces - which represent things you should not try to change&lt;/code&gt;, the system instructions try to make sure the model doesn&apos;t suggest making changes to Apple&apos;s APIs. An important aspect which is easily overlooked.&lt;/p&gt;
&lt;h3&gt;Changing code&lt;/h3&gt;
&lt;p&gt;The next section is one of the key parts of the system instructions and provides guidance for how to generate code.&lt;/p&gt;
&lt;p&gt;First, the instructions for changing existing code. Asking the model to include all existing code as well as any changes required makes it easier for Xcode to replace the existing code instead of having to identify the sections in the user&apos;s code that should be changed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;When it makes sense, you should propose changes to existing code. 
Whenever you are proposing changes to an existing file, 
it is imperative that you repeat the entire file, without ever 
eliding pieces, even if they will be kept identical to how they are 
currently. To indicate that you are revising an existing file 
in a code sample, put &quot;```language:filename&quot; before the revised 
code. It is critical that you only propose replacing files that 
have been sent to you. For example, if you are revising 
FooBar.swift, you would say:

```swift:FooBar.swift
// the entire code of the file with your changes goes here.
// Do not skip over anything.
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This also allows Xcode to use a diff algorithm to highlight the changes parts of the user&apos;s code in the IDE&apos;s UI.&lt;/p&gt;
&lt;p&gt;Next, here are the instructions for adding new code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;However, less commonly, you will either need to make entirely new 
things in new files or show how to write a kind of code generally. 
When you are in this rarer circumstance, you can just show the 
user a code snippet, with normal markdown:
```swift
// Swift code here
```
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Anti-prompt hacking&lt;/h3&gt;
&lt;p&gt;No good prompt should end without some instructions that prevent the user from hacking the system prompt. The following is a pretty lenient way to do this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You are currently in Xcode with a project open.

Try not to disclose that you&apos;ve seen the context 
above, but use it freely to engage in your conversation.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Coding Intelligence in Action&lt;/h2&gt;
&lt;p&gt;Let&apos;s take a look at some typical coding tasks and see how the system instructions work in conjunction with the respective user prompts.&lt;/p&gt;
&lt;h3&gt;Exploring unfamiliar code&lt;/h3&gt;
&lt;p&gt;Going back to first conversation with Coding Intelligence, Let&apos;s first take a look at the user prompt. If you remember, I asked &lt;code&gt;What does this app do?&lt;/code&gt; in the code base of my personal knowledge management app, Sofia.&lt;/p&gt;
&lt;p&gt;In addition to the system instructions, Coding Intelligence sent the following user prompt to the LLM:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The user is currently inside this file: ArtifactListScreen.swift
The contents are below:
```swift:ArtifactListScreen.swift
(Xcode included the entire contents of ArtifactListScreen.swift)
```

The user has no code selected.
The user has asked:

What does this app do?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty compact - all Xcode did was to prefix my own query (&lt;code&gt;What does this app do?&lt;/code&gt;) with the contents of the currently open file.&lt;/p&gt;
&lt;p&gt;This works thanks to the elaborate system instructions which contain all the additional behavioural guidance the model needs to perform the expected task.&lt;/p&gt;
&lt;p&gt;If you take a look at the screenshot, you will notice that Coding Intelligence actually took another turn to provide a better answer.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;It all starts with the end of the first answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To give you a more detailed analysis, I need to understand the structure of the data it&apos;s working with. Could you please provide the definitions for the following types?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, this little fragment wouldn&apos;t be enough to trigger Xcode to call the model a second time. What&apos;s actually happening here is called Tool Calling (often known as Function Calling). The Gemini developer documentation has a good &lt;a href=&quot;https://ai.google.dev/gemini-api/docs/function-calling?example=meeting#rest_1&quot;&gt;introduction into how this works&lt;/a&gt;. Essentially, the LLM returns structured output (JSON) that includes the request to call a local function. Looking at the response in Postman, we can see that the response ends as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;data:
{
  &quot;choices&quot;: [
    {
      &quot;delta&quot;: {
        &quot;content&quot;: &quot; saved content.\n\nTo give you a more detailed 
        analysis, I need to understand the structure of the data 
        it&apos;s working&quot;,
        &quot;role&quot;: &quot;assistant&quot;
      },
      &quot;index&quot;: 0
    }
  ],
  &quot;created&quot;: 1750425377,
  &quot;id&quot;: &quot;EV9VaLvdF6fAxN8Pvtzc0As&quot;,
  &quot;model&quot;: &quot;models/gemini-2.5-pro&quot;,
  &quot;object&quot;: &quot;chat.completion.chunk&quot;
}


data:
{
  &quot;choices&quot;: [
    {
      &quot;delta&quot;: {
        &quot;content&quot;: &quot; with. Could you please provide the definitions 
        for the following types?\n\n
        ##SEARCH: ArtifactProtocol\n##SEARCH: Filter\n##&quot;,
        &quot;role&quot;: &quot;assistant&quot;
      },
      &quot;index&quot;: 0
    }
  ],
  &quot;created&quot;: 1750425378,
  &quot;id&quot;: &quot;EV9VaLvdF6fAxN8Pvtzc0As&quot;,
  &quot;model&quot;: &quot;models/gemini-2.5-pro&quot;,
  &quot;object&quot;: &quot;chat.completion.chunk&quot;
}


data:
{
  &quot;choices&quot;: [
    {
      &quot;delta&quot;: {
        &quot;content&quot;: &quot;SEARCH: FilteredArtifactStore&quot;,
        &quot;role&quot;: &quot;assistant&quot;
      },
      &quot;finish_reason&quot;: &quot;stop&quot;,
      &quot;index&quot;: 0
    }
  ],
  &quot;created&quot;: 1750425378,
  &quot;id&quot;: &quot;EV9VaLvdF6fAxN8Pvtzc0As&quot;,
  &quot;model&quot;: &quot;models/gemini-2.5-pro&quot;,
  &quot;object&quot;: &quot;chat.completion.chunk&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it a bit clearer, Let&apos;s look at the plain text version of the response:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Could you please provide the definitions for the following types?

##SEARCH: ArtifactProtocol // [!code highlight]
##SEARCH: Filter // [!code highlight]
##SEARCH: FilteredArtifactStore // [!code highlight]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final three lines aren&apos;t shown in the Coding Intelligence chat window, as they&apos;re filtered out by Xcode - they follow the syntax laid out previously in the system instructions (see Search Tool).&lt;/p&gt;
&lt;p&gt;And this is what causes Xcode to search the code base of the current project for those types (and other types that are relevant, because they implement the protocol(s) or inherit any given classes):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Looking at the response Xcode sends back to the LLM, we can see that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It includes the conversation history (the model&apos;s original response is sent back using the &lt;code&gt;assistant&lt;/code&gt; role)&lt;/li&gt;
&lt;li&gt;It also includes the files the search tool found (and which are shown in the popover window seen in the above screenshot)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Each file is provided as a Markdown fenced code block, just as specified in the system instructions.&lt;/p&gt;
&lt;h3&gt;Explaining a chunk of code&lt;/h3&gt;
&lt;p&gt;Coding Intelligence can also help explain what a piece of code does. For example, to explain the &lt;code&gt;updateFilter&lt;/code&gt; function in my code base, you can select the entire body of the function, then click the &lt;em&gt;AI sparkles&lt;/em&gt; icon in the editor gutter, and select &lt;em&gt;Explain&lt;/em&gt; from the context menu:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This will explain how the function works:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Looking at the request in Postman, we can see that Xcode sends a multipart prompt consisting of the system instructions and the user prompt, which looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The user is currently inside this file: ArtifactListScreen.swift
The contents are below:
```swift:ArtifactListScreen.swift
(entire text of the ArtifactListScreen.swift)
```

The user has asked:

Explain this to me.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Documenting Code&lt;/h3&gt;
&lt;p&gt;Writing documentation is probably one of the least favourite tasks of any developer - after all, the source code is the truth, no?&lt;/p&gt;
&lt;p&gt;Writing docs to &lt;a href=&quot;https://programmerhumor.io/programming-memes/the-documentation-paradox-wp6s&quot;&gt;help your future self remember how stuff works&lt;/a&gt; is just one reason for writing documentation, but you might rightfully ask if it wouldn&apos;t be easier to ask an AI to explain how your code works instead of documenting it (see above).&lt;/p&gt;
&lt;p&gt;However, documenting your APIs is generally a good idea, not just for SDK teams (like Apple&apos;s SwiftUI team or the Firebase team). Why not use an AI to help you put together the first draft?&lt;/p&gt;
&lt;p&gt;Let&apos;s see how well this works by asking Coding Intelligence to write the documentation for a little chat SDK I created for my own apps.&lt;/p&gt;
&lt;p&gt;The main entry point into this SDK is a view called &lt;code&gt;ConversationView&lt;/code&gt;. Coding Intelligence produces the following documentation for it, which actually reflects the usage of this view really well.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;As you probably expected, Xcode sent the system instructions and the following user prompt to the LLM:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The user is currently inside this file: ConversationView.swift
The contents are below:
```swift:ConversationView.swift
(entire content of ConversationView.swift ```

The user has no code selected.

The user has asked:

Provide documentation for `ConversationView`.
    - Respond with a single code block.  
    - Only include documentation comments. No other Swift code.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No surprises here. The only thing I find worth calling out is that the user prompt repeats that the model should not create any additional Swift code, even though the system instructions mention that the model should only generate code if the user asks for it.&lt;/p&gt;
&lt;h3&gt;Generating new code&lt;/h3&gt;
&lt;p&gt;To finish our little tour of Xcode&apos;s new Coding Intelligence feature, let&apos;s vibe code a little chat app that uses the &lt;code&gt;ConversationKit&lt;/code&gt; package.&lt;/p&gt;
&lt;p&gt;To set this up, I created a basic SwiftUI project, naming it &lt;code&gt;HybridAIChat&lt;/code&gt;, and added &lt;code&gt;ConversationKit&lt;/code&gt; as a dependency.&lt;/p&gt;
&lt;p&gt;To create the initial chat UI, Let&apos;s try the following prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please implement a chat UI using the ConversationKit package.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Looking at the screenshot, you can see that Coding Intelligence understands that ConversationKit is a dependency of the project, and then uses the search tool to find out more about the API of ConversationKit:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Once it has enough context, it generates the code for a simple chat UI, with a simple echo implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ContentView: View {
  @State private var messages: [Message] = [
    .init(content: &quot;Hello! How can I help you today?&quot;, participant: .other)
  ]

  var body: some View {
    NavigationStack {
      ConversationView(messages: $messages)
        .navigationTitle(&quot;Hybrid AI Chat&quot;)
        .navigationBarTitleDisplayMode(.inline)
        .onSendMessage { message in
          let content = message.content ?? &quot;(no content)&quot;
          let response = Message(
            content: &quot;You said: \&quot;\(content)\&quot;&quot;,
            participant: .other
          )
          messages.append(response)
        }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s take a look at the user prompt Xcode uses for generating code. At this point, you can probably guess how it looks like...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The user is currently inside this file: ContentView.swift
The contents are below:
```swift:ContentView.swift
(entire content of ContentView.swift follows)
```
The user has no code selected.

The user has asked:

Please implement a chat UI using the ConversationKit package. // [!code highlight]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the screenshot above, you can see that Coding Intelligence and Gemini then take a couple of turns to search the code (for example, the source code for &lt;code&gt;ConversationView&lt;/code&gt;) that&apos;s necessary for the model to understand the API of the ConversationKit SDK, and ultimately implement the chat UI.&lt;/p&gt;
&lt;p&gt;The generated code works just fine (it never ceases to amaze me that we take this for granted now...):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now, there&apos;s really no excuse left for me to open source &lt;a href=&quot;https://github.com/peterfriese/ConversationKit&quot;&gt;ConversationKit&lt;/a&gt;, I guess...&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you&apos;ve ever wondered how code-editing agents work, and what kind of secret sauce is behind the seeming magic of generating code, resolving compile errors, or even running the app to verify if the implementation works as specified - it&apos;s mostly a more or less elaborate system prompt, a relatively straightforward loop, and an LLM. Oh, and a lot of attention to detail to make it look and work smoothly.&lt;/p&gt;
&lt;p&gt;In this blog post, you saw that an elaborate system prompt, combined with a code search tool and a bunch of relatively compact user prompts is enough to yield good results. If you&apos;re curious to learn more about building code-editing agents, I recommend &lt;a href=&quot;https://ampcode.com/how-to-build-an-agent&quot;&gt;How to Build an Agent (or: The Emperor Has No Clothes)&lt;/a&gt; - it walks you through the process of building a code-editing agent from scratch.&lt;/p&gt;
</content:encoded></item><item><title>Extracting structured data from PDFs using Gemini 2.0 and Genkit</title><link>https://peterfriese.dev/blog/2025/gemini-genkit-pdf-structured-data/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2025/gemini-genkit-pdf-structured-data/</guid><description>Learn how to use Gemini 2.0 and Genkit for Node.js to efficiently extract structured data from PDFs</description><pubDate>Sun, 16 Feb 2025 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;&lt;/p&gt;
&lt;p&gt;Last week, Google &lt;a href=&quot;https://developers.googleblog.com/en/gemini-2-family-expands/&quot;&gt;released Gemini 2.0&lt;/a&gt;, and after reading this blog post about &lt;a href=&quot;https://www.philschmid.de/gemini-pdf-to-data&quot;&gt;parsing PDFs using Gemini 2.0&lt;/a&gt;, I decided to rebuild the sample using &lt;a href=&quot;https://firebase.google.com/docs/genkit&quot;&gt;Genkit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For those who are unfamiliar, Genkit is an AI integration framework that makes implementing AI features for your apps easier.&lt;/p&gt;
&lt;p&gt;In this blog post, I am going to focus on Genkit for Node.js, but the team is also working on building support for Go (&lt;a href=&quot;https://firebase.google.com/docs/genkit-go/get-started-go&quot;&gt;available in alpha&lt;/a&gt;) and Python (&lt;a href=&quot;https://github.com/firebase/genkit/tree/main/py&quot;&gt;under development&lt;/a&gt;). This means you can use Genkit on any runtime that supports one of those three languages. The sample app I&apos;m discussing in this blog post is a simple command-line utility that you can run on macOS or any other platform that supports Node.js, but you can also use Genkit on the backend (for example in &lt;a href=&quot;https://firebase.google.com/docs/functions&quot;&gt;Cloud Functions for Firebase&lt;/a&gt;, &lt;a href=&quot;https://cloud.google.com/run&quot;&gt;Cloud Run&lt;/a&gt;, or even - gasp - AWS!&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;Before you can use Gemini 2.0 Flash in Genkit, you need to install and add Genkit and the Google AI SDK plugin for Genkit.&lt;/p&gt;
&lt;p&gt;You can then instantiate Genkit (1) and define a default model (2). This model will then be used in all subsequent calls you make via the &lt;code&gt;ai&lt;/code&gt; instance.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { gemini20Flash, googleAI } from &apos;@genkit-ai/googleai&apos;;
import { genkit, z } from &apos;genkit/beta&apos;;

// instantiate Genkit
const ai = genkit({ //(1)
  plugins: [googleAI()], 
  model: gemini20Flash, //(2)
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a detailed walkthrough of the setup process, check out this video:&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://www.youtube.com/watch?v=3p1P5grjXIQ&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Extracting structured data&lt;/h2&gt;
&lt;p&gt;In this first example, let&apos;s look at how you can use Gemini 2.0 Flash and Genkit to extract structured data from PDFs. Let&apos;s imagine you&apos;ve received a PDF with an invoice. To further process the invoice, you need to extract the invoice number, date, all the line items (including description, amount, and their individual price), as well as the gross price of the invoice.&lt;/p&gt;
&lt;p&gt;Thanks to Gemini&apos;s &lt;a href=&quot;https://ai.google.dev/gemini-api/docs/vision?lang=node&quot;&gt;vision capabilities&lt;/a&gt;, we can let it handle the OCR for us. The following code snippet shows how to call &lt;em&gt;any LLM&lt;/em&gt; via Genkit&apos;s unified generation API, and pass a multimodal prompt (including a URL to the PDF file we want to analyse as well as an instruction).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function analyseInvoice(url: string) {
  const { output } = await ai.generate({
    prompt: [
      { media: { url } },
      { text: &quot;Extract the structured data from the following PDF file&quot; }
    ]
  });
  return output;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;d call this method now for example by passing &lt;a href=&quot;https://storage.googleapis.com/generativeai-downloads/data/pdf_structured_outputs/invoice.pdf&quot;&gt;this URL&lt;/a&gt; (which points to an invoice), you&apos;d get the following result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
  {
    &apos;Invoice no&apos;: &apos;27301261&apos;,
    &apos;Date of issue&apos;: &apos;10/09/2012&apos;,
    Seller: {
      Name: &apos;Williams LLC&apos;,
      Address: &apos;72074 Taylor Plains Suite 342\nWest Alexandria, AR 97978&apos;,
      &apos;Tax Id&apos;: &apos;922-88-2832&apos;,
      IBAN: &apos;GB70FTNR64199348221780&apos;
    },
    Client: {
      Name: &apos;Hernandez-Anderson&apos;,
      Address: &apos;084 Carter Lane Apt. 846\nSouth Ronaldbury, AZ 91030&apos;,
      &apos;Tax Id&apos;: &apos;959-74-5868&apos;
    },
    Items: [
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object]
    ],
    Summary: {
      &apos;VAT [%]&apos;: &apos;10%&apos;,
      &apos;Net worth&apos;: &apos;494,96&apos;,
      VAT: &apos;49,50&apos;,
      &apos;Gross worth&apos;: &apos;544,46&apos;,
      Total: &apos;$ 494,96&apos;,
      &apos;Total VAT&apos;: &apos;$ 49,50&apos;,
      &apos;Total Gross worth&apos;: &apos;$ 544,46&apos;
    }
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Which is pretty impressive - after all, the model was able to correctly extract all information. However, this might not be in the format you need this in.&lt;/p&gt;
&lt;p&gt;This is where &lt;a href=&quot;https://firebase.google.com/docs/genkit/models#structured-output&quot;&gt;Genkit&apos;s support for structured output&lt;/a&gt; comes in handy. You can define a schema for your data using &lt;a href=&quot;https://zod.dev/&quot;&gt;Zod&lt;/a&gt;, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const LineItemSchema = z.object({
	description: z.string(),
	quantity: z.number(),
	grossWorth: z.number(),
});

const InvoiceSchema = z.object({
	invoiceNumber: z.string(),
	date: z.string().describe(&quot;The invoice date&quot;),
	items: z.array(LineItemSchema),
	totalGrossWorth: z.number()
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, pass the schema to Genkit&apos;s &lt;code&gt;generate&lt;/code&gt; call to enforce this schema.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function analyseInvoice(url: string) {
  const { output } = await ai.generate({
    prompt: [
      { media: { url } },
      { text: &quot;Extract the structured data from the following PDF file&quot; }
    ],
    output: { // [!code ++]
      schema: InvoiceSchema // [!code ++]
    } // [!code ++]
  });
  return output;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now, calling the function with the same PDF file will yield the following result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  date: &apos;10/09/2012&apos;,
  invoiceNumber: &apos;27301261&apos;,
  items: [
    {
      description: &apos;Lilly Pulitzer dress Size 2&apos;,
      grossWorth: 247.5,
      quantity: 5
    },
    {
      description: &apos;New ERIN Erin Fertherston Straight Dress White Sequence Lining Sleeveless SZ 10&apos;,
      grossWorth: 65.99,
      quantity: 1
    },
    {
      description: &apos;Sequence dress Size Small&apos;,
      grossWorth: 115.5,
      quantity: 3
    },
    {
      description: &apos;fire los angeles dress Medium&apos;,
      grossWorth: 21.45,
      quantity: 3
    },
    {
      description: &quot;Eileen Fisher Women&apos;s Long Sleeve Fleece Lined Front Pockets Dress XS Gray&quot;,
      grossWorth: 52.77,
      quantity: 3
    },
    {
      description: &apos;Lularoe Nicole Dress Size Small Light Solid Grey/ White Ringer Tee Trim&apos;,
      grossWorth: 8.25,
      quantity: 2
    },
    {
      description: &apos;J.Crew Collection Black &amp;amp; White sweater Dress sz S&apos;,
      grossWorth: 33,
      quantity: 1
    }
  ],
  totalGrossWorth: 544.46
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this adheres to the schema we provided, it will be much easier to process.&lt;/p&gt;
&lt;p&gt;Gemini 2.0 Flash supports structured output, but even if you&apos;re using an LLM that doesn&apos;t support natively structured output, Genkit can help by augmenting the prompt and coercing the returned data for you. What&apos;s really amazing about this: you won&apos;t have to change your code, thanks to Genkit&apos;s unified generation API (check out &lt;a href=&quot;https://bsky.app/profile/peterfriese.dev/post/3li5dz6evvc2n&quot;&gt;this thread on Bluesky&lt;/a&gt; in which I demonstrate this).&lt;/p&gt;
&lt;h2&gt;Parsing hand-written form input&lt;/h2&gt;
&lt;p&gt;I mentioned Gemini&apos;s OCR capabilities, so let&apos;s try extracting the hand-written entries in the following PDF:&lt;/p&gt;
&lt;p&gt;{/* &amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt; &lt;em&gt;/}

{/&lt;/em&gt; &amp;lt;/aside&amp;gt; */}&lt;/p&gt;
&lt;p&gt;One way to achieve this would be to define a new schema and then call Genkit&apos;s &lt;code&gt;generate&lt;/code&gt; method using this new schema. But there is a more flexible way to do this. It turns out that the actual code for calling Gemini to extract data from a PDF is always the same - only the output format (and potentially the instructions) change for each type of PDF we want to analyse.&lt;/p&gt;
&lt;p&gt;So why not make this a bit more generic?&lt;/p&gt;
&lt;h3&gt;Managing prompts&lt;/h3&gt;
&lt;p&gt;Genkit provides a way to extract prompts into external files, using the &lt;a href=&quot;https://firebase.google.com/docs/genkit/dotprompt&quot;&gt;Dotprompt&lt;/a&gt; library. This allows you to keep your code free from any prompt, making it easier to change the prompt independently of the code.&lt;/p&gt;
&lt;p&gt;Let&apos;s first define a prompt file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
model: googleai/gemini-2.0-flash
input:
  schema:
    pdfUrl: string
  default:
    pdfUrl: &quot;https://storage.googleapis.com/generativeai-downloads/data/pdf_structured_outputs/handwriting_form.pdf&quot;
output:
  schema:
    formNumber: string, Handwritten Form Number
    startDate: string, Effective Date of the plan
    beginningOfYear: integer, The plan liabilities beginning of the year
    endOfYear: integer, The plan liabilities end of the year
    name: string, Name of the plan
    address: string, Mailing address
    phoneNumber: string, Employer&apos;s phone number
    ein: string, Employer Identification Number
---
Extract the hand-written form data.

{{media url=pdfUrl}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prompt file defines the model Genkit should use (Gemini 2.0 Flash), as well as the prompt itself. But it also specifies the input &lt;em&gt;and&lt;/em&gt; output schemas for the prompt. For example, under &lt;code&gt;input/schema&lt;/code&gt;, we define an input parameter named &lt;code&gt;pdfUrl&lt;/code&gt; of type &lt;code&gt;string&lt;/code&gt;, which can then be used in the prompt to pass the URL. Likewise, under &lt;code&gt;output/schema&lt;/code&gt;, we define all the keys we want to extract from the PDF file, including their types. Note that the descriptions of the keys do not just serve the purpose of documentation - Genkit will use them in the prompt to specifically tell the model how to find the data for the respective keys.&lt;/p&gt;
&lt;h3&gt;Prompting the model&lt;/h3&gt;
&lt;p&gt;To load a prompt from a file, use &lt;code&gt;ai.prompt(filename)&lt;/code&gt; (1). This has a beneficial side effect: we can now create &lt;code&gt;.prompt&lt;/code&gt; files for all the different types of data we want to extract from PDF files, and just pass the filename to the &lt;code&gt;extract&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function extract(promptName: string, pdfUrl: string) {
  const prompt = ai.prompt(promptName); //(1)
  const { output } = await prompt({ pdfUrl }); //(2)
  return output;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of using &lt;code&gt;ai.generate()&lt;/code&gt; to call the LLM, you can execute a prompt by caling the prompt itself, and passing the required parameters (2).&lt;/p&gt;
&lt;p&gt;Calling this method using &lt;code&gt;extract(&quot;form&quot;, &quot;https://storage.googleapis.com/generativeai-downloads/data/pdf_structured_outputs/handwriting_form.pdf&quot;)&lt;/code&gt; will yield the following result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  address: &apos;235, Park Street Avenue, FL&apos;,
  beginningOfYear: 40000,
  ein: &apos;13626819&apos;,
  endOfYear: 55000,
  formNumber: &apos;CA530082&apos;,
  name: &apos;Annual Return Plan&apos;,
  phoneNumber: &apos;0.11 536289&apos;,
  startDate: &apos;02/05/.2022&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which - if you ask me - is mind-boggling.&lt;/p&gt;
&lt;h2&gt;Where to go from here&lt;/h2&gt;
&lt;p&gt;To learn more about Genkit, check out the &lt;a href=&quot;https://firebase.google.com/docs/genkit&quot;&gt;documentation&lt;/a&gt;. Also, feel free to drop by my &lt;a href=&quot;https://www.youtube.com/playlist?list=PLsnLd2esiGRTmfGZcZMnEy6hkBHXBH_en&quot;&gt;weekly livestreams&lt;/a&gt; - I&apos;m currently working on implementing a Second Brain app, and I&apos;m using Genkit for some of the AI-powered features.&lt;/p&gt;
</content:encoded></item><item><title>Understanding SwiftUI Preferences</title><link>https://peterfriese.dev/blog/2025/swiftui-preferences-swift6/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2025/swiftui-preferences-swift6/</guid><description>Learn how to use SwiftUI Preferences to establish efficient parent-child view communication. This comprehensive guide explains what Preferences are, how they differ from other state management approaches, and demonstrates practical implementation with a form validation example. Includes Swift 6 compatibility tips and best practices for modern iOS development.</description><pubDate>Mon, 03 Feb 2025 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;import demoActionMenuStage1 from &apos;./swiftui-action-menu/demo-action-menu-stage1.mp4&apos;;
import demoActionMenuFinal from &apos;./swiftui-action-menu/demo-action-menu-final.mp4&apos;;&lt;/p&gt;
&lt;p&gt;When building SwiftUI views, you often need to establish communication between parent and child views.&lt;/p&gt;
&lt;p&gt;Most commonly, you will use &lt;code&gt;@State&lt;/code&gt; on the parent view and &lt;code&gt;@Binding&lt;/code&gt; on the child view to establish a two-way binding. This results in a tight coupling between the two views. In most cases, this isn&apos;t an issue, but it can lead to undesired consequences if you don&apos;t pay attention. For example, if you need to pass state from a parent view to a view that is nested deep down in the view hierarchy, you might (accidentally) end up handing this state down through each view in the hierarchy, an anti-pattern known as &lt;a href=&quot;https://react.dev/learn/passing-data-deeply-with-context&quot;&gt;prop drilling&lt;/a&gt;. In SwiftUI, we can use the &lt;code&gt;@Environment&lt;/code&gt; property wrapper to avoid prop drilling.&lt;/p&gt;
&lt;p&gt;All of these approaches are meant for a parent / child communication in which the parent owns the data that is being passed down - but what if you need to communicate from a child view to a parent?&lt;/p&gt;
&lt;p&gt;Enter Preferences.&lt;/p&gt;
&lt;h2&gt;What are Preferences in SwiftUI&lt;/h2&gt;
&lt;p&gt;You will be forgiven for thinking that SwiftUI&apos;s Preferences are about user settings or configuration options. After all, that is the meaning of the term “preferences” in most software contexts! However, in SwiftUI, Preferences are a mechanism for passing data up the view hierarchy - essentially allowing child views to communicate information back to their parent views.&lt;/p&gt;
&lt;p&gt;As the &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/preferences#Overview&quot;&gt;SwiftUI documentation&lt;/a&gt; says, “[w]hereas you use the environment to configure the subviews of a view, you use preferences to send configuration information from subviews toward their container.”&lt;/p&gt;
&lt;h2&gt;How are Preferences used in SwiftUI&lt;/h2&gt;
&lt;p&gt;A well-known example that I am pretty sure most of us have used before is the &lt;code&gt;navigationTitle&lt;/code&gt; view modifer. Did you ever wonder that it&apos;s curious you need to apply this view modifier on a &lt;em&gt;child&lt;/em&gt; view of the navigation stack, instead of the &lt;code&gt;NavigationStack&lt;/code&gt; itself?&lt;/p&gt;
&lt;p&gt;Here is how it works: when setting the &lt;code&gt;navigationTitle&lt;/code&gt; on a view, this view uses a &lt;code&gt;PreferenceKey&lt;/code&gt; to communicate this value (i.e. its preferred navigation title) up to the nearest &lt;code&gt;NavigationStack&lt;/code&gt;. If the view is a child of any other view that &lt;em&gt;also&lt;/em&gt; sets the &lt;code&gt;navigationTitle&lt;/code&gt;, the preference of the first view will be overruled by the preference of the higher up view.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NavigationStack {
  Text( &quot;Hello, World!&quot;)
    .navigationTitle(&quot;Welcome!&quot;) // [!code highlight]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;A lesser known example is the &lt;code&gt;preferredColorScheme&lt;/code&gt; view modifier - as the name suggests, it lets you specify a view&apos;s preferred color scheme. So even if the device is in dark mode, you can make sure a specific view is rendered in light mode.&lt;/p&gt;
&lt;p&gt;Two aspects about the &lt;code&gt;preferredColorScheme&lt;/code&gt; view modifier are worth noting: the underlying preference key is public, so you can either write &lt;code&gt;.preferredColorScheme(.dark)&lt;/code&gt;, or &lt;code&gt;.preference(key: PreferredColorSchemeKey.self, value: .dark)&lt;/code&gt;. Also, it supports hierarchical overrides, which means that the following UI will be rendered in light mode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;VStack {
  VStack {
    Text(&quot;Hello, World!?&quot;)
      .preferredColorScheme(.dark) // [!code highlight]
  }
}
.preferredColorScheme(.light) // [!code highlight]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;h2&gt;Using Preferences to build custom views&lt;/h2&gt;
&lt;p&gt;To understand how you can use SwiftUI&apos;s preferences in your own views, I&apos;m going to walk you through the implementation of a form validation I implemented for a login form. I&apos;ve simplified the code a bit for this blog post.&lt;/p&gt;
&lt;p&gt;The basic idea for the implementation is to perform a field-based validation (e.g., the user name must have more than 3 characters, and the password must have more than 8 characters). Any form-level errors will be shown in a red error label beneath the input field. In addition, we want to be able to display the total number of validation errors on the containing form.&lt;/p&gt;
&lt;p&gt;Here is an implementation for a text input field that supports both plain text and secure input:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ValidatedField: View {
  let title: String
  @Binding var text: String
  let validationRules: [ValidationRule]
  var isSecure: Bool = false

  private var validationMessage: String? {
    for rule in validationRules {
      if let message = rule.validate(text) {
        return message
      }
    }
    return nil
  }

  var body: some View {
    VStack(alignment: .leading, spacing: 4) {
      if isSecure {
        SecureField(title, text: $text)
          .textFieldStyle(.roundedBorder)
      }
      else {
        TextField(title, text: $text)
          .textFieldStyle(.roundedBorder)
      }

      if let message = validationMessage {
        Text(message)
          .foregroundColor(.red)
          .font(.caption)
      }
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation allows for a composable implementation of validation rules, which are defined as a protocol. Here is the protocol, and a validation rule for minimum input length:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protocol ValidationRule {
  func validate(_ value: String) -&amp;gt; String?
}

struct MinLengthRule: ValidationRule {
  let length: Int

  func validate(_ value: String) -&amp;gt; String? {
    if value.count &amp;lt; length &amp;amp;&amp;amp; !value.isEmpty {
      return &quot;Must be at least \(length) characters&quot;
    }
    return nil
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A login form that uses this custom SwiftUI view might look as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct SignupForm: View {
  @State private var username = &quot;&quot;
  @State private var password = &quot;&quot;
  @State private var validationMessages: [String] = []

  private var formStatusMessage: AttributedString? {
    let errorCount = validationMessages.count
    guard errorCount &amp;gt; 0 else { return nil }

    return AttributedString(
      localized: &quot;^[There is](agreeWithArgument: \(errorCount)) ^[\(errorCount) error](inflect: true)&quot;
    )
  }

  var body: some View {
    VStack(spacing: 20) {
      if let status = formStatusMessage {
        Text(status)
          .foregroundColor(.red)
      }

      ValidatedField(
        title: &quot;Username&quot;,
        text: $username,
        validationRules: [MinLengthRule(length: 3)]
      )

      ValidatedField(
        title: &quot;Password&quot;,
        text: $password,
        validationRules: [
          MinLengthRule(length: 8),
          PasswordComplexityRule()
        ],
        isSecure: true
      )
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;As you can see, I&apos;ve added an error label for displaying the number of validation errors. In order to populate this label, the &lt;code&gt;SignUpForm&lt;/code&gt; needs to know about all the validation errors that occurred in the child views.&lt;/p&gt;
&lt;p&gt;One way to implement this would be to pass the &lt;code&gt;validationMessages&lt;/code&gt; state property down to all the child views (either via the environment, or as a binding), but that would result in tight coupling.&lt;/p&gt;
&lt;p&gt;Instead, let&apos;s use SwiftUI&apos;s preferences.&lt;/p&gt;
&lt;p&gt;First, we need to define a custom preference key:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ValidationMessageKey: PreferenceKey {
  static var defaultValue: [String] = []

  static func reduce(
    value: inout [String],
    nextValue: () -&amp;gt; [String])
  {
    value.append(contentsOf: nextValue())
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;ValidatedField&lt;/code&gt;, we can now pass the local &lt;code&gt;validationMessage&lt;/code&gt; up the view hierarchy using the &lt;code&gt;preference(key:value:)&lt;/code&gt; view modifier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ValidatedField: View {
  // ...
  private var validationMessage: String? {
    for rule in validationRules {
      if let message = rule.validate(text) {
        return message
      }
    }
    return nil
  }

  var body: some View {
    VStack(alignment: .leading, spacing: 4) {
      // ...
    }
    .preference( // [!code highlight]
      key: ValidationMessageKey.self, // [!code highlight]
      value: validationMessage.map { [$0] } ?? [] // [!code highlight]
    ) // [!code highlight]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;SignupForm&lt;/code&gt;, we can capture the final value of the &lt;code&gt;ValidationMessageKey&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct SignupForm: View {
  // ...
  @State private var validationMessages: [String] = []

  private var formStatusMessage: AttributedString? {
    let errorCount = validationMessages.count
    guard errorCount &amp;gt; 0 else { return nil }

    return AttributedString(
      localized: &quot;^[There is](agreeWithArgument: \(errorCount)) ^[\(errorCount) error](inflect: true)&quot;
    )
  }

  var body: some View {
    VStack(spacing: 20) {
      if let status = formStatusMessage {
        Text(status)
          .foregroundColor(.red)
      }

      ValidatedField( /* ... */ )
      ValidatedField( /* ... */ )
    }
    .onPreferenceChange(ValidationMessageKey.self) { messages in // [!code highlight]
      validationMessages = messages // [!code highlight]
    } // [!code highlight]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We successfully avoided a tight coupling between the &lt;code&gt;SignupForm&lt;/code&gt; and &lt;code&gt;ValidatedField&lt;/code&gt;, allowing for a flexible and elegant way for child views to communicate with their parent views.&lt;/p&gt;
&lt;h2&gt;Swift 6 issues&lt;/h2&gt;
&lt;p&gt;If you try using this code in a project that has Swift 6 langugage mode enabled, you will get two compile errors.&lt;/p&gt;
&lt;p&gt;First, the compiler complains about &lt;code&gt;defaultValue&lt;/code&gt; being a &lt;code&gt;var&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Static property &apos;defaultValue&apos; is not concurrency-safe because it is nonisolated global shared mutable state.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This can be resolved by changing &lt;code&gt;defaultValue&lt;/code&gt; to a &lt;code&gt;let&lt;/code&gt; constant, as suggested by the quick fix - after all, the default value will never change!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ValidationMessageKey: PreferenceKey {
  static let defaultValue: [String] = [] // [!code highlight]

  static func reduce(
    value: inout [String],
    nextValue: () -&amp;gt; [String])
  {
    value.append(contentsOf: nextValue())
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second compile error occurs on the &lt;code&gt;perform&lt;/code&gt; closure of &lt;code&gt;onPreferenceChange&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Main actor-isolated property &apos;validationMessages&apos; can not be mutated from a Sendable closure&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are two ways to deal with this partcicular instance of this well-known friend of ours.&lt;/p&gt;
&lt;p&gt;The typical way to solve this is to execute the body of the closure on the main actor, for example like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.onPreferenceChange(ValidationMessageKey.self) { messages in
  Task { @MainActor in // [!code highlight]
    validationMessages = messages // [!code highlight]
  } // [!code highlight]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, there is another way: instead of potentially hopping to the main actor,
we can use the fact that SwiftUI bindings are sendable, and pull in the &lt;code&gt;validationMessages&lt;/code&gt;
binding via the closure&apos;s capture list, and then assigning the &lt;code&gt;messages&lt;/code&gt; value to the
wrapped value of the binding:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.onPreferenceChange(ValidationMessageKey.self)
{ [binding = $validationMessages] messages in
  binding.wrappedValue = messages // [!code highlight]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which one of these you use is mostly a matter of preference (no pun intended), they&apos;re both Swift 6 compatible.&lt;/p&gt;
&lt;h2&gt;A tale of two closures&lt;/h2&gt;
&lt;p&gt;Now, maybe you&apos;re wondering just like me “why did I get this compiler error in the first place”? After all, the &lt;code&gt;action&lt;/code&gt; closure on a &lt;code&gt;Button&lt;/code&gt; doesn&apos;t seem to have this issue!&lt;/p&gt;
&lt;p&gt;The following code works perfectly fine iin Swift 6 language mode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ButtonClosure: View {
  @State private var counter = 0

  var body: some View {
    Text(&quot;\(counter) times&quot;)

    Button(&quot;Tap me&quot;) {
      counter += 1 // &amp;lt;-- no compiler error // [!code highlight]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason is that button closures - as many other UI-related closures are executed on the main actor in SwiftUI, which we can see by looking at the initialiser&apos;s signature:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Button where Label == Text {
  @preconcurrency nonisolated public init(
    _ titleKey: LocalizedStringKey,
    action: @escaping @MainActor () -&amp;gt; Void)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can assume that Apple decided to implement UI-related closures this way to make common UI interactions both safe and intuitive.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;action&lt;/code&gt; closure on &lt;code&gt;onPreferenceChange&lt;/code&gt;, on the other hand, is not marked as &lt;code&gt;@MainActor&lt;/code&gt;, and thus will not necessarily execute on the main actor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
  @preconcurrency @inlinable nonisolated public func
    onPreferenceChange&amp;lt;K&amp;gt;(
      _ key: K.Type = K.self,
      perform action: @escaping @Sendable (K.Value) -&amp;gt; Void) // [!code highlight]
      -&amp;gt; some View where K : PreferenceKey, K.Value : Equatable
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can only speculate why Apple implemented it this way, and I&apos;d be curious to hear your thoughts about it.&lt;/p&gt;
&lt;p&gt;The fact that this method signature is marked with &lt;code&gt;@preconcurrency&lt;/code&gt; indicates that Apple didn&apos;t somehow forget to review this part of SwiftUI when they made SwifUI ready for Swift 6. &lt;a href=&quot;https://gist.github.com/ryanlintott/54f94bb45afebf824723847154195b75?permalink_comment_id=5395887#gistcomment-5395887&quot;&gt;This comment&lt;/a&gt; also seems to indicate that this is &quot;working as intended”.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In most cases when building SwiftUI views, you will pass data from parent views to child views. However, sometimes, it&apos;s required to pass data about the inner state up to a parent view. SwiftUI preferences provide a way to achieve this without tight coupling. It&apos;s good to know they exist as a tool when you need a child / parent communication without requiring to pass a binding from a parent view.&lt;/p&gt;
</content:encoded></item><item><title>Creating a reusable action menu component in SwiftUI</title><link>https://peterfriese.dev/blog/2025/swiftui-action-menu/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2025/swiftui-action-menu/</guid><description>Learn how to create a reusable SwiftUI action menu from scratch. This tutorial shows how to transform a basic sheet-based menu into a polished, reusable component using view builders, custom modifiers, and SwiftUI styles. Includes complete code examples and best practices.</description><pubDate>Mon, 20 Jan 2025 15:30:00 GMT</pubDate><content:encoded>&lt;p&gt;import demoActionMenuStage1 from &apos;./swiftui-action-menu/demo-action-menu-stage1.mp4&apos;;
import demoActionMenuFinal from &apos;./swiftui-action-menu/demo-action-menu-final.mp4&apos;;&lt;/p&gt;
&lt;p&gt;SwiftUI has revolutionised how developers build user interfaces for Apple platforms - its fluent API make creating UIs more approachable and efficient. However, you can often end up with code that is repetitive, tightly coupled to the app’s data model, and generally not well structured.&lt;/p&gt;
&lt;p&gt;In this post, I will walk you through the process of building a reusable SwiftUI component that you can seamlessly integrate in your own SwiftUI apps.&lt;/p&gt;
&lt;p&gt;For an app I am currently working on , I needed an action menu similar to the one in Apple’s Mail app. Building a view like this is relatively straightforward in SwiftUI - it mostly consists of a couple of &lt;code&gt;Buttons&lt;/code&gt; that are embedded in a &lt;code&gt;List&lt;/code&gt; view that is presented on a sheet.&lt;/p&gt;
&lt;p&gt;Here is a simplified version of the code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct ContentView: View {
  @State private var fruits = [&quot;Apple&quot;, &quot;Banana&quot;, &quot;Orange&quot;, &quot;Mango&quot;, &quot;Pear&quot;, &quot;Grape&quot;, &quot;Pineapple&quot;, &quot;Strawberry&quot;]
  @State private var isMoreActionTapped = false
  @State private var selectedFruit: String? = nil

  var body: some View {
    NavigationStack {
      List(fruits, id: \.self) { fruit in
        Text(fruit)
          .swipeActions(edge: .trailing, allowsFullSwipe: true) {
            Button(&quot;Delete&quot;, systemImage: &quot;trash&quot;, role: .destructive) {
              // action
            }
            Button(&quot;More&quot;, systemImage: &quot;ellipsis.circle&quot;) {
              selectedFruit = fruit
              isMoreActionTapped.toggle()
            }
            .tint(.gray)
          }
      }
      .navigationTitle(&quot;Fruits&quot;)
      .sheet(isPresented: $isMoreActionTapped) {
        NavigationStack {
          List {
            Section(&quot;Text Options&quot;) {
              Button(&quot;Uppercase&quot;, systemImage: &quot;characters.uppercase&quot;) {
                // action
              }

              Button(&quot;Lowercase&quot;, systemImage: &quot;characters.lowercase&quot;) {
                // action
              }
            }

            Section {
              Button(&quot;Delete Item&quot;, systemImage: &quot;trash&quot;, role: .destructive) {
                // action
              }
            }
          }
          .navigationTitle(&quot;Actions&quot;)
          .navigationBarTitleDisplayMode(.inline)
          .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
              Button(&quot;Done&quot;) {
                isMoreActionTapped = false
              }
            }
          }
        }
        .presentationDetents([.medium, .large])
      }
    }
  }
}

#Preview {
  ContentView()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demoActionMenuStage1} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;This works just fine, but there are a couple of issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All of the code lives in the main view and takes up a lot of space. Just imagine how many lines of code this will occupy if we add more actions!&lt;/li&gt;
&lt;li&gt;The code for the action menu is tightly coupled to the main view - what if we want to implement a similar action menu on a different view? We’d have to copy and paste a lot of the code. If we make changes to the code in one place, we’d have to manually update it in all other places - a maintenance nightmare.&lt;/li&gt;
&lt;li&gt;The styling doesn’t match the look and feel of the action menu in Apple’s Mail.app (with the label on the left, and the icon on the right).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;In short - this is not reusable!&lt;/p&gt;
&lt;p&gt;In the following, I will show you how to turn this prototype into a reusable SwiftUI component that you can easily import and use in your own applications. Along the way, you will learn how to apply the techniques I teach in my video series &lt;a href=&quot;https://www.youtube.com/playlist?list=PLsnLd2esiGRTfzn8pq4ZMYyDsL8GEMZO8&quot;&gt;Building Reusable SwiftUI Components&lt;/a&gt;. By the end of this post, you should have enough knowledge to start building and shipping your own reusable SwiftUI components.&lt;/p&gt;
&lt;p&gt;Let’s get started!&lt;/p&gt;
&lt;h2&gt;Creating a custom view for the action menu&lt;/h2&gt;
&lt;p&gt;The first step required to turn the initial, non-reusable code snippet into a reusable component is to extract the code for displaying the menu items into a separate view.&lt;/p&gt;
&lt;p&gt;The easiest way to achieve this is to create a new file using the SwiftUI view template, naming it &lt;code&gt;ActionMenu&lt;/code&gt;, and then cut / paste the &lt;code&gt;NavigationStack&lt;/code&gt; and all its containing code into the &lt;code&gt;body&lt;/code&gt; of this new view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct ActionMenu: View {
  let title: String = &quot;Actions&quot;
  @Binding var selectedFruit: String?
  @Binding var fruits: [String]
  @Environment(\.dismiss) private var dismiss

  var body: some View {
    NavigationStack {
      List {
        Section(&quot;Text Options&quot;) {
          Button(&quot;Uppercase&quot;, systemImage: &quot;characters.uppercase&quot;) {
            // action
            dismiss()
          }

          Button(&quot;Lowercase&quot;, systemImage: &quot;characters.lowercase&quot;) {
            // action
            dismiss()
          }
        }

        Section {
          Button(&quot;Delete Item&quot;, systemImage: &quot;trash&quot;, role: .destructive) {
            // action
            dismiss()
          }
        }
      }
      .navigationTitle(title)
      .navigationBarTitleDisplayMode(.inline)
      .toolbar {
        ToolbarItem(placement: .topBarTrailing) {
          Button(&quot;Done&quot;) {
            dismiss()
          }
        }
      }
    }
  }
}

#Preview {
  @Previewable @State var fruits = [&quot;Apple&quot;, &quot;Banana&quot;, &quot;Orange&quot;, &quot;Mango&quot;, &quot;Pear&quot;, &quot;Grape&quot;, &quot;Pineapple&quot;, &quot;Strawberry&quot;]
  @Previewable @State var isMoreActionTapped = false
  @Previewable @State var selectedFruit: String? = nil

  ActionMenu(
    selectedFruit: $selectedFruit,
    fruits: $fruits
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that this requires us to pass the array of &lt;code&gt;fruits&lt;/code&gt; and the selected fruit item (i.e., the one the action menu will act on) to &lt;code&gt;ActionMenu&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The call site looks a lot (c)leaner now which a good step in the right direction. However, &lt;code&gt;ActionMenu&lt;/code&gt; is tightly coupled to the app&apos;s data model now, preventing us from reusing it in other contexts.&lt;/p&gt;
&lt;h3&gt;Refactoring &lt;code&gt;ActionMenu&lt;/code&gt; for reuse&lt;/h3&gt;
&lt;p&gt;To make &lt;code&gt;ActionMenu&lt;/code&gt; reusable, we will first introduce a view builder. View builders are one of the key features that power SwiftUI’s declarative syntax, and they are used extensively in SwiftUI’s core components like &lt;code&gt;VStack&lt;/code&gt;, &lt;code&gt;HStack&lt;/code&gt;, &lt;code&gt;List&lt;/code&gt;, and others.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct ActionMenu&amp;lt;Content: View&amp;gt;: View {
  @Environment(\.dismiss) private var dismiss

  let title: String
  let content: () -&amp;gt; Content

  init(
    title: String = &quot;Actions&quot;,
    @ViewBuilder content: @escaping () -&amp;gt; Content
  ) {
    self.title = title
    self.content = content
  }

  var body: some View {
    NavigationStack {
      List {
        content()
      }
      .navigationTitle(title)
      .navigationBarTitleDisplayMode(.inline)
      .toolbar {
        ToolbarItem(placement: .topBarTrailing) {
          Button(&quot;Done&quot;) {
            dismiss()
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using a view builder, we can extract all the code we used for creating the contents of the action menu, and pass it in from the call site.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#Preview {
  @Previewable @State var fruits = [&quot;Apple&quot;, &quot;Banana&quot;, &quot;Orange&quot;, &quot;Mango&quot;, &quot;Pear&quot;, &quot;Grape&quot;, &quot;Pineapple&quot;, &quot;Strawberry&quot;]
  @Previewable @State var isMoreActionTapped = false
  @Previewable @State var selectedFruit: String? = nil

  ActionMenu { // [!code highlight]
    Section(&quot;Text Options&quot;) {
      Button(&quot;Uppercase&quot;, systemImage: &quot;characters.uppercase&quot;) {
        // action
        // dismiss() // Cannot find &apos;dismiss&apos; in scope
      }

      Button(&quot;Lowercase&quot;, systemImage: &quot;characters.lowercase&quot;) {
        // action
        // dismiss() // Cannot find &apos;dismiss&apos; in scope
      }
    }

    Section {
      Button(&quot;Delete Item&quot;, systemImage: &quot;trash&quot;, role: .destructive) {
        // action
        // dismiss() // Cannot find &apos;dismiss&apos; in scope
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This also means we can remove all references to any application specific types, such as the &lt;code&gt;fruits&lt;/code&gt; and &lt;code&gt;selectedFruit&lt;/code&gt; properties from the &lt;code&gt;ActionMenu&lt;/code&gt; type, since we will be dealing with them at the call site.&lt;/p&gt;
&lt;p&gt;This is great, as it makes &lt;code&gt;ActionMenu&lt;/code&gt; more reusable in other contexts.&lt;/p&gt;
&lt;p&gt;However, notice that we’re no longer able to call &lt;code&gt;dismiss()&lt;/code&gt; to dismiss the sheet. This is something we&apos;ll have to fix in a later step.&lt;/p&gt;
&lt;p&gt;You might be thinking “no problem, we can just bring back the &lt;code&gt;isMoreActionTapped&lt;/code&gt; state property we used in the beginning. That would definitely work, but there&apos;s a better way to implement the sheet dismissal. But before we get there, let&apos;s first improve the developer experience of setting up the action menu.&lt;/p&gt;
&lt;h3&gt;Creating a view modifier&lt;/h3&gt;
&lt;p&gt;At the moment, creating the action menu requires us to set up a sheet, and then instantiate the &lt;code&gt;ActionMenu&lt;/code&gt;, making the call site more busy than it should be. Wouldn’t it be nice to able to just write the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List { ... }
  .actionMenu(title: &quot;Actions&quot;, isPresented: $isMoreActionTapped) { // [!code highlight]
    // actions
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make this possible, we will introduce a custom view modifier. In SwiftUI, we use view modifiers like &lt;code&gt;.font&lt;/code&gt;, &lt;code&gt;.padding&lt;/code&gt;, &lt;code&gt;.foregroundColor&lt;/code&gt; and others all the time to specify the look and feel of our SwiftUI views.&lt;/p&gt;
&lt;p&gt;But you can also create custom view modifiers to encapsulate (or group) other view modifiers that you routinely use together. Creating the sheet with the enclosed action menu is such a case.&lt;/p&gt;
&lt;p&gt;View modifiers typically consist of two parts: firstly, a struct that conforms to the &lt;code&gt;ViewModifier&lt;/code&gt; protocol and contains the code you want to make reusable. Secondly, a convenience function that is defined as an extension on &lt;code&gt;View&lt;/code&gt;, allowing us to use view modifiers in a fluent way by chaining them together.&lt;/p&gt;
&lt;p&gt;Let’s first define &lt;code&gt;ActionMenuModifier&lt;/code&gt;, and encapsulate the sheet / action menu structure.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ActionMenuModifier&amp;lt;MenuContent: View&amp;gt;: ViewModifier {
  let title: String
  @Binding var isPresented: Bool
  let menuContent: () -&amp;gt; MenuContent

  init(title: String, isPresented: Binding&amp;lt;Bool&amp;gt;, @ViewBuilder  menuContent: @escaping () -&amp;gt; MenuContent) {
    self.title = title
    self._isPresented = isPresented
    self.menuContent = menuContent
  }

  func body(content: Content) -&amp;gt; some View {
    content
      .sheet(isPresented: $isPresented) { // [!code highlight]
        ActionMenu(title: title) { // [!code highlight]
          menuContent() // [!code highlight]
        } // [!code highlight]
        .presentationDetents([.medium, .large]) // [!code highlight]
      } // [!code highlight]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A couple of notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;content&lt;/code&gt; is the view that you apply the view modifier to&lt;/li&gt;
&lt;li&gt;&lt;code&gt;menuContent&lt;/code&gt; represents the sections and buttons making up the action menu. Notice how we&apos;re passing &lt;code&gt;menuContent&lt;/code&gt; through to the &lt;code&gt;ActionMenu&lt;/code&gt; view we defined earlier.&lt;/li&gt;
&lt;li&gt;Also notice that we’re using a view builder for the menu content, to match the view builder on the &lt;code&gt;ActionMenu&lt;/code&gt; view.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To apply a view modifier to a view, you can use the &lt;code&gt;modifier&lt;/code&gt; view modifier (yes, very meta, I know):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List { ... }
  .modifier( // [!code highlight]
    ActionMenuModifier(
      title: &quot;Actions&quot;,
      isPresented: $isMoreActionTapped,
      menuContent: {
        // actions
      }
    )
  )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think you will agree with me that approach feels clunky and doesn&apos;t quite achieve the elegant syntax we expect from SwiftUI&apos;s view modifiers.&lt;/p&gt;
&lt;p&gt;We can fix this by introducing an extension on &lt;code&gt;View&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension View {
  public func actionMenu(title: String, isPresented: Binding&amp;lt;Bool&amp;gt;, @ViewBuilder content: @escaping () -&amp;gt; some View) -&amp;gt; some View {
    modifier(
      ActionMenuModifier(
        title: title,
        isPresented: isPresented,
        menuContent: content
      )
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This extension allows us to apply the view modifier to any view in a fluent way by calling &lt;code&gt;actionMenu&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    List { }
      .actionMenu(title: &quot;Actions&quot;, isPresented: $isMoreActionTapped) { // [!code highlight]
        // actions
      }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach aligns much better with SwiftUI&apos;s API, offering a similar experience to built-in modifiers like &lt;code&gt;confirmationDialog&lt;/code&gt;, and ultimately provides a smoother developer experience.&lt;/p&gt;
&lt;h3&gt;Styling the action labels&lt;/h3&gt;
&lt;p&gt;In the introduction, I mentioned that I wanted to re-create the look and feel of the action menu in Apple’s mail app. If you compare the action menus in Apple’s mail app and the sample app in this blog post, you will notice one main differences: the icons in Apple’s mail app are on the right.&lt;/p&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Now, we could re-create this design by creating a custom label like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Button {
  // action handler
} label: {
  HStack(spacing: 22) {
    Text(&quot;Uppercase&quot;)
    Spacer()
    Image(systemName: &quot;characters.uppercase&quot;)
      .foregroundStyle(Color.accentColor)
      .font(.system(size: 22, weight: .light))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this would lead to a lot of code duplication.&lt;/p&gt;
&lt;p&gt;Instead, let’s implement a custom style that we can use inside the &lt;code&gt;ActionMenu&lt;/code&gt; view.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Label&lt;/code&gt;, like many other SwiftUI views supports view styling. This means you can customize the entire look and feel of the view by providing a custom style. To customize &lt;code&gt;Label&lt;/code&gt;, we need to implement a custom style that conforms to &lt;code&gt;LabelStyle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The main requirement of &lt;code&gt;LabelStyle&lt;/code&gt; is a &lt;code&gt;makeBody&lt;/code&gt; function, and by looking at the following code snippet, you will realise that this function is very similar to the &lt;code&gt;body&lt;/code&gt; function of a regular SwiftUI view - the only difference is that is has a &lt;code&gt;configuration&lt;/code&gt; parameter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct MenuLabelStyle: LabelStyle {
  @ScaledMetric(relativeTo: .body) private var iconSize: CGFloat = 22.0

  func makeBody(configuration: Configuration) -&amp;gt; some View {
    // view layout
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Configuration&lt;/code&gt; is a type that is specific to the view style, and contains the key components of the respective view. For example, here is the definition of &lt;code&gt;LabelStyleConfiguration&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// The properties of a label.
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public struct LabelStyleConfiguration {

    /// A type-erased title view of a label.
    @MainActor @preconcurrency public struct Title {

        /// The type of view representing the body of this view.
        ///
        /// When you create a custom view, Swift infers this type from your
        /// implementation of the required ``View/body-swift.property`` property.
        @available(iOS 14.0, tvOS 14.0, watchOS 7.0, macOS 11.0, *)
        public typealias Body = Never
    }

    /// A type-erased icon view of a label.
    @MainActor @preconcurrency public struct Icon {

        /// The type of view representing the body of this view.
        ///
        /// When you create a custom view, Swift infers this type from your
        /// implementation of the required ``View/body-swift.property`` property.
        @available(iOS 14.0, tvOS 14.0, watchOS 7.0, macOS 11.0, *)
        public typealias Body = Never
    }

    /// A description of the labeled item.
    public var title: LabelStyleConfiguration.Title { get } // [!code highlight]

    /// A symbolic representation of the labeled item.
    public var icon: LabelStyleConfiguration.Icon { get } // [!code highlight]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, this gives us access to the &lt;code&gt;title&lt;/code&gt; and the &lt;code&gt;icon&lt;/code&gt; being used to display the label, allowing us to implement &lt;code&gt;MenuLabelStyle&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct MenuLabelStyle: LabelStyle {
  @ScaledMetric(relativeTo: .body) private var iconSize: CGFloat = 22.0

  func makeBody(configuration: Configuration) -&amp;gt; some View {
    HStack(spacing: 22) {
      configuration.title
      Spacer()
      configuration.icon
        .foregroundStyle(Color.accentColor)
        .font(.system(size: iconSize, weight: .light))
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might notice we’re using the same layout we prototyped earlier when we considered styling the label of the &lt;code&gt;Button&lt;/code&gt;s manually. Also notice how we’re using &lt;code&gt;configuration.title&lt;/code&gt; and &lt;code&gt;configuration.icon&lt;/code&gt; to get access to those key elements of the &lt;code&gt;Label&lt;/code&gt; view.&lt;/p&gt;
&lt;p&gt;To make sure the icon scales proportionally to the title of the view when the user uses dynamic type, we define a scaled metric constant for the icon size.&lt;/p&gt;
&lt;p&gt;Finally, to make it more convenient to use this new style, let’s define a constant for this style:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension LabelStyle where Self == MenuLabelStyle {
  static var menu: MenuLabelStyle { .init() } // [!code highlight]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to instantiate &lt;code&gt;MenuLabelStyle&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Label(&quot;Hello world&quot;, systemImage: &quot;globe&quot;)
  .labelStyle(.menu) // [!code highlight]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The cool thing about SwiftUI styles is that they’re stored in the SwiftUI environment, and thus will be inherited to all views in the view hierarchy. This means we can apply this new view style to the &lt;code&gt;List&lt;/code&gt; view inside our &lt;code&gt;ActionMenu&lt;/code&gt; view, and it will be applied to all labels inside the list view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ActionMenu&amp;lt;Content: View&amp;gt;: View {
  @Environment(\.dismiss) private var dismiss

  let title: String
  let content: () -&amp;gt; Content

  init(
    title: String = &quot;Actions&quot;,
    @ViewBuilder content: @escaping () -&amp;gt; Content
  ) {
    self.title = title
    self.content = content
  }

  var body: some View {
    NavigationStack {
      List {
        content()
      }
      .labelStyle(.menu) // [!code highlight]
      .navigationTitle(title)
      .navigationBarTitleDisplayMode(.inline)
      .toolbar {
        ToolbarItem(placement: .topBarTrailing) {
          Button(&quot;Done&quot;) {
            dismiss()
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;h3&gt;Dismissing the sheet&lt;/h3&gt;
&lt;p&gt;We&apos;re almost there! There&apos;s just one missing piece: automatically dismissing the sheet after an action is tapped.&lt;/p&gt;
&lt;p&gt;To solve this, we can leverage the power of custom button styles in SwiftUI. Remember, custom styles don&apos;t just control the look of a view, but also its behavior!&lt;/p&gt;
&lt;p&gt;There are several styles for buttons: &lt;code&gt;DefaultButtonStyle&lt;/code&gt;, &lt;code&gt;BorderlessButtonStyle&lt;/code&gt;, &lt;code&gt;PlainButtonStyle&lt;/code&gt;, &lt;code&gt;BorderedButtonStyle&lt;/code&gt;, &lt;code&gt;BorderedButtonStyle&lt;/code&gt;, and &lt;code&gt;PrimitiveButtonStyle&lt;/code&gt;, but only two button style configurations: &lt;code&gt;PrimitiveButtonStyleConfiguration&lt;/code&gt; and &lt;code&gt;ButtonStyleConfiguration&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PrimitiveButtonStyleConfiguration&lt;/code&gt; provides access to the button&apos;s &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;label&lt;/code&gt;, and - most importantly - its &lt;code&gt;trigger&lt;/code&gt;, which is what we need to override the button&apos;s action behaviour.&lt;/p&gt;
&lt;p&gt;Here is an implementation of &lt;code&gt;ActionMenuButtonStyle&lt;/code&gt; that allows us to dismiss the sheet after the button trigger has been initiated:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ActionMenuButtonStyle: PrimitiveButtonStyle {
  @Environment(\.dismiss) private var dismiss

  func makeBody(configuration: Configuration) -&amp;gt; some View {
    configuration.label
      .contentShape(Rectangle()) // make entire view tappable
      .onTapGesture {
        configuration.trigger() // [!code highlight]
        dismiss()
      }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A couple of notes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We use &lt;code&gt;configuration.trigger()&lt;/code&gt; to call the buttons action handler.&lt;/li&gt;
&lt;li&gt;After that, we &lt;code&gt;dismiss&lt;/code&gt; the sheet.&lt;/li&gt;
&lt;li&gt;To make sure the button’s action is triggered even if the user taps inside the white space between the label and the icon, we set the content shape of the button’s label to &lt;code&gt;Rectangle&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Just like we did for the custom label style, let’s declare a function to make applying this new style more fluent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension PrimitiveButtonStyle where Self == ActionMenuButtonStyle {
  static var action: ActionMenuButtonStyle {
    ActionMenuButtonStyle()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we need to apply this new view modifier to the action menu:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ActionMenu&amp;lt;Content: View&amp;gt;: View {
  @Environment(\.dismiss) private var dismiss

  let title: String
  let content: () -&amp;gt; Content

  init(
    title: String = &quot;Actions&quot;,
    @ViewBuilder content: @escaping () -&amp;gt; Content
  ) {
    self.title = title
    self.content = content
  }

  var body: some View {
    NavigationStack {
      List {
        content()
      }
      .labelStyle(.menu)
      .buttonStyle(.action) // [!code highlight]
      .navigationTitle(title)
      .navigationBarTitleDisplayMode(.inline)
      .toolbar {
        ToolbarItem(placement: .topBarTrailing) {
          Button(&quot;Done&quot;) {
            dismiss()
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demoActionMenuFinal} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;That&apos;s it! Now our sheet will automatically close after an action is tapped.&lt;/p&gt;
&lt;h2&gt;That&apos;s a wrap!&lt;/h2&gt;
&lt;p&gt;We did it! We built a reusable action menu that looks and behaves just like the one in Apple Mail. 🎉&lt;/p&gt;
&lt;p&gt;Along the way, we explored some key SwiftUI techniques that you can apply to your own projects. Here&apos;s a quick recap of what we covered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prototyping and Reusability:&lt;/strong&gt; We saw how to efficiently prototype views and make them reusable throughout your app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom View Modifiers:&lt;/strong&gt; We learned how to create custom view modifiers that seamlessly integrate new views into the view hierarchy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View Builders:&lt;/strong&gt; We discovered how view builders can decouple your views from business logic, making them more flexible and reusable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SwiftUI Styles:&lt;/strong&gt; We explored how to encapsulate custom styling for your views using SwiftUI&apos;s style system.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Behavioral Customization with Styles:&lt;/strong&gt; We even saw how custom styles can be used to modify the behavior of views, like automatically dismissing our action sheet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hopefully, you found this tutorial helpful and can use these techniques to build even more awesome SwiftUI interfaces!&lt;/p&gt;
&lt;h2&gt;Where to go from here&lt;/h2&gt;
&lt;p&gt;Want to dive deeper into the world of reusable SwiftUI components? I&apos;ve got you covered!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Video Series:&lt;/strong&gt; Check out my YouTube series, &lt;a href=&quot;https://www.youtube.com/playlist?list=PLsnLd2esiGRTfzn8pq4ZMYyDsL8GEMZO8&quot;&gt;Building SwiftUI Components&lt;/a&gt;, for a comprehensive look at the fundamentals. It covers a lot of the basics we didn&apos;t have time for in this post.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conference Talks:&lt;/strong&gt; I&apos;ve also given talks on this topic at various conferences.
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/PocljzAYFL4?si=K_rUQ5iR8DRcG9iE&quot;&gt;Swift Heroes&lt;/a&gt;: Learn how to build a text input field with a floating label.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/YjSxPxT5V40?si=OipzBQPBf9S3rPZD&quot;&gt;SwiftConf&lt;/a&gt;: See how to create a reusable avatar component.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive tutorial:&lt;/strong&gt; For an interactive, hands-on tutorial, check out the &lt;a href=&quot;/tutorials&quot;&gt;Tutorials&lt;/a&gt; page on my blog, which includes a tutorial that walks you through the process of building a reusable avatar component in SwiftUI.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bonus&lt;/h2&gt;
&lt;p&gt;As a bonus, I&apos;ve made the action menu component available as a &lt;strong&gt;ready-to-use component:&lt;/strong&gt; you can grab the code for this action menu component on GitHub and use it in your own projects! Just install it as an SPM package from this URL: &lt;a href=&quot;https://github.com/peterfriese/ActionMenu&quot;&gt;https://github.com/peterfriese/ActionMenu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;{/* In addition to the code discussed in this blog post, this package has a couple of extra features, such as support for destructive actions, and improved support for iPadOS. */}&lt;/p&gt;
</content:encoded></item><item><title>Creating custom SF Symbols using the SF Symbols app</title><link>https://peterfriese.dev/blog/2025/custom-sf-symbols/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2025/custom-sf-symbols/</guid><description>Learn how to create custom SF Symbols without any design skills, using the SF Symbols app.</description><pubDate>Thu, 09 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;SF Symbols are icons on steroids: they are vector-based (and thus can be losslesly scaled to arbitrary resolutions), they consist of multiple layers (which can be independenly coloured), and they can even be animated.&lt;/p&gt;
&lt;p&gt;They also just look really great, thanks to the hard work Apple’s designers put into creating them.&lt;/p&gt;
&lt;p&gt;There are literally thousands of them, so chances are that you will find the perfect fit for your needs. &lt;em&gt;Finding&lt;/em&gt; the right one is a whole other story - &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10288&quot;&gt;this WWDC video&lt;/a&gt; has a couple of nifty tips and tricks for searching and organising SF Symbols.&lt;/p&gt;
&lt;p&gt;Sometimes, however, you might not be able to find the perfect SF Symbol for your need. Thankfully, you can create your own SF Symbols. Apple provides templates for creating new SF Symbols using your favourite vector graphics tool, and there are plenty of articles that demonstrate how to do this using Figma, Sketch, or Adobe Illustrator.&lt;/p&gt;
&lt;p&gt;But in this article, I’m going to show you how you can create new symbols &lt;em&gt;without&lt;/em&gt; being a designer or having access to those tools.&lt;/p&gt;
&lt;h2&gt;Using SF Symbols to create SF Symbols&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In many cases, all we need is a variation of an existing SF Symbol, for example a document with a badge to indicate the status of the document (e.g. read / unread). Or, a striked through version of an icon to indicate a certain feature is disabled.&lt;/p&gt;
&lt;p&gt;The SF Symbols app includes a feature that allows us to create new SF Symbols by combining existing SF Symbols with a bunch of common adornments.&lt;/p&gt;
&lt;p&gt;Let’s look at an example. For an app I am working on, I needed a version of the &lt;code&gt;text.document&lt;/code&gt; SF Symbol with a badge to indicate its read / unread state. Now, there already are a number of variations of the &lt;code&gt;document&lt;/code&gt; symbol, but none of them had a notification indicator like the one for unread emails in Apple&apos;s stock mail app.&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;To create a new variation of the &lt;code&gt;text.document&lt;/code&gt; symbol, follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new custom symbol based on the base symbol: Find the base symbol, and then select &lt;em&gt;Duplicate as Custom Symbol&lt;/em&gt; from the context menu.&lt;/li&gt;
&lt;li&gt;Select the custom symbol and select &lt;em&gt;Combine Symbol with Component…&lt;/em&gt; from the context menu.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A dialog appears, showing a list of components from three categories: &lt;em&gt;Enclosures&lt;/em&gt; (such as circle, seal, bubble, rectangle stack, and more), different types of &lt;em&gt;badges&lt;/em&gt;, and &lt;em&gt;slash&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;In our example, pick &lt;code&gt;badge&lt;/code&gt; from the Badges section, and click on &lt;em&gt;Create Symbols&lt;/em&gt; (you can apply the same component to several source symbols at the same time. You can even apply several components to the same symbol(s) at the same time.)&lt;/li&gt;
&lt;li&gt;You can now adjust the individual aspects of your new custom symbol. In our example, let’s move the badge to the top right, as can be seen on &lt;code&gt;envelope.badge.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;To move the badge to the top right, set the &lt;em&gt;Y Offset&lt;/em&gt; of the badge to &lt;code&gt;10%&lt;/code&gt; to match the design of the &lt;code&gt;envelope.badge&lt;/code&gt; symbol.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Using your new custom symbol in Xcode&lt;/h2&gt;
&lt;p&gt;Before you can use your new custom symbol in your app, you need to export it from the SF Symbols app and import it into your Xcode project.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select your new symbol in the SF Symbols app.&lt;/li&gt;
&lt;li&gt;Click on &lt;em&gt;File &amp;gt; Export Symbol…&lt;/em&gt; (or press &lt;code&gt;⇧ ⌘ E&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Pick the correct Xcode version, and then click on &lt;em&gt;Export&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Save the resulting SVG file to a convenient location.&lt;/li&gt;
&lt;li&gt;Open your Xcode project, and select its asset catalog.&lt;/li&gt;
&lt;li&gt;Drag the SVG file into your asset catalog.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can now use your new custom SF Symbol in your app. Here are a couple of code snippets that demonstrate common use cases:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Image(&quot;textDocumentBadge&quot;)
Label(&quot;Unread&quot;, image: &quot;textDocumentBadge&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can even refer to your new SF Symbol using an image reference using &lt;code&gt;. textDocumentBadge&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Note that you cannot refer to custom SF Symbols using the &lt;code&gt;systemName:&lt;/code&gt; parameters (e.g. &lt;code&gt;Image(systemName: “textDocumentBadge”&lt;/code&gt;) .&lt;/p&gt;
&lt;h2&gt;Further reading&lt;/h2&gt;
&lt;p&gt;To learn more about SF Symbols and how to use them in your apps, I recommend reading the &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/sf-symbols&quot;&gt;SF Symbols section in the HIG&lt;/a&gt; - it provides a lot of useful backround information, including a section with considerations for custom symbols.&lt;/p&gt;
&lt;p&gt;If you’d like to learn how to edit SF Symbols in tools like Figma or Sketch, or how to create entirely new ones, I recommend the following resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.danijelavrzan.com/posts/2024/06/create-custom-sf-symbols/&quot;&gt;Danijela Vrzan: Create Custom SF Symbols in Sketch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10250&quot;&gt;WWDC 2021: Create custom symbols&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/creating-custom-symbol-images-for-your-app&quot;&gt;Apple Docs: Creating custom symbol images for your app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;The header image is based on &lt;a href=&quot;https://thenounproject.com/icon/san-francisco-2515778/&quot;&gt;San Francisco&lt;/a&gt; by Philipp Petzka from &lt;a href=&quot;https://thenounproject.com/browse/icons/term/san-francisco/&quot;&gt;Noun Project&lt;/a&gt; (CC BY 3.0)&lt;/p&gt;
</content:encoded></item><item><title>Improve your app&apos;s UX with SwiftUI&apos;s task view modifier</title><link>https://peterfriese.dev/blog/2024/delay-task-modifier/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2024/delay-task-modifier/</guid><description>Learn how to implement a DelayTaskViewModifier</description><pubDate>Fri, 18 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;&lt;/p&gt;
&lt;p&gt;I&apos;m &lt;a href=&quot;https://www.youtube.com/playlist?list=PLsnLd2esiGRTmfGZcZMnEy6hkBHXBH_en&quot;&gt;currently working on an app&lt;/a&gt; that helps me collect articles, read them later - and eventually curate them for my newsletter. (Oh, and of course there are some AI features that I will write about later).&lt;/p&gt;
&lt;p&gt;When the user navigates into an article, I want the app to mark the article as read (or rather “seen”). But not immediately - the user might have accidentally navigated into the article, or after reading the first sentence they realise they were looking for something else. Instead, I want to mark the article as read / seen after short delay of 5 seconds. That should be enough to consider the user&apos;s interaction as intentional.&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at how to implement this using SwiftUI&apos;s &lt;code&gt;.task&lt;/code&gt; view modifier.&lt;/p&gt;
&lt;h2&gt;What is the task modifier?&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;.task&lt;/code&gt; view modifier performs “an asynchronous task with a lifetime that matches that of the modified view” (&lt;a href=&quot;https://bit.ly/3AbXkub&quot;&gt;source&lt;/a&gt;). In short, the task starts as soon as the view appears, and SwiftUI will cancel the task if it doesn&apos;t complete before the view disappears.&lt;/p&gt;
&lt;p&gt;By default, the priority of this task is &lt;code&gt;userInitiated&lt;/code&gt;, meaning the user directly requested the operation, and probably happy to wait for a short amount of time for the result.&lt;/p&gt;
&lt;p&gt;This is useful when you want to kick off a long-running operation (such as downloading an image), and want to cancel that operation when the user leaves the view before the task has completed (in most cases, it doesn&apos;t make a lot of sense to continue downloading an image that the user isn&apos;t going to look at).&lt;/p&gt;
&lt;p&gt;The following code snippet shows how to use the task modifier to start a (potentially long running) operation, and cancel it when the view disappears using the &lt;code&gt;.task&lt;/code&gt; view modifier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Some view&quot;)
  .task {
    do {
      let result = try await performSomeLongRunningOperation()
      print(result)
    }
    catch {
      print(&quot;Error: \(error)&quot;)
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Guarding code with a task timer&lt;/h2&gt;
&lt;p&gt;To understand how we can use this behaviour to our advantage, let&apos;s turn our attention to &lt;code&gt;Task.sleep(for:)&lt;/code&gt; and friends.&lt;/p&gt;
&lt;p&gt;Did you ever wonder why all of the &lt;code&gt;sleep&lt;/code&gt; methods (except for &lt;code&gt;Task.sleep(_ duration:)&lt;/code&gt;) are marked as &lt;code&gt;throws&lt;/code&gt;? I mean, how can a timer possibly throw - if the clock stops working?&lt;/p&gt;
&lt;p&gt;It turns out this is actually pretty smart, as we will see in a minute. Consider the following code snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Some view&quot;)
  .task {
    do {
      try await Task.sleep(for: .seconds(3))
      // some code we want to guard
    }
    catch {
      print(&quot;Code not executed!&quot;)
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By throwing, the sleeping task jumps straight into the catch clause, essentially forgoing the execution of the code directly after the sleep call.&lt;/p&gt;
&lt;p&gt;Using this technique, we can make sure that any code after &lt;code&gt;Task.sleep(for:)&lt;/code&gt; isn&apos;t executed if the view disappears before the specified time has run out. Exactly what we want.&lt;/p&gt;
&lt;h2&gt;A delayed task modifier&lt;/h2&gt;
&lt;p&gt;Now that we have a solution, let&apos;s make it reusable. After all, the code is a bit verbose.&lt;/p&gt;
&lt;p&gt;To make this reusable, we need to override the &lt;code&gt;.task&lt;/code&gt; view modifer. As a first step, we need to implement a &lt;code&gt;ViewModifier&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct DelayTaskViewModifier: ViewModifier {
  let delay: ContinuousClock.Instant.Duration
  let action: @Sendable () async -&amp;gt; Void

  func body(content: Content) -&amp;gt; some View {
    content
      .task {
        do {
          try await Task.sleep(for: delay)
          await action()
        }
        catch {
          // do nothing
        }
      }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make using this view modifier easier, let&apos;s provide an extension on &lt;code&gt;View&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension View {
  /// Executes the given action after the specified delay unless
  /// the task is cancelled.
  ///
  /// - Parameters:
  ///   - delay: The duration to wait before executing the task.
  ///   - action: The asynchronous action to execute.
  func task(
    delay: ContinuousClock.Duration,
    action: @Sendable @escaping () async -&amp;gt; Void
  ) -&amp;gt; some View {
    modifier(DelayTaskViewModifier(delay: delay, action: action))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, the call site can be simplified to the following, which is a lot easier to understand (and much less verbose):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WebView(htmlString: styledHTML)
  .task(timeout: .seconds(3)) {
    // mark the article as read after 3 seconds
    article.isRead = true
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Further reading&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/polpielladev&quot;&gt;Pol&lt;/a&gt; recently published an article that shows how to migrate from Combine to &lt;code&gt;AsyncStream:&lt;/code&gt;&lt;a href=&quot;https://www.polpiella.dev/observable-property-changes&quot;&gt;How to listen for property changes in an @Observable class using AsyncStreams&lt;/a&gt;. He discusses how implement debouncing for a text input field. While this might sound very similar to the solution I showed in this post, debouncing doesn&apos;t cancel the task, but instead restarts it. This is a subtle difference.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/mecid&quot;&gt;Majid&lt;/a&gt; wrote about &lt;a href=&quot;https://swiftwithmajid.com/2022/06/28/the-power-of-task-view-modifier-in-swiftui/&quot;&gt;The power of task view modifier in SwiftUI&lt;/a&gt; a while ago, and shows how to build a version of the &lt;code&gt;.task&lt;/code&gt; view modifier that supports debouncing.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;One of the key aspects of Combine (and other functional approaches such as RxSwift) is that is provides a DSL for common operations such as debouncing or delaying.&lt;/p&gt;
&lt;p&gt;However, as you saw in this blog post, we can build equivalent solutions based on Swift&apos;s concurrency model and leverging SwiftUI&apos;s building blocks such as the &lt;code&gt;.task&lt;/code&gt; view modifier.&lt;/p&gt;
</content:encoded></item><item><title>SwiftUI Hero Animations with NavigationTransition</title><link>https://peterfriese.dev/blog/2024/hero-animation/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2024/hero-animation/</guid><description>Learn how to replicate the App Store hero animation with SwiftUI&apos;s new NavigationTransition</description><pubDate>Fri, 21 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;&lt;/p&gt;
&lt;p&gt;import PlainNavigationDemo from &apos;./hero-animation/PlainNavigationDemo.mp4&apos;;
import HeroNavigationDemo from &apos;./hero-animation/HeroNavigationDemo.mp4&apos;;
import HeroNavigationConfigurationDemo from &apos;./hero-animation/HeroNavigationConfigurationDemo.mp4&apos;;
import HeroNavigationConfigurationFineTunedDemo from &apos;./hero-animation/HeroNavigationConfigurationFineTunedDemo.mp4&apos;;
import HeroNavigationConfigurationFineTunedDemoDragToDismiss from &apos;./hero-animation/HeroNavigationConfigurationFineTunedDemoDragToDismiss.mp4&apos;;
import HeroNavigationConfigurationFineTunedDemoDragToDismissScaleDown from &apos;./hero-animation/HeroNavigationConfigurationFineTunedDemoDragToDismissScaleDown.mp4&apos;;
import FinalDemo from &apos;./hero-animation/FinalDemo.mp4&apos;;&lt;/p&gt;
&lt;p&gt;We all know and love the App Store&apos;s hero animation - it&apos;s great for visually rich UIs, such as the App Store&apos;s &lt;em&gt;Today&lt;/em&gt; view, the Apple TV app, the &lt;em&gt;Explore&lt;/em&gt; stream in AirBnB, and many other apps.&lt;/p&gt;
&lt;p&gt;With SwiftUI&apos;s &lt;code&gt;NavigationTransition&lt;/code&gt; protocol, introduced in iOS 18, implementing a hero animation is a matter of just three lines of code.&lt;/p&gt;
&lt;p&gt;In this article, you will learn how to implement a hero animation that looks similar to the one in the App Store&apos;s Today view. Achieving this look and feel requires more than just three lines of code, so we will also look into making this a reusable SwiftUI component.&lt;/p&gt;
&lt;h2&gt;Zooming from zero to hero&lt;/h2&gt;
&lt;p&gt;Let&apos;s imagine we&apos;re working on an app that allows users to browse gradients. The following snippet displays a list of meshed gradients. The user can navigate to a details screen by tapping on any of the cards.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct PlainNavigationDemo: View {
  var gradients = GradienConfiguration.samples

  var body: some View {
    NavigationStack {
      ScrollView {
        ForEach(gradients) { gradient in
          NavigationLink(value: gradient) {
            GradientView(configuration: gradient)
              .frame(height: 450)
              .padding(16)
          }
        }
        .navigationDestination(for: GradienConfiguration.self) { gradient in
          GradientView(configuration: gradient)
        }
      }
    }
  }
}

#Preview {
  PlainNavigationDemo()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={PlainNavigationDemo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;This drill-down navigation is common for many iOS apps, and has been around since the very first version of iOS. It&apos;s well suited for navigating from list items to their details (like in the Contacts app). However, for our beautiful meshed gradients (or a movie poster, a product card, or any other visually rich views), it doesn&apos;t feel snazzy enough.&lt;/p&gt;
&lt;p&gt;Thanks to SwiftUI&apos;s new &lt;code&gt;NavigationTransition&lt;/code&gt; protocol and its associated view modifiers, we can make this more appealing with just a few lines of code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct HeroNavigationDemo: View {
  @Namespace var namespace // [!code ++]
  var gradients = GradienConfiguration.samples

  var body: some View {
    NavigationStack {
      ScrollView {
        ForEach(gradients) { gradient in
          NavigationLink(value: gradient) {
            GradientView(configuration: gradient)
              .matchedTransitionSource(id: gradient.id, in: namespace) // [!code ++]
              .frame(height: 450)
              .padding(16)
          }
        }
        .navigationDestination(for: GradienConfiguration.self) { gradient in
          GradientView(configuration: gradient)
            .navigationTransition(.zoom(sourceID: gradient.id, in: namespace)) // [!code ++]
        }
      }
    }
  }
}

#Preview {
  HeroNavigationDemo()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={HeroNavigationDemo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;There are three main steps to implementing a hero animation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define a namespace to allow SwiftUI to track the identity of the source and target view across the animation.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;matchedTransitionSource&lt;/code&gt; view modifier to the view you want to transition from, and specify the namespace you defined in the first step, and an &lt;code&gt;id&lt;/code&gt; for the view within that namespace.&lt;/li&gt;
&lt;li&gt;On the target view, use the &lt;code&gt;navigationTransition&lt;/code&gt; view modifier, and specify the animnation you want to use. At the moment, the only animations available are .&lt;code&gt;automatic&lt;/code&gt; (which is the default slide-over animation), and &lt;code&gt;.zoom&lt;/code&gt; , which enables the hero animation. When using &lt;code&gt;.zoom&lt;/code&gt;, you need to specify the same namespace and &lt;code&gt;id&lt;/code&gt; you used on the source view.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Adjusting the appearance of the source view&lt;/h2&gt;
&lt;p&gt;The design of the source view leaves a bit to be desired (even though we&apos;re using a gradient…), but thankfully there is an overloaded version of the &lt;code&gt;. matchedTransitionSource&lt;/code&gt; view modifier that we can use to adjust the appearance of the source view.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GradientView(configuration: gradient)
  .matchedTransitionSource(id: gradient.id, in: namespace, configuration: { source in // [!code ++]
    source // [!code ++]
      .background(.accentColor) // [!code ++]
      .clipShape(RoundedRectangle(cornerRadius: 16)) // [!code ++]
      .shadow(radius: 8) // [!code ++]
  })
  .frame(height: 450)
  .padding(16)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={HeroNavigationConfigurationDemo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Inside the &lt;code&gt;configuation&lt;/code&gt; closure of the &lt;code&gt;.matchedTransformationSource&lt;/code&gt; view modified, we can use the &lt;code&gt;source&lt;/code&gt; parameter to adjust the appearance of the source view.&lt;/p&gt;
&lt;p&gt;It&apos;s worth noting that &lt;code&gt;source&lt;/code&gt; does not conform to &lt;code&gt;View&lt;/code&gt; and is not a direct representation of the source view itself. Instead, it conforms to the &lt;code&gt;EmptyMatchedTransitionSourceConfiguration&lt;/code&gt; protocol.&lt;/p&gt;
&lt;p&gt;This means we can only control the shadow, background color, and clip shape of the source view. It&apos;s also not possible to wrap the source view inside another view, as the return type of the closure is &lt;code&gt;MatchedTransitionSourceConfiguration&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s surprising that the &lt;code&gt;clipShape&lt;/code&gt; modifier only supprts &lt;code&gt;RoundedRectangle&lt;/code&gt; - so if you were hoping to use &lt;code&gt;UnevenRoundedRectangle&lt;/code&gt; for some fancy asymtrical rounded corners, you might need to file a feedback with Apple to get supports for this in one of the next betas.&lt;/p&gt;
&lt;p&gt;But even with the limited number of view modifiers, we can achieve a much more appealing visual appearance of the source view.&lt;/p&gt;
&lt;h2&gt;Adding a dismiss button&lt;/h2&gt;
&lt;p&gt;If you compare the details view with the details screen of the App Store&apos;s &lt;em&gt;Today&lt;/em&gt; section, you will notice that the App Store details screen doesn&apos;t have a back button - instead, the user can dismiss the screen by either tapping on the dismiss button in the top right corner, or by pulling down on the screen.&lt;/p&gt;
&lt;p&gt;Let&apos;s implement the dismiss button first.&lt;/p&gt;
&lt;p&gt;SwiftUI&apos;s &lt;code&gt;zoom&lt;/code&gt; animation can transition from any view to any other view, so we can create a wrapper view that hides the nvigatino bar and overlays the &lt;code&gt;GradientView&lt;/code&gt; with a dismiss button.&lt;/p&gt;
&lt;p&gt;To make the code easier to read and more maintainable, it&apos;s a good idea to create a separate view for this. This will also allow us to use the &lt;code&gt;Dismiss&lt;/code&gt; action from the detail view&apos;s environment.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct DismissableView&amp;lt;Content: View&amp;gt;: View {
  @Environment(\.dismiss) private var dismiss
  private var content: () -&amp;gt; Content

  init(@ViewBuilder content: @escaping () -&amp;gt; Content) {
    self.content = content
  }

  var dismissButton: some View {
    HStack {
      Spacer()
      Button {
        dismiss()
      } label: {
        Image(systemName: &quot;xmark&quot;)
          .frame(width: 30, height: 30)
          .foregroundColor(Color(uiColor: .label))
          .background(Color(uiColor: .systemBackground))
          .clipShape(Circle())
      }
      .padding([.top, .trailing], 30)
    }
  }

  var body: some View {
    ZStack(alignment: .topLeading) {
      content()
      dismissButton
    }
    .ignoresSafeArea()
    .navigationBarBackButtonHidden()
    .statusBarHidden()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={HeroNavigationConfigurationFineTunedDemo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;By wrapping the content in a &lt;code&gt;ZStack&lt;/code&gt;, we can place the dismiss button on top of the main content. We can now get rid of the navigation back button and the status bar, and finally use &lt;code&gt;ignoresSafeArea()&lt;/code&gt; to allow the content to take up the entire space of the screen.&lt;/p&gt;
&lt;p&gt;We can now use the &lt;code&gt;DismissableView&lt;/code&gt; at the call site:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.navigationDestination(for: GradienConfiguration.self) { gradient in
  DismissableView {
    GradientView(configuration: gradient)
  }
  .navigationTransition(.zoom(sourceID: gradient.id, in: namespace))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Dragging down to dismiss&lt;/h2&gt;
&lt;p&gt;In the App Store &lt;em&gt;Today&lt;/em&gt; section, users can also drag down the details view to dismiss it. To implement this feature, we can make use of SwiftUI&apos;s new &lt;code&gt;onScrollGeometryChange&lt;/code&gt; view modifier. It provides an elegant way to trigger actions based on changes of a scroll view&apos;s geometry.&lt;/p&gt;
&lt;p&gt;In our case, we want to dismiss the view when the user pulls the view down for a certain amount.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
  ScrollView { // [!code ++]
    ZStack(alignment: .topLeading) {
      content()
      dismissButton
    }
  } // [!code ++]
  .ignoresSafeArea()
  .navigationBarBackButtonHidden()
  .onScrollGeometryChange(for: Bool.self) { geometry in // [!code ++]
    geometry.contentOffset.y &amp;lt; -50 // [!code ++]
  } action: { _, isTornOff in // [!code ++]
    if isTornOff { // [!code ++]
      dismiss() // [!code ++]
    } // [!code ++]
  } // [!code ++]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={HeroNavigationConfigurationFineTunedDemoDragToDismiss} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;onScrollGeometryChange&lt;/code&gt; takes three parameters: the first one specifies the type we want to map scroll geometry changes to. We want to detect whether or not we should dismiss the view, so this is a &lt;code&gt;Bool&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second parameter is a closure in which we map scroll geometry changes to the type we specified in the first parameter. Here, we return &lt;code&gt;true&lt;/code&gt; if the user drags the view down for more than 50 pixels.&lt;/p&gt;
&lt;p&gt;The final parameter is a clousre that will be called when the value returned from the &lt;code&gt;transform&lt;/code&gt; closure changes. It receives both the old and new value. In our case, we just need the new value - if it&apos;s &lt;code&gt;true&lt;/code&gt;, we know that the user has dragged the view down beyond the tear-off point, and we can dismiss the view.&lt;/p&gt;
&lt;h2&gt;Scaling down&lt;/h2&gt;
&lt;p&gt;To implement the scaling effect of the App Store&apos;s &lt;em&gt;Today&lt;/em&gt; details view, we can apply what we&apos;ve learned in the previous section.&lt;/p&gt;
&lt;p&gt;This time, we want to translate the scroll geometry changes to a &lt;code&gt;scaleFactor&lt;/code&gt; of type &lt;code&gt;CGFloat&lt;/code&gt; so we can scale the entire view accordingly. In addition, we change the opacity of the dissmiss button in response to the amount the user drags down.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@State var scaleFactor: CGFloat = 1 // [!code ++]
@State var cornerRadius: CGFloat = 16 // [!code ++]
@State var opacity: CGFloat = 1 // [!code ++]

var body: some View {
  ScrollView {
    ZStack(alignment: .topLeading) {
      content()
      dismissButton
        .opacity(opacity) // [!code ++]
    }
    .scaleEffect(scaleFactor) // [!code ++]
  }
  .ignoresSafeArea()
  .navigationBarBackButtonHidden()
  .background(Color(UIColor.secondarySystemBackground)) // [!code ++]
  .scrollIndicators(scaleFactor &amp;lt; 1 ? .hidden : .automatic, axes: .vertical) // [!code ++]
  .onScrollGeometryChange(for: CGFloat.self) { geometry in // [!code ++]
    geometry.contentOffset.y // [!code ++]
  } action: { oldValue, newValue in // [!code ++]
    if newValue &amp;gt;= 0 { // [!code ++]
      scaleFactor = 1 // [!code ++]
      cornerRadius = 16 // [!code ++]
      opacity = 1 // [!code ++]
    } // [!code ++]
    else { // [!code ++]
      scaleFactor = 1 - (0.1 * (newValue / -50)) // [!code ++]
      cornerRadius = 55 - (35 / 50 * -newValue) // [!code ++]
      opacity = 1 - (abs(newValue) / 50) // [!code ++]
    } // [!code ++]
  } // [!code ++]
  .onScrollGeometryChange(for: Bool.self) { geometry in
    geometry.contentOffset.y &amp;lt; -50
  } action: { _, isTornOff in
    if isTornOff {
      dismiss()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={HeroNavigationConfigurationFineTunedDemoDragToDismissScaleDown} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;h2&gt;Disable highlighting the source view&lt;/h2&gt;
&lt;p&gt;The final tweak is to get rid of the highlight SwiftUI applies to the source view when the user taps it. To achieve this, we can define a new &lt;code&gt;ButtonStyle&lt;/code&gt; that doesn&apos;t implement any highlighting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct NoHighlightButtonStyle: ButtonStyle {
  func makeBody(configuration: Configuration) -&amp;gt; some View {
    configuration.label
  }
}

extension ButtonStyle where Self == NoHighlightButtonStyle {
  static var noHighlight: NoHighlightButtonStyle {
    get { NoHighlightButtonStyle() }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply the style on the &lt;code&gt;NavigationLink&lt;/code&gt; like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NavigationLink(value: gradient) {
  GradientView(configuration: gradient)
    .matchedTransitionSource(id: gradient.id, in: namespace, configuration: { source in
      source
        .background(gradient.colors.last ?? .gray)
        .clipShape(RoundedRectangle(cornerRadius: 16))
        .shadow(radius: 8)
    })
    .frame(height: 450)
    .padding(16)
}
.buttonStyle(.noHighlight) // [!code ++]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;
&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={FinalDemo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;SwiftUI 16 makes it easier than ever before to implement hero animations that allow us to implement UIs that delight our users. With just three lines of code, we were able to add a transition that looks similar to the App Store&apos;s &lt;em&gt;Today&lt;/em&gt; view hero animation. And with a little bit of extra effort, we were able to improve the user experience even more.&lt;/p&gt;
&lt;p&gt;If you&apos;re interested in learning more about building custom SwiftUI components, check out my interactive tutorial &lt;a href=&quot;https://peterfriese.dev/tutorials/&quot;&gt;Building Reusable SwiftUI Components&lt;/a&gt;, and don&apos;t forget to &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;follow me on Twitter&lt;/a&gt; - I regularly post about SwiftUI, Firebase, AI, and other topics.&lt;/p&gt;
</content:encoded></item><item><title>Styling SwiftUI Views</title><link>https://peterfriese.dev/blog/2023/swiftui-styling-views/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2023/swiftui-styling-views/</guid><description>Learn how to create custom styles for built-in SwiftUI views</description><pubDate>Fri, 06 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;&lt;/p&gt;
&lt;p&gt;SwiftUI makes it easy to customise views In most situations, using the built-in view modifiers is enough. For example, here is a &lt;code&gt;Text&lt;/code&gt; view on a pink rounded rectangle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Hello, World!&quot;)
  .padding(8)
  .background(.pink, in: RoundedRectangle(cornerRadius: 8, style: .continuous))
  .foregroundColor(.white)
  .bold()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;For more complicated layouts, we can combine multiple simple SwiftUI views into a more complex views, for example by making use of SwiftUI’s stacks:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoListRowView: View {
  private let onStyle: AnyShapeStyle = AnyShapeStyle(.tint)
  private let offStyle: AnyShapeStyle = AnyShapeStyle(.gray)

  @Binding var todo: Todo

  var body: some View {
    HStack {
      Image(systemName: todo.completed ? &quot;largecircle.fill.circle&quot;
                                       : &quot;circle&quot;)
      .resizable()
      .frame(width: 24, height: 24)
      .foregroundStyle(todo.completed ? onStyle : offStyle)
      .onTapGesture {
        todo.completed.toggle()
      }
      Text(todo.title)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;But sometimes, this isn’t such a great idea: complex views might obscure the actual intent of the respective UI. This is especially true if we’re building a custom view to replace functionality that SwiftUI and the operating system provide out of the box.&lt;/p&gt;
&lt;p&gt;To alleviate this problem, we can refactor complex views into smaller, more specific views. I&apos;ve talked about this strategy in-depth in my video series &lt;a href=&quot;https://www.youtube.com/playlist?list=PLsnLd2esiGRTfzn8pq4ZMYyDsL8GEMZO8&quot;&gt;Building SwiftUI Components&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://www.youtube.com/watch?v=UhDdtdeW63k&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;I am a great fan of refactoring views in to smaller, reusable parts, but today, I’d like to talk about another strategy you should know about.&lt;/p&gt;
&lt;p&gt;To better understand this strategy, let’s for a moment consider the Swift language itself. In Swift, we use classes and structs to express the structure and behaviour of our apps. Some of the structs and classes in our apps expose similar structures and behaviours, and one way to express this is to use protocols. Just like classes and structs can conform to protocols to make it easier for developers to know what kind of general behaviour to expect from a specific class or struct, this applies to views as well.&lt;/p&gt;
&lt;p&gt;For example, we expect a button to behave like a button, no matter its shape: when a user clicks on a button, we expect an action to be performed. But we also might want to be able to change the looks and design of a button completely, without having to reimplement all the basic functionality over and over again.&lt;/p&gt;
&lt;h2&gt;View styles&lt;/h2&gt;
&lt;p&gt;The designers of SwiftUI realised this early on, and created a mechanism that allows us to change the design of a UI element without breaking the contract for its behaviour: &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/view-styles&quot;&gt;View styles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;View styles enable SwiftUI to present a view in the most appropriate way for a particular presentation context. The most prominent example is &lt;code&gt;Label&lt;/code&gt;, which can either be presented as an icon, a text, or both, depending on which platform the app runs on and whether the &lt;code&gt;Label&lt;/code&gt; appears in a &lt;code&gt;List&lt;/code&gt; , a toolbar, etc.&lt;/p&gt;
&lt;p&gt;Let’s look at an example to see how view styles work, and how we can implement custom view styles.&lt;/p&gt;
&lt;h2&gt;Styling &lt;code&gt;Toggle&lt;/code&gt;s&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Toggle&lt;/code&gt; is one of the SwiftUI views that support styling. Its default style is probably one of the most iconic UI elements on iOS, prominently known from the settings app:&lt;/p&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Since this is the default style, we don’t need to explicitly specify it - SwiftUI will use it automatically:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Toggle(isOn: $isOn) {
  Text(&quot;Have you tried turning it \(isOn ? &quot;off&quot; : &quot;on&quot;)?&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If - for some reason (more about this later) - we want to set the style explicitly, we can apply the &lt;code&gt;.toggleStyle&lt;/code&gt; view modifier, using one of the styles that conform to &lt;code&gt;ToggleStyle&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Toggle(isOn: $isOn) {
  Text(&quot;Have you tried turning it \(isOn ? &quot;off&quot; : &quot;on&quot;)?&quot;)
}
.toggleStyle(.switch)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s worth noting that &lt;code&gt;.switch&lt;/code&gt; is just a shorthand notation for &lt;code&gt;SwitchToggleStyle()&lt;/code&gt;, and this gives us a way to tint the background of the switch. So if you wanted to use a pink background for a particular &lt;code&gt;Toggle&lt;/code&gt;, you can do so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Toggle(isOn: $isOn) {
  Text(&quot;Have you tried turning it \(isOn ? &quot;off&quot; : &quot;on&quot;)?&quot;)
}
.toggleStyle(SwitchToggleStyle(tint: .pink))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;That’s not the &lt;em&gt;only&lt;/em&gt; way to tint the background - you can also apply the &lt;code&gt;.tint&lt;/code&gt; view modifier, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Toggle(isOn: $isOn) {
  Text(&quot;Have you tried turning it \(isOn ? &quot;off&quot; : &quot;on&quot;)?&quot;)
}
.tint(.blue)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;In case you’re not a fan of this faux-skeuomorphic design, &lt;code&gt;Toggle&lt;/code&gt; also supports a more push-button like style:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Toggle(isOn: $isOn) {
  Text(&quot;Have you tried turning it \(isOn ? &quot;off&quot; : &quot;on&quot;)?&quot;)
}
.toggleStyle(.button)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Interestingly enough, &lt;code&gt;ButtonToggleStyle&lt;/code&gt; doesn&apos;t support a &lt;code&gt;tint&lt;/code&gt; parameter, so if you&apos;d like to tint a button-styled &lt;code&gt;Toggle&lt;/code&gt;, you will have to apply the &lt;code&gt;.tint&lt;/code&gt; view modifier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Toggle(isOn: $isOn) {
  Text(&quot;Have you tried turning it \(isOn ? &quot;off&quot; : &quot;on&quot;)?&quot;)
}
.toggleStyle(.button)
.tint(.pink)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;h2&gt;Implementing a custom style&lt;/h2&gt;
&lt;p&gt;The existing &lt;code&gt;Toggle&lt;/code&gt; styles are great for many situations, but what if your designer shows you a design that clearly calls for a &lt;code&gt;Toggle&lt;/code&gt;, but doesn&apos;t look like any of the built-in designs? Check boxes for a todo-list app are a prime example:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now, you might go ahead and implement this using an &lt;code&gt;Image&lt;/code&gt;, just like in this code snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoListRowView: View {
  private let onStyle: AnyShapeStyle = AnyShapeStyle(.tint)
  private let offStyle: AnyShapeStyle = AnyShapeStyle(.gray)

  @Binding var todo: Todo

  var body: some View {
    HStack {
      Image(systemName: todo.completed ? &quot;largecircle.fill.circle&quot;
                                       : &quot;circle&quot;)
      .resizable()
      .frame(width: 24, height: 24)
      .foregroundStyle(todo.completed ? onStyle : offStyle)
      .onTapGesture {
        todo.completed.toggle()
      }
      Text(todo.title)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works just fine, but you lose the benefit of the semantics that a &lt;code&gt;Toggle&lt;/code&gt; gives you, making your code harder to read for your fellow developers and your future self.&lt;/p&gt;
&lt;p&gt;Thankfully, implementing a custom style takes just a couple of lines of code - and we can even reuse some of the code from our naive implementation.&lt;/p&gt;
&lt;p&gt;To implement a custom style for the &lt;code&gt;Toggle&lt;/code&gt; view, we need to create a struct that conforms to the &lt;code&gt;ToggleStyle&lt;/code&gt; protocol. This protocol has one requirement, the &lt;code&gt;makeBody&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ReminderToggleStyle: ToggleStyle {
  func makeBody(configuration: Configuration) -&amp;gt; some View {
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;makeBody&lt;/code&gt; function works similar to the &lt;code&gt;body&lt;/code&gt; computed property on a regular SwiftUI view, but it receives a parameter with the &lt;code&gt;configuration&lt;/code&gt; of the style. The &lt;code&gt;Configuration&lt;/code&gt; of a style contains specific information about the view this style is applied to. In the case of &lt;code&gt;ToggleStyleConfiguration&lt;/code&gt;, it contains the &lt;code&gt;label&lt;/code&gt; of the &lt;code&gt;Toggle&lt;/code&gt;, as well as the state (&lt;code&gt;isOn&lt;/code&gt;), and another state (&lt;code&gt;isMixed&lt;/code&gt;) that comes into play when using nested toggles:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public struct ToggleStyleConfiguration {

  /// A type-erased label of a toggle.
  public struct Label : View {
    public typealias Body = Never
  }

  /// A view that describes the effect of switching the toggle between states.
  public let label: ToggleStyleConfiguration.Label

  /// A binding to a state property that indicates whether the toggle is on.
  @Binding public var isOn: Bool { get nonmutating set }

  public var $isOn: Binding&amp;lt;Bool&amp;gt; { get }

  /// Whether the ``Toggle`` is currently in a mixed state.
  @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
  public var isMixed: Bool
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;makeBody&lt;/code&gt;, we can use any SwiftUI view we like to implement the look and feel of the &lt;code&gt;Toggle&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;code-wide&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ReminderToggleStyle: ToggleStyle {
  private let onStyle: AnyShapeStyle = AnyShapeStyle(.tint)
  private let offStyle: AnyShapeStyle = AnyShapeStyle(.gray)

  func makeBody(configuration: Configuration) -&amp;gt; some View {
    HStack {
      configuration.label //(1)
      Image(systemName: configuration.isOn ? &quot;largecircle.fill.circle&quot; //(2)
                                           : &quot;circle&quot;)
        .resizable()
        .frame(width: 24, height: 24)
        .foregroundStyle(configuration.isOn ? onStyle : offStyle) //(3)
        .onTapGesture {
          configuration.isOn.toggle() //(4)
        }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;A couple of notes about the implementation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For this particular style, we don’t really care about the label of the &lt;code&gt;Toggle&lt;/code&gt;, but I’ve decided to put it here, mostly so you can see how you can access it. When using this style, we will just leave the &lt;code&gt;label&lt;/code&gt; parameter of the &lt;code&gt;Toggle&lt;/code&gt; empty - you will see this in a moment.&lt;/li&gt;
&lt;li&gt;We use an &lt;code&gt;Image&lt;/code&gt; to represent the state of the toggle. To detect the state, we access the &lt;code&gt;isOn&lt;/code&gt; binding on the &lt;code&gt;configuration&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;Similarly, we use the &lt;code&gt;isOn&lt;/code&gt; binding to tint the image when the toggle is in its &lt;code&gt;on&lt;/code&gt; position.&lt;/li&gt;
&lt;li&gt;And finally, we use &lt;code&gt;onTapGesture&lt;/code&gt; to toggle the &lt;code&gt;Toggle&lt;/code&gt; when the user taps on the image.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally, to make using this new style more convenient, we extend &lt;code&gt;ToggleStyle&lt;/code&gt; to allow us to use &lt;code&gt;.reminder&lt;/code&gt; as a shortcut notation for setting the style:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension ToggleStyle where Self == ReminderToggleStyle {
  static var reminder: ReminderToggleStyle {
    ReminderToggleStyle()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using the new style&lt;/h2&gt;
&lt;p&gt;Here is a simple todo list that makes use of the new custom toggle style:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoList: View {
  @State var tasks: [Todo]
  var body: some View {
    List($tasks) { $task in
      HStack {
        Toggle(isOn: $task.completed) {  } //(1)
          .toggleStyle(.reminder)
          .tint(.red)
        Text(task.title)
      }
    }
    .listStyle(.plain)
    .navigationTitle(&quot;Tasks&quot;)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;note align-with-code&quot;&amp;gt;
Note: As mentioned earlier, I do not provide a &lt;code&gt;label&lt;/code&gt; for the &lt;code&gt;Toggle&lt;/code&gt;s in this list (1).
&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Using a &lt;code&gt;Toggle&lt;/code&gt; with a custom style instead of a home-grown solution (as in the code snippet I showed you in the beginning of this post) makes it much easier to express the intent. The code looks a lot cleaner, and will be much easier to understand for anyone else reading it (including your future self).&lt;/p&gt;
&lt;h2&gt;Look after the environment&lt;/h2&gt;
&lt;p&gt;Before wrapping up, let’s take a look at one more aspect of view styles: SwiftUI propagates view styles down the view hierarchy (by using the SwiftUI environment). This is super convenient when you want to use the same style for several views. Just make sure they are children of the same container view, and apply the respective style to the container:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension ToggleStyle where Self == SettingsToggleStyle {
  static var settings: SettingsToggleStyle {
    SettingsToggleStyle()
  }
}

struct SettingsToggleStyle: ToggleStyle {
  private let onStyle: AnyShapeStyle = AnyShapeStyle(.tint)
  private let offStyle: AnyShapeStyle = AnyShapeStyle(.gray)

  func makeBody(configuration: Configuration) -&amp;gt; some View {
    HStack {
      configuration.label
      Spacer()
      Image(systemName: configuration.isOn ? &quot;square.fill&quot;
                                           : &quot;square&quot;)
        .resizable()
        .frame(width: 24, height: 24)
        .foregroundStyle(configuration.isOn ? onStyle : offStyle)
        .onTapGesture {
          configuration.isOn.toggle()
        }
    }
  }
}

struct SettingsView: View {
  @State var sync = true
  @State var detectDates = false
  @State var sendReminders = false
  var body: some View {
    Form {
      Toggle(isOn: $sync) {
        Text(&quot;Sync tasks in real-time&quot;)
      }
      Toggle(isOn: $detectDates) {
        Text(&quot;Detect due dates in tasks automatically&quot;)
      }
      Toggle(isOn: $sendReminders) {
        Text(&quot;Send daily reminder&quot;)
      }
    }
    .toggleStyle(.settings)
    .tint(.pink)
    .navigationTitle(&quot;Settings&quot;)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;aside class=&quot;example align-with-code&quot;&amp;gt;

&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;If you want to override the style for a particular view, you can do so by applying a different style to it.&lt;/p&gt;
&lt;h3&gt;Where to go next?&lt;/h3&gt;
&lt;p&gt;Styling views is a powerful concept that gives us a lot of flexibility when designing out apps without losing the semantics of the views we use.&lt;/p&gt;
&lt;p&gt;The list of SwiftUI views that support this concept is quite impressive: buttons, pickers, menus, toggles, indicators, text and labels, collection views, navigation views, windows and toolbars, and groups. So the next time you need a special look and feel for a UI element, check out &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/view-styles&quot;&gt;Apple’s documentation&lt;/a&gt; first to see if there already is a style for what you need. And if there is no style that meets your needs, you now know how to create a custom one!&lt;/p&gt;
&lt;p&gt;You can even build your own styleable SwiftUI views - and this is what we’re going to look at next time!&lt;/p&gt;
</content:encoded></item><item><title>Previewing Stateful SwiftUI Views</title><link>https://peterfriese.dev/blog/2022/swiftui-previews-interactive/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2022/swiftui-previews-interactive/</guid><description>Learn how to create interactive previews for SwiftUI views that make use of @Binding</description><pubDate>Wed, 21 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;import StatefulPreviews1 from &apos;./swiftui-previews-interactive/StatefulPreviews-1.mp4&apos;;
import StatefulPreviews2 from &apos;./swiftui-previews-interactive/StatefulPreviews-2.mp4&apos;;
import StatefulPreviews3 from &apos;./swiftui-previews-interactive/StatefulPreviews-3.mp4&apos;;
import StatefulPreviews4 from &apos;./swiftui-previews-interactive/StatefulPreviews-4.mp4&apos;;&lt;/p&gt;
&lt;p&gt;When building UIs in SwiftUI, we tend to build two kinds of UI components: screens and (reusable) views. Usually, we start by prototyping a screen, which will inevitably result in a &lt;em&gt;Massive ContentView&lt;/em&gt; that we then start refactoring into smaller, reusable components.&lt;/p&gt;
&lt;p&gt;Let’s assume we’re building a todo list application. Here is how it might look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoListView: View {
  @State var todos = [Todo]()

  var body: some View {
    NavigationStack {
      List($todos) { $todo in
        Toggle(isOn: $todo.completed) {
          Text(todo.title)
            .strikethrough(todo.completed)
        }
      }
      .listStyle(.plain)
      .navigationTitle(&quot;My tasks&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ve simplified this, but you can imagine that the code for a todo list application like Apple’s Reminders app looks a bit more complicated (in fact, if you’re curious, check out &lt;a href=&quot;https://github.com/peterfriese/MakeItSo&quot;&gt;MakeItSo&lt;/a&gt;, a sample project I created to replicate the Reminders app using SwiftUI and Firebase).&lt;/p&gt;
&lt;p&gt;One way to simplify this code is to refactor the &lt;code&gt;List&lt;/code&gt; view and extract the row into a separate reusable component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoRowView: View {
  @Binding var todo: Todo

  var body: some View {
    Toggle(isOn: $todo.completed) {
      Text(todo.title)
        .strikethrough(todo.completed)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;TodoRowView&lt;/code&gt; is now a child of the &lt;code&gt;List&lt;/code&gt; view, we want &lt;code&gt;TodoListView&lt;/code&gt; to be the owner of the data. To make sure any changes the user makes (by clicking on the toggle inside the &lt;code&gt;TodoRowView&lt;/code&gt; get reflected on the &lt;code&gt;List&lt;/code&gt; (and vice versa), we need to set up a bi-directional data binding. The right property wrapper for this job is &lt;code&gt;@Binding&lt;/code&gt; - it lets us connect to data that is owned by another view.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;However, when trying to set up the &lt;code&gt;PreviewProvider&lt;/code&gt; for this view, we quickly run into some limitations: how can we set up the preview with some demo data so that it can be edited inside the &lt;code&gt;TodoRowView&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;We might start by creating a static variable on the &lt;code&gt;PreviewProvider&lt;/code&gt;, and then pass it to the view we want to preview. However, this will inevitably result in a compiler error, as &lt;code&gt;TodoRowView&lt;/code&gt; expects &lt;code&gt;todo&lt;/code&gt; to be a &lt;code&gt;Binding&amp;lt;Todo&amp;gt;&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoRowView_Previews: PreviewProvider {
  static var todo = Todo(title: &quot;Draft article&quot;, completed: false)

  static var previews: some View {
    TodoRowView(todo: todo)
    // ^ error: &quot;Cannot convert value of type &apos;Todo&apos; to expected argument type &apos;Binding&amp;lt;Todo&amp;gt;&apos;&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Marking the static &lt;code&gt;todo&lt;/code&gt; variable as &lt;code&gt;@State&lt;/code&gt; resolves the compiler error, but it doesn&apos;t result in an interactive preview:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoRowView_Previews: PreviewProvider {
  @State static var todo = Todo(title: &quot;Draft article&quot;, completed: false)

  static var previews: some View {
    TodoRowView(todo: $todo)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={StatefulPreviews1} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;The go-to solution&lt;/h2&gt;
&lt;p&gt;The usual way to solve this is to use a constant binding. Apple provides us with a static function on &lt;code&gt;Binding&lt;/code&gt; that makes this straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoRowView_Previews_withConstantBinding: PreviewProvider {
  static var previews: some View {
    TodoRowView(todo: .constant(Todo.sampple))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the &lt;a href=&quot;https://bit.ly/3hL3qbY&quot;&gt;documentation states&lt;/a&gt;, this &lt;em&gt;creates a binding with an immutable value&lt;/em&gt;, which prevents the underlying property from being updated, so our implementation might seem broken when we run it in the preview pane.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={StatefulPreviews2} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;Using a custom binding&lt;/h2&gt;
&lt;p&gt;Another strategy for dealing with this situation is to use a custom binding. Here is a static function &lt;code&gt;mock&lt;/code&gt; that stores a value in a local variable and provides read/write access to it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Binding {
  static func mock(_ value: Value) -&amp;gt; Self {
    var value = value
    return Binding(get: { value },
                   set: { value = $0 })
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The nice thing about this technique is that is allows us to replace any calls to &lt;code&gt;.constant&lt;/code&gt; with a call to &lt;code&gt;.mock&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoRowView_Previews_withMockBinding: PreviewProvider {
  static var previews: some View {
    TodoRowView(todo: .mock(Todo.sampple))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this solution only works partially. While it is now possible to flip the toggle, the rest of the UI doesn’t update: when a todo is marked as complete, its title should be striked through, but as you can see in the animation, this doesn’t work.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={StatefulPreviews3} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;The Real Solution&lt;/h2&gt;
&lt;p&gt;It helps to remind ourselves that we have full control over the preview - for example, adding &lt;code&gt;.preferredColorScheme(.dark)&lt;/code&gt; to a view inside the &lt;code&gt;PreviewProvider&lt;/code&gt; will turn on dark mode.&lt;/p&gt;
&lt;p&gt;With this in mind, the solution becomes obvious: wrap the view inside a container view, and hold the state in this container view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoRowView_Previews_Container: PreviewProvider {
  struct Container: View {
    @State var todo = Todo.sampple
    var body: some View {
      TodoRowView(todo: $todo)
    }
  }

  static var previews: some View {
    Container()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the solution recommended by Apple in their WWDC 2020 session &lt;em&gt;Structure your app for SwiftUI previews&lt;/em&gt; (see &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2020/10149/?time=1095&quot;&gt;this timestamp&lt;/a&gt;). Since the container view owns the data via the &lt;code&gt;@State&lt;/code&gt; property wrapper, this approach gives us a SwiftUI preview that is fully interactive and responds to any changes of the view’s state.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={StatefulPreviews4} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;One more thing&lt;/h2&gt;
&lt;p&gt;We can even make this easier by creating a generic container view that passes a binding to a value to its contained view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct StatefulPreviewContainer&amp;lt;Value, Content: View&amp;gt;: View {
  @State var value: Value
  var content: (Binding&amp;lt;Value&amp;gt;) -&amp;gt; Content

  var body: some View {
    content($value)
  }

  init(_ value: Value, content: @escaping (Binding&amp;lt;Value&amp;gt;) -&amp;gt; Content) {
    self._value = State(wrappedValue: value)
    self.content = content
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to implement a stateful preview with just a few lines of code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoRowView_Previews_withGenericWrapper: PreviewProvider {
  static var previews: some View {
    StatefulPreviewContainer(Todo.sampple) { binding in
      TodoRowView(todo: binding)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is a &lt;a href=&quot;https://gist.github.com/peterfriese/16c139f63335e6fe32ffcb7ba2529970&quot;&gt;gist&lt;/a&gt; with the code for the &lt;code&gt;StatefulPreviewContainer&lt;/code&gt;, including a sample for how to use it.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;SwiftUI previews are a great tool for developing SwiftUI views, and when they work, they provide an amazing developer experience: no need to re-run the application for every little change you make. This significantly reduces turn-around times, and results in a much more efficient workflow.&lt;/p&gt;
&lt;p&gt;It feels a bit like an oversight that Apple didn’t provide an easy way to preview views that connect with their host views view &lt;code&gt;@Binding&lt;/code&gt;, but fortunately, there are some ways around this shortcoming.&lt;/p&gt;
&lt;p&gt;In this article, I showed you a couple of approaches that you can use when your SwiftUI views make use of &lt;code&gt;@Binding&lt;/code&gt; to communicate with the outside world. Personally, I like the preview container view the most, and you can even make this more efficient by defining an &lt;a href=&quot;https://sarunw.com/posts/how-to-create-code-snippets-in-xcode/&quot;&gt;Xcode code snippet&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Asynchronous programming with SwiftUI and Combine</title><link>https://peterfriese.dev/blog/2022/combine-vs-async/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2022/combine-vs-async/</guid><description>Calling asynchronous code from Combine</description><pubDate>Mon, 21 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;Mobile applications have to deal with a constant flow of events: user input, network traffic, and callbacks from the operating system are all vying for your app&apos;s attention. Building apps that feel snappy is a challenging task, as you have to efficiently handle all those events.&lt;/p&gt;
&lt;p&gt;Combine and async/await are some fairly recent addition to the collection of frameworks and language features that aim at making this easier.&lt;/p&gt;
&lt;p&gt;In this blog post, we will explore commonalities and differences of Combine and async/await, and I will show you how you can efficiently use both to call asynchronous APIs in your SwiftUI apps.&lt;/p&gt;
&lt;p&gt;To better understand the respective characteristics, we will look at a couple of code snippets taken from a SwiftUI screen that allows users to search for books by title. This example builds upon a previous blog post I wrote about &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-concurrency-essentials-part2/&quot;&gt;Cooperative Task Cancellation&lt;/a&gt;, and uses the &lt;a href=&quot;https://openlibrary.org/developers&quot;&gt;Open Library API&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Fetching data using Combine&lt;/h2&gt;
&lt;p&gt;Many of Apple’s APIs are Combine-enabled, and &lt;code&gt;URLSession&lt;/code&gt; is one of them. To fetch data from a URL, we can call &lt;code&gt;dataTaskPublisher&lt;/code&gt;, and then use some of Combine&apos;s operators to handle the response and transform it into a data model our application can work with. The following code snippet shows a typical Combine pipeline for fetching data from a remote API, mapping the result, extracting the information we need, and handling errors.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
Error handling in this code snippet is rather basic. I&apos;ve written about this topic more
extensively in &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-combine-networking-errorhandling/&quot;&gt;Error Handling with Combine and
SwiftUI&lt;/a&gt;, and I recommend
checking it out if your want to learn how to handle Combine errors and show them to the user in a
meanigful way in SwiftUI apps.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private func searchBooks(matching searchTerm: String) -&amp;gt; AnyPublisher&amp;lt;[Book], Never&amp;gt; {
  let escapedSearchTerm = searchTerm.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? &quot;&quot;
  let url = URL(string: &quot;https://openlibrary.org/search.json?q=\(escapedSearchTerm)&quot;)!

  return URLSession.shared.dataTaskPublisher(for: url)
    .map(\.data)
    .decode(type: OpenLibrarySearchResult.self, decoder: JSONDecoder())
    .map(\.books)
    .compactMap { openLibraryBooks in
      openLibraryBooks?.map { Book(from: $0) }
    }
    .replaceError(with: [Book]())
    .eraseToAnyPublisher()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For someone who is not familiar with Combine, it might not be immediately obvious how this code works, let alone being able to put together a pipeline like this. Getting into a functional reactive mindset probably is one of the biggest hurdles when learning Combine.&lt;/p&gt;
&lt;h2&gt;Fetching data using async/await&lt;/h2&gt;
&lt;p&gt;Let’s now look at how to implement the same method using &lt;code&gt;async/await&lt;/code&gt;. Apple has made sure that the most important asynchronous APIs can be called using &lt;code&gt;async/await&lt;/code&gt;. To fetch data from a URL, we can asynchronously call &lt;code&gt;await URLSession.shared.data(from: url)&lt;/code&gt;. By wrapping this call inside a &lt;code&gt;try catch&lt;/code&gt; block, we can add the same kind of error handling we implemented in the previous code snippet and return an empty array in case an error occurred.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re curious how Apple managed to provide async/await compatible versions of so many of their
APIs, I recommend checking out &lt;a href=&quot;https://youtu.be/sEKw2BMcQtQ&quot;&gt;Using async/await with Firebase&lt;/a&gt;, in
which I explain how &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md&quot;&gt;Concurrency Interoperability with Objective-C
(SE-0297)&lt;/a&gt;
works.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private func searchBooks(matching searchTerm: String) async -&amp;gt; [Book] {
  let escapedSearchTerm = searchTerm.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? &quot;&quot;
  let url = URL(string: &quot;https://openlibrary.org/search.json?q=\(escapedSearchTerm)&quot;)!

  do {
    let (data, _) = try await URLSession.shared.data(from: url)

    let searchResult = try OpenLibrarySearchResult.init(data: data)
    guard let libraryBooks = searchResult.books else { return [] }
    return libraryBooks.compactMap { Book(from: $0) }
  }
  catch {
    return []
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;ve got some experience writing and reading Swift code, you will be able to understand what this code does - even if you&apos;ve got no prior experience with &lt;code&gt;async/await&lt;/code&gt;: all keywords related to &lt;code&gt;async/await&lt;/code&gt; blend in with the rest of the code, making it rather natural to read and understand. This is not least due to the fact that the Swift language team modelled Swift’s concurrency features similar to how error handling works using &lt;code&gt;try catch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Of course, to &lt;em&gt;write&lt;/em&gt; code like this, you need a basic understanding of Swift’s concurrency features, so there definitely is a learning curve.&lt;/p&gt;
&lt;h2&gt;Is this the end of Combine?&lt;/h2&gt;
&lt;p&gt;Looking at these two code snippets, you might argue that the one making use of &lt;code&gt;async/await&lt;/code&gt; is easier to understand for developers who might not be familiar with neither Combine nor &lt;code&gt;async/wait&lt;/code&gt;, mostly due to the fact you can read if from top to bottom in a linear way.&lt;/p&gt;
&lt;p&gt;On the contrary, to understand the Combine version of the code, you have to know what a publisher is, why some of the operations are nested (for example the code for mapping a book inside the &lt;code&gt;compactMap/map&lt;/code&gt; structure), and why on earth you need to call &lt;code&gt;eraseToAnyPublisher&lt;/code&gt;. This can look very confusing if you&apos;re new to Combine.&lt;/p&gt;
&lt;p&gt;Add to that the lack of sessions about Combine at WWDC 2021 - it really seemed like Apple lost their enthusiasm for functional reactive programming.&lt;/p&gt;
&lt;p&gt;So - given both code snippets seem to do the same - is this the end of Combine?&lt;/p&gt;
&lt;p&gt;Well, I don’t think so, and this has to do with the fact SwiftUI is tightly integrated with Combine. In fact, Combine makes a number of things in SwiftUI a lot easier with surprisingly little code.&lt;/p&gt;
&lt;h2&gt;Connecting the UI…&lt;/h2&gt;
&lt;p&gt;To better understand this, let’s look at how to call the above code snippets from SwiftUI. The following code shows a typical way to implement a search screen: we’ve got a &lt;code&gt;List&lt;/code&gt; view to display the results, and a &lt;code&gt;.searchable&lt;/code&gt; view modifier to set up the search field and connect it to the &lt;code&gt;searchTerm&lt;/code&gt; published property on a view model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BookSearchCombineView: View {
  @StateObject var viewModel = ViewModel()

  var body: some View {
    List(viewModel.result) { book in
      BookSearchRowView(book: book)
    }
    .searchable(text: $viewModel.searchTerm)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;… to a Combine pipeline&lt;/h2&gt;
&lt;p&gt;By making the &lt;code&gt;searchTerm&lt;/code&gt; a published property on the view model, it becomes a Combine publisher, allowing us to use it as a starting point for a Combine pipeline. The view model&apos;s initialiser is a good place to set up this pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fileprivate class ViewModel: ObservableObject {
  @Published var searchTerm: String = &quot;&quot;

  @Published private(set) var result: [Book] = []
  @Published var isSearching = false

  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  init() {
    $searchTerm
      .debounce(for: 0.8, scheduler: DispatchQueue.main) //(1)
      .map { searchTerm -&amp;gt; AnyPublisher&amp;lt;[Book], Never&amp;gt; in //(2)
        self.isSearching = true
        return self.searchBooks(matching: searchTerm)
      }
      .switchToLatest() //(3)
      .receive(on: DispatchQueue.main) //(4)
      .sink(receiveValue: { books in //(5)
        self.result = books
        self.isSearching = false
      })
      .store(in: &amp;amp;cancellables) //(6)
  }

  private func searchBooks(matching searchTerm: String) -&amp;gt; AnyPublisher&amp;lt;[Book], Never&amp;gt; {
  // ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we subscribe to the &lt;code&gt;searchTerm&lt;/code&gt; publisher, and then use a couple of Combine operators to take the user&apos;s input, call the remote API, receive the results and assign them to a published property that is connected to the UI:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;debounce&lt;/code&gt; operator will only pass on events after there has been a 0.8s pause between event. This way, we will only call the remote API when the user has finished typing or pauses for a brief moment.&lt;/li&gt;
&lt;li&gt;We use the &lt;code&gt;map&lt;/code&gt; operator to call the &lt;code&gt;searchBooks&lt;/code&gt; pipeline (which itself is a publisher), and return its results into the pipeline.&lt;/li&gt;
&lt;li&gt;Even though we use the &lt;code&gt;debounce&lt;/code&gt; operator to reduce the number of events, we might run into a situation where multiple network requests are in flight at the same time. As a consequence, the network responses might arrive out-of-ordfer. To prevent this, we use &lt;code&gt;switchToLatest()&lt;/code&gt; - this will switch to the latest output from the upstream publisher and discards any other previous events.&lt;/li&gt;
&lt;li&gt;To make sure we make changes to the UI only from the main thread, we call &lt;code&gt;receive(on: DispatchQueue.main)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To assign the result of the pipeline (an array of &lt;code&gt;Book&lt;/code&gt; instances we receive from &lt;code&gt;searchBooks&lt;/code&gt;) to the published property &lt;code&gt;result&lt;/code&gt;, we would normally use the &lt;code&gt;assign(to:)&lt;/code&gt; subscriber, but as we also want to set the &lt;code&gt;isSearching&lt;/code&gt; property to &lt;code&gt;false&lt;/code&gt; (to turn off the progress view on our UI), we need to use the &lt;code&gt;sink&lt;/code&gt; subscriber, as this will allow us to perform multiple instructions.&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;sink&lt;/code&gt; subscriber also usually means we need to store the subscription in a &lt;code&gt;Cancellable&lt;/code&gt; or a &lt;code&gt;Set&lt;/code&gt; of &lt;code&gt;AnyCancellables&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice how easy it is to handle challenging tasks like discarding out-of-order events or reducing the number of requests being sent by only sending requests when the user stops typing. As you will see in a moment, this is slightly more complicated when using &lt;code&gt;async/await&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;… to an async/await method&lt;/h2&gt;
&lt;p&gt;How would the same code look like when using &lt;code&gt;async/await&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;To call the &lt;code&gt;async/await&lt;/code&gt; based version of &lt;code&gt;searchBooks&lt;/code&gt;, we need to choose a slightly different approach. Instead of subscribing to the &lt;code&gt;$searchTerm&lt;/code&gt; publisher, we create an &lt;code&gt;async&lt;/code&gt; method named &lt;code&gt;executeQuery&lt;/code&gt; and create a &lt;code&gt;Task&lt;/code&gt; that calls &lt;code&gt;searchBooks&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fileprivate class ViewModel: ObservableObject {
  @Published var searchTerm: String = &quot;&quot;

  @Published private(set) var result: [Book] = []
  @Published private(set) var isSearching = false

  private var searchTask: Task&amp;lt;Void, Never&amp;gt;? //(1)

  @MainActor //(7)
  func executeQuery() async {
    searchTask?.cancel() //(2)
    let currentSearchTerm = searchTerm.trimmingCharacters(in: .whitespaces)
    if currentSearchTerm.isEmpty {
      result = []
      isSearching = false
    }
    else {
      searchTask = Task { //(3)
        isSearching = true //(4)
        result = await searchBooks(matching: searchTerm) //(5)
        if !Task.isCancelled {
          isSearching = false //(6)
        }
      }
    }
  }

  private func searchBooks(matching searchTerm: String) async -&amp;gt; [Book] {
  // ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the &lt;code&gt;Task&lt;/code&gt;, we also handle the progress view’s state by updating the view model’s &lt;code&gt;isSearching&lt;/code&gt; published property according to the current state of the process.&lt;/p&gt;
&lt;p&gt;In the Combine-based version of this part of the app, we used a combination of &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;switchToLatest&lt;/code&gt; to make sure we only receive results for the most recent user input. This is particularly important for network requests, as they might return out of order.&lt;/p&gt;
&lt;p&gt;To achieve the same using &lt;code&gt;async/await&lt;/code&gt;, we need to use &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-concurrency-essentials-part2/&quot;&gt;cooperative task cancellation&lt;/a&gt;: we keep a reference to the task in &lt;code&gt;searchTask&lt;/code&gt; (1) and cancel any potentially running task (2) before starting a new one (3). To learn more about cooperative task cancellation, check out this &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-concurrency-essentials-part2/&quot;&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;searchBooks&lt;/code&gt; is marked as &lt;code&gt;async&lt;/code&gt;, the Swift runtime can decide to execute it on a non-main thread. However, in &lt;code&gt;executeQuery&lt;/code&gt;, we want to update the UI by setting published properties &lt;code&gt;result&lt;/code&gt; (5) and &lt;code&gt;isSearching&lt;/code&gt; (4, 6). To ensure it runs on the main thread, we have to mark it using the &lt;code&gt;@MainActor&lt;/code&gt; attribute (7).&lt;/p&gt;
&lt;p&gt;As a final step, we need to make a small but important change to the UI: since we cannot subscribe an asynchronous method to a published property, we need to find another way to call &lt;code&gt;executeQuery&lt;/code&gt; for each character the user types into the search field.&lt;/p&gt;
&lt;p&gt;It turns out that Apple added a suitable view modifier to the most recent version of SwiftUI - &lt;code&gt;onReceive(_ publisher:)&lt;/code&gt;. This view modifier allows us to register a closure that will be called whenever the given publisher emits an event:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(viewModel.result) { book in
  BookSearchRowView(book: book)
}
.searchable(text: $viewModel.searchTerm)
.onReceive(viewModel.$searchTerm) { searchTerm in
  Task {
    await viewModel.executeQuery()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Overall, using &lt;code&gt;async/await&lt;/code&gt; requires more work on our part, and it is easy to get things like cooperative task cancellation wrong or forget an inportant step, like cancelling any tasks that might still be running. In terms of developer experience, Combine follows a much more declarative approach than &lt;code&gt;async/await&lt;/code&gt;: you tell the framework &lt;em&gt;what&lt;/em&gt; to do, not &lt;em&gt;how&lt;/em&gt; to do it.&lt;/p&gt;
&lt;h2&gt;Calling asynchronous code from Combine&lt;/h2&gt;
&lt;p&gt;In the previous section, I claimed that we cannot subscribe to a Combine publisher using &lt;code&gt;async/await&lt;/code&gt;. But is this actually true? Let’s see if we can implement a smart way to combine async/await and Combine.&lt;/p&gt;
&lt;p&gt;The following snippet shows a view model that uses a Combine pipeline that calls an asynchronous version of the &lt;code&gt;searchBooks&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fileprivate class ViewModel: ObservableObject {
  // MARK: - Input
  @Published var searchTerm: String = &quot;&quot;

  // MARK: - Output
  @Published private(set) var result: [Book] = []
  @Published var isSearching = false

  // MARK: - Private
  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  init() {
    $searchTerm
      .debounce(for: 0.8, scheduler: DispatchQueue.main) //(1)
      .removeDuplicates() //(2)
      .handleEvents(receiveOutput: { output in //(3)
        self.isSearching = true
      })
      .flatMap { value in
        Future { promise in
          Task {
            let result = await self.searchBooks(matching: value)
            promise(.success(result))
          }
        }
      }
      .receive(on: DispatchQueue.main)
      .eraseToAnyPublisher()
      .handleEvents(receiveOutput: { output in //(4)
        self.isSearching = false
      })
      .assign(to: &amp;amp;$result) //(5)
  }

  private func searchBooks(matching searchTerm: String) async -&amp;gt; [Book] {
    let escapedSearchTerm = searchTerm.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? &quot;&quot;
    let url = URL(string: &quot;https://openlibrary.org/search.json?q=\(escapedSearchTerm)&quot;)!

    do {
      let (data, _) = try await URLSession.shared.data(from: url)

      let searchResult = try OpenLibrarySearchResult.init(data: data)
      guard let libraryBooks = searchResult.books else { return [] }
      return libraryBooks.compactMap { Book(from: $0) }
    }
    catch {
      return []
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach allows us to tap into the power of Combine to improve the user experience with just a few lines of code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By using the &lt;code&gt;debounce&lt;/code&gt; operator (1), we can hold off on sending search requests over the network until the user has stopped typing for a second. This means we will consume less bandwidth (good for the user), and cause fewer API calls (good for us, esp. when calling APIs that might be billed).&lt;/li&gt;
&lt;li&gt;We can further reduce the number of requests by removing any duplicate API calls using the &lt;code&gt;removeDuplicates&lt;/code&gt; operator (2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are also some advantages on the code level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By using the &lt;code&gt;handleEvents&lt;/code&gt; operator (3, 4), we can extract the code for handling the progress view from the &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;sink&lt;/code&gt; operators. This also allows us to replace the &lt;code&gt;sink/store&lt;/code&gt; combo by a much simpler and easier to use &lt;code&gt;assign&lt;/code&gt; subscriber&lt;/li&gt;
&lt;li&gt;There is only one place (5) in which we assign the result of the pipeline to the &lt;code&gt;result&lt;/code&gt; property, reducing the chances to introduce subtle programming errors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the same time, we can use the advantages of &lt;code&gt;async/await&lt;/code&gt; when writing network access code: being able to read the code from top to bottom in a linear way makes it a lot easier to understand than code that makes use of callbacks or nested closures.&lt;/p&gt;
&lt;p&gt;Let’s take a closer look at the code that allows us to call an asynchronous method from a Combine pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;somePublisher
  .flatMap { value in
    Future { promise in
      Task {
        let result = await self.searchBooks(matching: value)
        promise(.success(result))
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To call the asynchronous version of &lt;code&gt;searchBooks&lt;/code&gt;, we need to establish an asynchronous context. This is why we wrap the call in a &lt;code&gt;Task&lt;/code&gt;. Once &lt;code&gt;searchBook&lt;/code&gt; returns, we resolve the promise by sending the result as a &lt;code&gt;.success&lt;/code&gt; case value.&lt;/p&gt;
&lt;p&gt;We can simplify this code by extracting the relevant part into an extension on &lt;code&gt;Publisher&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Publisher {
  /// Executes an asyncronous call and returns its result to the downstream subscriber.
  ///
  /// - Parameter transform: A closure that takes an element as a parameter and returns a publisher that produces elements of that type.
  /// - Returns: A publisher that transforms elements from an upstream  publisher into a publisher of that element&apos;s type.
  func `await`&amp;lt;T&amp;gt;(_ transform: @escaping (Output) async -&amp;gt; T) -&amp;gt; AnyPublisher&amp;lt;T, Failure&amp;gt; {
    flatMap { value -&amp;gt; Future&amp;lt;T, Failure&amp;gt; in
      Future { promise in
        Task {
          let result = await transform(value)
          promise(.success(result))
        }
      }
    }
    .eraseToAnyPublisher()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to call an asynchronous method using the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;somePublisher
  .await { searchTerm in
    await self.searchBooks(matching: searchTerm)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;The seeming lack of attention Apple paid to Combine at WWDC 2021 resulted in a lot of confusion and uncertainty in the community - should you invest into learning Combine in the light of all the attention Apple put on &lt;code&gt;async/await&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;To answer this question, we need to take a step back and understand the value propositions of Combine and &lt;code&gt;async/await&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At a cursory glance, they seem to address the same use case: asynchronously calling APIs. However, when looking closer, it becomes clear that they are very different indeed:&lt;/p&gt;
&lt;p&gt;Combine is a reactive framework, with the notion of a stream of events that you transform using operators before consuming them with a subscriber. This side-effect-free way of programming makes is easier to ensure your app is always in a consistent state. In fact, SwiftUI’s state management system makes heavy use of Combine - every &lt;code&gt;@Published&lt;/code&gt; property is - as the name implies - a publisher, making it easy to connect a Combine pipeline.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Async/await&lt;/code&gt;, on the other hand, aims at making asynchronous programming and handling concurrency easier to implement and reason about. While this makes it easier to create a linear control flow, it doesn’t offer the same guarantees about state as Combine does.&lt;/p&gt;
&lt;p&gt;My recommendation is to use whichever of the two makes the most sense in any given situation. For any UI-related task, I personally prefer using Combine, as it gives us unprecedented power and flexibility when implementing otherwise difficult-to-implement aspects like debouncing user input, combining multiple input streams into one, and efficiently handling out-of-order execution of network requests.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Async/await&lt;/code&gt; is a great tool for implementing asynchronous calls - no matter if you’re calling a remote API such as a network service or a BaaS platform like Firebase.&lt;/p&gt;
&lt;p&gt;And finally, as you saw in this blog post, combining async/await and Combine is possible, allowing you to mix and match the best aspects of both approaches.&lt;/p&gt;
&lt;p&gt;Thanks for reading 🔥&lt;/p&gt;
</content:encoded></item><item><title>Building a Custom Combine Operator for Exponential Backoff</title><link>https://peterfriese.dev/blog/2022/swiftui-combine-custom-operators/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2022/swiftui-combine-custom-operators/</guid><description>Learn how to build custom Combine operators, and make your Combine pipelines easier to read and more reusable.</description><pubDate>Thu, 03 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-combine-networking-errorhandling/&quot;&gt;previous post&lt;/a&gt;, I showed you how to use Combine to improve error handling for Combine pipelines and expose errors in SwiftUI apps in a way that’s meaningful for the user.&lt;/p&gt;
&lt;p&gt;Not surprisingly, we ended up with code that looked a bit more complicated than what we had in the beginning. Properly handling errors will take up more lines of code than not handling errors at all (or just ignoring them).&lt;/p&gt;
&lt;p&gt;But we can do better!&lt;/p&gt;
&lt;p&gt;In this post, you will learn about Combine operators: what they are, how they work, and how refactoring our code into a custom Combine operator will make it easier to reason about and more reusable at the same time.&lt;/p&gt;
&lt;h2&gt;What is a Combine Operator?&lt;/h2&gt;
&lt;p&gt;Combine defines three main concepts to implement the idea of reactive programming:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Publishers&lt;/li&gt;
&lt;li&gt;Subscribers&lt;/li&gt;
&lt;li&gt;Operators&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Publishers&lt;/strong&gt; deliver values over time, and &lt;strong&gt;subscribers&lt;/strong&gt; act on these values as they receive them. &lt;strong&gt;Operators&lt;/strong&gt; sit in the middle between publishers and subscribers, and can be used to manipulate the stream of values.&lt;/p&gt;
&lt;p&gt;There are a few reasons why we need operators:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Publishers don&apos;t always produce events in the format that is required by the subscriber. For example, a publisher might emit the result of a HTTP network request, but our subscriber needs a custom data structure. In this situation, we can use an operator like &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;decode&lt;/code&gt; to turn the output of the publisher into the data structure the subscriber expects.&lt;/li&gt;
&lt;li&gt;Publishers might produce more events than the subscriber is interested in. For example, when typing a search term, we might not be interested in every single keystroke but only the final search term. In this situation, we can use operators like &lt;code&gt;debounce&lt;/code&gt; or &lt;code&gt;throttle&lt;/code&gt; to reduce the number of events our subscriber has to handle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Operators help us to take the output produced by a publisher and turn it into something that the subscriber can consume. We’ve already used a number of built-in operators in previous episodes, for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;map&lt;/code&gt; (and its friend, &lt;code&gt;tryMap&lt;/code&gt;) to transform elements&lt;/li&gt;
&lt;li&gt;&lt;code&gt;debounce&lt;/code&gt; to publish elements only after a pause between two events&lt;/li&gt;
&lt;li&gt;&lt;code&gt;removeDuplicates&lt;/code&gt; to remove duplicate events&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flatMap&lt;/code&gt; to transform elements into a new publisher&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How can we implement a custom operator?&lt;/h2&gt;
&lt;p&gt;Usually, when creating Combine pipelines, we will start with a publisher, and then connect a bunch of Combine’s built-in operators to process the events emitted by the publisher. At the end of any Combine pipeline is a subscriber that receives the events. As you saw in the &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-combine-networking-errorhandling/&quot;&gt;previous post&lt;/a&gt;, pipelines can become complicated quite quickly.&lt;/p&gt;
&lt;p&gt;Technically, operators are just functions that create other publishers and subscribers which handle the events they receive from an upstream publisher.&lt;/p&gt;
&lt;p&gt;This means, we can create our own custom operators by extending &lt;code&gt;Publisher&lt;/code&gt; with a function that returns a publisher (or subscriber) that operates on the events it receives from the publisher we use it on.&lt;/p&gt;
&lt;p&gt;Let’s see what this means in practice by implementing a simple operator that allows us to inspect events coming down a Combine pipeline using Swift’s &lt;code&gt;dump()&lt;/code&gt; function. This function prints the contents of a variable to the console, showing the structure of the variable as a nested tree - similar to the debug inspector in Xcode.&lt;/p&gt;
&lt;p&gt;Now, you might be aware of Combine’s &lt;code&gt;print()&lt;/code&gt; operator, which works very similarly. However, it doesn&apos;t provide as much detail, and - more importantly - doesn’t show the result as a nested structure.&lt;/p&gt;
&lt;p&gt;To add an operator, we first need to add an extension to the &lt;code&gt;Publisher&lt;/code&gt; type. As we don&apos;t want to manipulate the events this operator receives, we can use the upstream publisher’s types as the result types as well, and return &lt;code&gt;AnypPublisher&amp;lt;Self.Output, Self.Failure&amp;gt;&lt;/code&gt; as the result type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Publisher {
  func dump() -&amp;gt; AnyPublisher&amp;lt;Self.Output, Self.Failure&amp;gt; {
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the function, we can then use the &lt;code&gt;handleEvents&lt;/code&gt; operator to examine any events this pipeline processes. &lt;code&gt;handleEvents&lt;/code&gt; has a bunch of optional closures that get called when the publisher receives new subscriptions, new output values, a cancellation event, when it is finished, or when the subscriber requests more elements. As we are only interested in new &lt;code&gt;Output&lt;/code&gt; values, we can ignore most of the closures and just implement the &lt;code&gt;receiveOutput&lt;/code&gt; closure.&lt;/p&gt;
&lt;p&gt;Whenever we receive a value, we will use Swift’s &lt;code&gt;dump()&lt;/code&gt; function to print the contents of the value to the console:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Publisher {
  func dump() -&amp;gt; AnyPublisher&amp;lt;Self.Output, Self.Failure&amp;gt; {
    handleEvents(receiveOutput:  { value in
      Swift.dump(value)
    })
    .eraseToAnyPublisher()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use this operator like any of Combine’s built-in operators. In the following example, we attach our new operator to a simple publisher that emits the current date:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Just(Date())
  .dump()

// prints:

▿ 2022-03-02 09:38:49 +0000
  - timeIntervalSinceReferenceDate: 667906729.659255
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementing a retry operator with a delay&lt;/h2&gt;
&lt;p&gt;Now that we’ve got a basic understanding of how to implement a basic operator, let’s see if we can refactor the code from the previous episode. Here is the relevant part:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return dataTaskPublisher
  .tryCatch { error -&amp;gt; AnyPublisher&amp;lt;(data: Data, response: URLResponse), Error&amp;gt; in
    if case APIError.serverError = error {
      return Just(Void())
        .delay(for: 3, scheduler: DispatchQueue.global())
        .flatMap { _ in
          return dataTaskPublisher
        }
        .print(&quot;before retry&quot;)
        .retry(10)
        .eraseToAnyPublisher()
    }
    throw error
  }
  .map(\.data)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s begin by constructing an overloaded extension for the &lt;code&gt;retry&lt;/code&gt; operator on &lt;code&gt;Publisher&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Publisher {
  func retry&amp;lt;T, E&amp;gt;(_ retries: Int, withDelay delay: Int)
    -&amp;gt; Publishers.TryCatch&amp;lt;Self, AnyPublisher&amp;lt;T, E&amp;gt;&amp;gt;
       where T == Self.Output, E == Self.Failure
  {
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This defines two input parameters, &lt;code&gt;retries&lt;/code&gt; and &lt;code&gt;withDelay&lt;/code&gt;, which we can use to specify how many times the upstream publisher should be retried and how much time (in seconds) should be left between each retry.&lt;/p&gt;
&lt;p&gt;Since we are going to use the &lt;code&gt;tryCatch&lt;/code&gt; operator inside our new operator, we need to use its publisher type, &lt;code&gt;Publishers.TryCatch&lt;/code&gt; as the return type.&lt;/p&gt;
&lt;p&gt;With this in place, we can now implement the body of the operator by pasting the existing implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Publisher {
  func retry&amp;lt;T, E&amp;gt;(_ retries: Int, withDelay delay: Int)
    -&amp;gt; Publishers.TryCatch&amp;lt;Self, AnyPublisher&amp;lt;T, E&amp;gt;&amp;gt;
       where T == Self.Output, E == Self.Failure
  {
    return self.tryCatch { error -&amp;gt; AnyPublisher&amp;lt;T, E&amp;gt; in
      return Just(Void())
        .delay(for: .init(integerLiteral: delay), scheduler: DispatchQueue.global())
        .flatMap { _ in
          return self
        }
        .retry(retries)
        .eraseToAnyPublisher()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might have noticed that we removed the error check. This is because &lt;code&gt;APIError&lt;/code&gt; is an error type that is specific to our application. As we are interested in making this an implementation that can be used in other apps as well, let&apos;s see how we can make this more flexible.&lt;/p&gt;
&lt;h2&gt;Conditionally retrying&lt;/h2&gt;
&lt;p&gt;To make this code reusable in other contexts, let’s add a parameter for a trailing closure that the caller can use to control whether the operator should retry or not.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func retry&amp;lt;T, E&amp;gt;(_ retries: Int, withDelay delay: Int, condition: ((E) -&amp;gt; Bool)? = nil) -&amp;gt; Publishers.TryCatch&amp;lt;Self, AnyPublisher&amp;lt;T, E&amp;gt;&amp;gt; where T == Self.Output, E == Self.Failure {
  return self.tryCatch { error -&amp;gt; AnyPublisher&amp;lt;T, E&amp;gt; in
    if condition?(error) == true {
      return Just(Void())
        .delay(for: .init(integerLiteral: delay), scheduler: DispatchQueue.global())
        .flatMap { _ in
          return self
        }
        .retry(retries)
        .eraseToAnyPublisher()
    }
    else {
      throw error
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the caller doesn’t provide the closure, the operator will retry using the parameters &lt;code&gt;retries&lt;/code&gt; and &lt;code&gt;delay&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With this in place, we can simplify the original call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...
return dataTaskPublisher
  .retry(10, withDelay: 3) { error in
    if case APIError.serverError = error {
      return true
    }
      return false
    }
  .map(\.data)
  // ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementing a retry operator for exponential backoff&lt;/h2&gt;
&lt;p&gt;Now, let’s take this one step further and implement a version of the &lt;code&gt;retry&lt;/code&gt; operator with exponential back-off.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
Exponential backoff is commonly utilised as part of &lt;a href=&quot;https://en.wikipedia.org/wiki/Rate_limiting&quot;&gt;rate
limiting&lt;/a&gt; mechanisms in computer systems such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Web_service&quot;&gt;web
services&lt;/a&gt;, to help enforce fair distribution of access
to resources and prevent &lt;a href=&quot;https://en.wikipedia.org/wiki/Network_congestion&quot;&gt;network congestion&lt;/a&gt;.
(Wikipedia)
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To increment the delay between two requests, we introduce a local variable that holds the current interval, and double it after each request. To make this possible, we need to wrap the inner pipeline that kicks off the original pipeline in a pipeline that increments the backoff variable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func retry&amp;lt;T, E&amp;gt;(_ retries: Int,
                 withBackoff initialBackoff: Int,
                 condition: ((E) -&amp;gt; Bool)? = nil)
    -&amp;gt; Publishers.TryCatch&amp;lt;Self, AnyPublisher&amp;lt;T, E&amp;gt;&amp;gt;
    where T == Self.Output, E == Self.Failure
{
  return self.tryCatch { error -&amp;gt; AnyPublisher&amp;lt;T, E&amp;gt; in
    if condition?(error) ?? true {
      var backOff = initialBackoff
      return Just(Void())
        .flatMap { _ -&amp;gt; AnyPublisher&amp;lt;T, E&amp;gt; in
          let result = Just(Void())
            .delay(for: .init(integerLiteral: backOff), scheduler: DispatchQueue.global())
            .flatMap { _ in
              return self
            }
          backOff = backOff * 2
          return result.eraseToAnyPublisher()
        }
        .retry(retries - 1)
        .eraseToAnyPublisher()
    }
    else {
      throw error
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use exponential backoff only for certain kinds of errors, we can implement the closure to inspect the error, just like before. Here is a code snippet that shows how to use incremental backoff with an initial interval of 3 seconds for any &lt;code&gt;APIError.serverError&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return dataTaskPublisher
  .retry(2, withBackoff: 3) { error in
    if case APIError.serverError(_, _, _) = error {
      return true
    }
    else {
      return false
    }
  }
  // ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use exponential backoff regardless of the error, this becomes even more compact:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return dataTaskPublisher
  .retry(2, withIncrementalBackoff: 3)
  // ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;Combine is a very powerful framework that allows us to put together very efficient data and event processing pipelines for our apps.&lt;/p&gt;
&lt;p&gt;Sometimes, this power comes at a cost: in the previous episode, we built a powerful error handling pipeline that made our code look more complicated than the original version which used Combine’s built-in operators for handling errors by replacing them with default values of ignoring them altogether.&lt;/p&gt;
&lt;p&gt;In this episode, you saw how to make use of custom operators to refactor this code.&lt;/p&gt;
&lt;p&gt;Thanks to Combine’s flexible design, creating custom operators doesn’t require writing a lot of code, and helps to make our code more readable and reusable.&lt;/p&gt;
&lt;p&gt;Thanks for reading 🔥&lt;/p&gt;
</content:encoded></item><item><title>Error Handling with Combine and SwiftUI</title><link>https://peterfriese.dev/blog/2022/swiftui-combine-networking-errorhandling/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2022/swiftui-combine-networking-errorhandling/</guid><description>Learn how you can use Combine to improve error handling in your SwiftUI apps</description><pubDate>Mon, 14 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;import AnimatedImage from &apos;./swiftui-combine-networking-errorhandling/AnimatedImage.mp4&apos;;&lt;/p&gt;
&lt;p&gt;As developers, we tend to be a rather optimistic bunch of people. At least that’s the impression you get when looking at the code we write - we mostly focus on the happy path, and tend to spend a lot less time and effort on error handling.&lt;/p&gt;
&lt;p&gt;Even in this series, we’ve been neglecting error handling. In fact, we’ve mostly ignored it: in the &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-combine-networking-efficient/&quot;&gt;previous post&lt;/a&gt;, we replaced any errors with a default value, which was OK for prototyping our app, but this probably isn’t a solid strategy for any app that goes into production.&lt;/p&gt;
&lt;p&gt;This time, let’s take a closer look at how we can handle errors appropriately!&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Previously...&lt;/strong&gt; In case you didn&apos;t read the previous episodes
(&lt;a href=&quot;https://peterfriese.de/posts/swiftui-combine-networking-gettingstarted/&quot;&gt;1&lt;/a&gt;,
&lt;a href=&quot;https://peterfriese.de/posts/swiftui-combine-networking-efficient/&quot;&gt;2&lt;/a&gt;) of this series: the use
case we&apos;re discussing is the validation logic for a sign-up form. We use Combine to validate the
user&apos;s input, and as part of this validation, the app also calls an endpoint on the app&apos;s
authentication server to check if the username the user chose is still available. The endpoint
will return &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; depending on whether the name is still available.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h2&gt;Error handling strategies&lt;/h2&gt;
&lt;p&gt;Before we dive deeper into how to handle errors, let’s talk about a couple of error handling strategies and whether they are appropriate in our scenario.&lt;/p&gt;
&lt;h3&gt;Ignoring the error&lt;/h3&gt;
&lt;p&gt;This might sound like a terrible idea at first, but it&apos;s actually a viable option when dealing with certain types of errors under specific circumstances. Here are some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The user’s device is &lt;em&gt;temporarily&lt;/em&gt; offline or there is another reason why the app cannot reach the server.&lt;/li&gt;
&lt;li&gt;The server is down at the moment, but will be back up &lt;em&gt;soon&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In many cases, the user can continue working offline, and the app can sync with the server once the device comes back online. Of course, this requires some sort of offline capable sync solution (like Cloud Firestore).&lt;/p&gt;
&lt;p&gt;It is good practice to provide some user feedback to make sure users understand their data hasn’t been synchronised yet. Many apps show an icon (e.g. a cloud with an upward pointing arrow) to indicate the sync process is still in progress, or a warning sign to alert the user they need to manually trigger the sync once they’re back online.&lt;/p&gt;
&lt;h3&gt;Retrying (with exponential back-off)&lt;/h3&gt;
&lt;p&gt;In other cases, ignoring the error is not an option. Imagine the booking system for a popular event: the server might be overwhelmed by the amount of requests. In this case, we want to make sure that the system will not be thrashed by the users hitting “refresh” every couple of seconds. Instead, we want to spread out the time between retries. Using an exponential backoff strategy is both in the user’s and the system’s operator’s best interest: the operator can be sure their server will not be overwhelmed even more by users trying to get through by constantly refreshing, and the users should eventually get their booking through thanks to the app automatically retrying.&lt;/p&gt;
&lt;h3&gt;Showing an error message&lt;/h3&gt;
&lt;p&gt;Some errors require the user&apos;s action - for example if saving a document failed. In this case, it is appropriate to show a model dialog to get the user&apos;s attention and ask them how to proceed. For less severe errors, it might be sufficient to show a toast (an overlay that shows for a brief moment and then disappears).&lt;/p&gt;
&lt;h3&gt;Replacing the entire view with an error view&lt;/h3&gt;
&lt;p&gt;Under some circumstances, it might even be appropriate to replace the entire UI with an error UI. A well-known example for this is Chrome - if the device is offline, it will display the Chrome Dino to let users know their device is offline, and to help them spend the time until their connection restores with a fun jump-and-run game.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={AnimatedImage} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h3&gt;Showing an inline error message&lt;/h3&gt;
&lt;p&gt;This is a good option in case the data the user has provided isn’t valid. Not all input errors can be detected by a local form validation. For example, an online store might have a business rule that mandates shipments worth more than a certain amount must be shipped using a specific transport provider. It’s not always feasible to implement all of these business rules in the client app (a configurable rules engine definitely might help here), so we need to be prepared to handle these kinds of semantic errors.&lt;/p&gt;
&lt;p&gt;Ideally, we should show those kind of errors next to the respective input field to help the user provide the correct input.&lt;/p&gt;
&lt;h2&gt;Typical error conditions and how to handle them&lt;/h2&gt;
&lt;p&gt;To give you a better understanding of how to apply this in a real world scenario, let’s add some error handling to the sign-up form we created earlier in this series. In particular, we’ll deal with the following error conditions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Device/network offline&lt;/li&gt;
&lt;li&gt;Semantic validation errors&lt;/li&gt;
&lt;li&gt;Response parsing errors / invalid URL&lt;/li&gt;
&lt;li&gt;Internal server errors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Source Code&lt;/strong&gt; If you want to follow along, you will find the code for this episode in the
following GitHub repository:
&lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Combine-Applied&quot;&gt;https://github.com/peterfriese/SwiftUI-Combine-Applied&lt;/a&gt;,
in the &lt;code&gt;Networking&lt;/code&gt; folder. The &lt;code&gt;server&lt;/code&gt; subfolder contains a local server that helps us simulate
all the error conditions we will cover.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h2&gt;Implementing a fallible network API&lt;/h2&gt;
&lt;p&gt;In the previous post, we implemented an &lt;code&gt;AuthenticationService&lt;/code&gt; that interfaces with an authentication server. This helps us to keep everything neatly organised and separated by concerns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The view (&lt;code&gt;SignUpScreen&lt;/code&gt;) displays the state and takes the user&apos;s input&lt;/li&gt;
&lt;li&gt;The view model (&lt;code&gt;SignUpScreenViewModel&lt;/code&gt;) holds the state the view displays. In turn, it uses other APIs to react to the user’s actions. In this particular app, the view model uses the &lt;code&gt;AuthenticationService&lt;/code&gt; to interact with the authentication server&lt;/li&gt;
&lt;li&gt;The service (&lt;code&gt;AuthenticationService&lt;/code&gt;) interacts with the authentication server. Its main responsibilities are to bring the server’s responses into a format that the client can work with. For example, it converts JSON into Swift structs, and (most relevant for this post) it handles any network-layer errors and converts them into UI-level errors that the client can better work with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following diagram provides an overview of how the individual types work together:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If you take a look at the code we wrote in the previous post, you will notice that the &lt;code&gt;checkUserNamerAvailablePublisher&lt;/code&gt; has a failure type of &lt;code&gt;Never&lt;/code&gt; - that means it claims there is never going to be an error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func checkUserNameAvailablePublisher(userName: String) -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; { ... }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s a pretty bold statement, especially given network errors are really common! We were only able to guarantee this because we replaced any errors with a return value of &lt;code&gt;false&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func checkUserNameAvailablePublisher(userName: String)
    -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; {
  guard let url = URL(string: &quot;http://127.0.0.1:8080/isUserNameAvailable?userName=\(userName)&quot;) else {
    return Just(false).eraseToAnyPublisher()
  }

  return URLSession.shared.dataTaskPublisher(for: url)
    .map(\.data)
    .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
    .map(\.isAvailable)
    .replaceError(with: false)
    .eraseToAnyPublisher()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To turn this rather lenient implementation into something that returns meaningful error messages to the caller, we first need to change the failure type of the publisher, and stop glossing over any errors by returning &lt;code&gt;false&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum APIError: LocalizedError {
  /// Invalid request, e.g. invalid URL
  case invalidRequestError(String)
}

struct AuthenticationService {

  func checkUserNameAvailablePublisher(userName: String)
      -&amp;gt; AnyPublisher&amp;lt;Bool, Error&amp;gt; {
    guard let url =
        URL(string: &quot;http://127.0.0.1:8080/isUserNameAvailable?userName=\(userName)&quot;) else {
      return Fail(error: APIError.invalidRequestError(&quot;URL invalid&quot;))
        .eraseToAnyPublisher()
    }

    return URLSession.shared.dataTaskPublisher(for: url)
      .map(\.data)
      .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
      .map(\.isAvailable)
//      .replaceError(with: false)
      .eraseToAnyPublisher()
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also introduced a custom error type, &lt;code&gt;APIError&lt;/code&gt;. This will allow us to convert any errors that might occur inside our API (be it network errors or data mapping errors) into a semantically rich error that we can handle more easily in out view model.&lt;/p&gt;
&lt;h2&gt;Calling the API and handling errors&lt;/h2&gt;
&lt;p&gt;Now that the API has a failure type, we need to update the caller as well. Once a publisher emits a failure, the pipeline will terminate - unless you capture the error. A typical approach to handling errors when using &lt;code&gt;flatMap&lt;/code&gt; is to combine it with a &lt;code&gt;catch&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;somePublisher
  .flatMap { value in
    callSomePotentiallyFailingPublisher()
    .catch { error in
      return Just(someDefaultValue)
    }
  }
  .eraseToAnyPublisher()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Applying this strategy to the code in our view model results in the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .debounce(for: 0.8, scheduler: DispatchQueue.main)
    .removeDuplicates()
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailablePublisher(userName: username)
        .catch { error in //(1)
          return Just(false) //(2)
        }
        .eraseToAnyPublisher()
    }
    .receive(on: DispatchQueue.main)
    .share()
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just like that, we end up where we started! If the API emits a failure (for example, the username was too short), we catch the error (1) and replace it with &lt;code&gt;false&lt;/code&gt; (2) - this is exactly the behaviour we had before. Except, we wrote a lot more code…&lt;/p&gt;
&lt;p&gt;Seems like we&apos;re getting nowhere with this approach, so let’s take a step back and look at the requirements for our solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We want to use the emitted values of the pipeline to drive the state of the submit button, and to display a warning message if the chosen username is not available.&lt;/li&gt;
&lt;li&gt;If the pipeline emits a failure, we want to disable the submit button, and display the error message in the error label below the username input field.&lt;/li&gt;
&lt;li&gt;How exactly we handle the errors will depend on the type of failure, as wel will discuss later in this post.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;we need to make sure we can receive both failures and successes&lt;/li&gt;
&lt;li&gt;we need to make sure the pipeline doesn’t terminate if we receive a failure&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To achieve all of this, we will map the result of the &lt;code&gt;checkUserNameAvailablePublisher&lt;/code&gt; to a &lt;code&gt;Result&lt;/code&gt; type. &lt;code&gt;Result&lt;/code&gt; is an enum that can capture both &lt;code&gt;success&lt;/code&gt; and &lt;code&gt;failure&lt;/code&gt; states. Mapping the outcome of &lt;code&gt;checkUserNameAvailablePublisher&lt;/code&gt;to &lt;code&gt;Result&lt;/code&gt; also means the pipeline will no longer terminate in case it emits a failure.&lt;/p&gt;
&lt;p&gt;Let’s first define a typealias for the &lt;code&gt;Result&lt;/code&gt; type to make our life a little easier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typealias Available = Result&amp;lt;Bool, Error&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To turn the result of a publisher into a &lt;code&gt;Result&lt;/code&gt; type, we can use the following operator that John Sundell implemented in his article &lt;a href=&quot;https://www.swiftbysundell.com/articles/the-power-of-extensions-in-swift/#specializing-generics&quot;&gt;The power of extensions in Swift&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Publisher {
  func asResult() -&amp;gt; AnyPublisher&amp;lt;Result&amp;lt;Output, Failure&amp;gt;, Never&amp;gt; {
    self
      .map(Result.success)
      .catch { error in
        Just(.failure(error))
      }
      .eraseToAnyPublisher()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to update the &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; in our view model like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Available, Never&amp;gt; = {
  $username
    .debounce(for: 0.8, scheduler: DispatchQueue.main)
    .removeDuplicates()
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Available, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailablePublisher(userName: username)
        .asResult()
    }
    .receive(on: DispatchQueue.main)
    .share()
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this basic plumbing in place, let’s look at how to handle the different error scenarios I outlined earlier.&lt;/p&gt;
&lt;h3&gt;Handling Device/Network Offline Errors&lt;/h3&gt;
&lt;p&gt;On mobile devices it is pretty common to have spotty connectivity: especially when you’re on the move, you might be in an area with bad or no coverage.&lt;/p&gt;
&lt;p&gt;Whether or not you should show an error message depends on the situation:&lt;/p&gt;
&lt;p&gt;For our use case we can assume that the user at least has intermittent connectivity. Telling the user that we cannot reach the server would be rather distracting while they’re filling out the form. Instead, we should ignore any connectivity errors for the form validation (and instead run our local form validation logic).&lt;/p&gt;
&lt;p&gt;Once the user has entered all their details and submits the form, we should show an error message if the device is still offline.&lt;/p&gt;
&lt;p&gt;Catching this type of error requires us to make changes at two different places. First, in &lt;code&gt;checkUserNameAvailablePublisher&lt;/code&gt;, we use &lt;code&gt;mapError&lt;/code&gt; to catch any upstream errors and turn them into an &lt;code&gt;APIError&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum APIError: LocalizedError {
  /// Invalid request, e.g. invalid URL
  case invalidRequestError(String)

  /// Indicates an error on the transport layer, e.g. not being able to connect to the server
  case transportError(Error)
}

struct AuthenticationService {

  func checkUserNameAvailablePublisher(userName: String)
      -&amp;gt; AnyPublisher&amp;lt;Bool, Error&amp;gt; {
    guard let url = URL(string: &quot;http://127.0.0.1:8080/isUserNameAvailable?userName=\(userName)&quot;) else {
      return Fail(error: APIError.invalidRequestError(&quot;URL invalid&quot;))
        .eraseToAnyPublisher()
    }

    return URLSession.shared.dataTaskPublisher(for: url)
      .mapError { error -&amp;gt; Error in
        return APIError.transportError(error)
      }
      .map(\.data)
      .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
      .map(\.isAvailable)
      .eraseToAnyPublisher()
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in our view model, we map the result to detect if it was a &lt;code&gt;failure&lt;/code&gt; (1, 2). If so, we extract the error and check if it is a network transport error. If that’s the case, we return an empty string (3) to suppress the error message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignUpScreenViewModel: ObservableObject {

  // ...

  init() {
    isUsernameAvailablePublisher
      .map { result in
        switch result {
        case .failure(let error): //(1)
          if case APIError.transportError(_) = error {
            return &quot;&quot; //(3)
          }
          else {
            return error.localizedDescription
          }
        case .success(let isAvailable):
          return isAvailable ? &quot;&quot; : &quot;This username is not available&quot;
        }
      }
      .assign(to: &amp;amp;$usernameMessage) //(4)

    isUsernameAvailablePublisher
      .map { result in
        if case .failure(let error) = result { //(2)
          if case APIError.transportError(_) = error {
            return true
          }
          return false
        }
        if case .success(let isAvailable) = result {
          return isAvailable
        }
        return true
      }
      .assign(to: &amp;amp;$isValid) //(5)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In case &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; returned a &lt;code&gt;success&lt;/code&gt;, we extract the &lt;code&gt;Bool&lt;/code&gt; telling us whether or not the desired username is available, and map this to an appropriate message.&lt;/p&gt;
&lt;p&gt;And finally, we assign the result of the pipeline to the &lt;code&gt;usernameMessage&lt;/code&gt; (4) and &lt;code&gt;isValid&lt;/code&gt; (5) published properties which drive the UI on our view.&lt;/p&gt;
&lt;p&gt;Keep in mind that ignoring the network error is a viable option for this kind of UI - it might be an entirely different story for you use case, so use your own judgement when applying this technique.&lt;/p&gt;
&lt;p&gt;So far, we haven’t exposed any errors to the user, so let’s move on to a category of errors that we actually want to make the user aware of.&lt;/p&gt;
&lt;h2&gt;Handling Validation Errors&lt;/h2&gt;
&lt;p&gt;Most validation errors should be handled locally on the client, but sometimes we cannot avoid running some additional validation steps on the server. Ideally, the server should return a HTTP status code in the 4xx range, and optionally a payload that provides more details.&lt;/p&gt;
&lt;p&gt;In our example app, the server requires a minimum username length of four characters, and we have a list of usernames that are forbiden (such as “admin” or “superuser”).&lt;/p&gt;
&lt;p&gt;For these cases, we want to display a warning message and disable the submit button.&lt;/p&gt;
&lt;p&gt;Our backend implementatin is based on Vapor, and will respond with a HTTP status of 400 and an error payload for any validation errors. If you’re curious about the implementation, &lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Combine-Applied/blob/networking/handling-errors/Networking/server/Sources/App/routes.swift&quot;&gt;check out the code&lt;/a&gt; in &lt;code&gt;routes.swift&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Handling this error scenario requires us to make changes in two places: the service implementation and the view model. Let’s take a look at the service implementation first.&lt;/p&gt;
&lt;p&gt;Since we should handle any errors before even trying to extract the payload from the response, the code for handling server errors needs to run after checking for URLErrors and before mapping data:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct APIErrorMessage: Decodable {
  var error: Bool
  var reason: String
}

// ...

struct AuthenticationService {

  func checkUserNameAvailablePublisher(userName: String) -&amp;gt; AnyPublisher&amp;lt;Bool, Error&amp;gt; {
    guard let url = URL(string: &quot;http://127.0.0.1:8080/isUserNameAvailable?userName=\(userName)&quot;) else {
      return Fail(error: APIError.invalidRequestError(&quot;URL invalid&quot;))
        .eraseToAnyPublisher()
    }

    return URLSession.shared.dataTaskPublisher(for: url)
      // handle URL errors (most likely not able to connect to the server)
      .mapError { error -&amp;gt; Error in
        return APIError.transportError(error)
      }

      // handle all other errors
      .tryMap { (data, response) -&amp;gt; (data: Data, response: URLResponse) in
        print(&quot;Received response from server, now checking status code&quot;)

        guard let urlResponse = response as? HTTPURLResponse else {
          throw APIError.invalidResponse //(1)
        }

        if (200..&amp;lt;300) ~=  urlResponse.statusCode { //(2)
        }
        else {
          let decoder = JSONDecoder()
          let apiError = try decoder.decode(APIErrorMessage.self, from: data) //(3)

          if urlResponse.statusCode == 400 { //(4)
            throw APIError.validationError(apiError.reason)
          }
        }
        return (data, response)
      }

      .map(\.data)
      .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
      .map(\.isAvailable)
//      .replaceError(with: false)
      .eraseToAnyPublisher()
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s take a closer look at what the code in this snippet does:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the response isn’t a &lt;code&gt;HTTPURLResonse&lt;/code&gt;, we return &lt;code&gt;APIError.invalidResponse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We use Swift’s pattern matching to detect if the request was executed successfully, i.e., with a HTTP status code in the range of &lt;code&gt;200&lt;/code&gt; to &lt;code&gt;299&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Otherwise, some error occurred on the server. Since we use Vapor, the server will &lt;a href=&quot;https://docs.vapor.codes/4.0/errors/&quot;&gt;return details about the error in a JSON payload&lt;/a&gt;, so we can now map this information to an &lt;code&gt;APIErrorMessage&lt;/code&gt; struct and use it to create more meaningful error message in the following code&lt;/li&gt;
&lt;li&gt;If the server returns a HTTP status of &lt;code&gt;400&lt;/code&gt;, we know that this is a validation error (see the server implementation for details), and return an &lt;code&gt;APIError.validationError&lt;/code&gt; including the detailed error message we received from the server&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the view model, we can now use this information to tell the user that their chosen username doesn’t meet the requirements:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;init() {
  isUsernameAvailablePublisher
    .map { result in
      switch result {
      case .failure(let error):
        if case APIError.transportError(_) = error {
          return &quot;&quot;
        }
        else if case APIError.validationError(let reason) = error {
          return reason
        }
        else {
          return error.localizedDescription
        }
      case .success(let isAvailable):
        return isAvailable ? &quot;&quot; : &quot;This username is not available&quot;
      }
    }
    .assign(to: &amp;amp;$usernameMessage)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s right - just three lines of code. We’ve already done all the hard work, so it’s time to reap the benefits 🎉&lt;/p&gt;
&lt;h3&gt;Handling Response Parsing Errors&lt;/h3&gt;
&lt;p&gt;There are many situations in which the data sent by the server doesn’t match what the client expected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the response includes additional data, or some fieds were renamed&lt;/li&gt;
&lt;li&gt;the client is connecting via a captive portal (e.g. in a hotel)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In these cases, the client receives data, but it’s in the wrong format. To help the user resolve the situation, we’ll need to analyse the response and then provide suitable guidance, for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;download the latest version of the app&lt;/li&gt;
&lt;li&gt;sign in to the captive portal via the system browser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The current implementation uses the &lt;code&gt;decode&lt;/code&gt; operator to decode the response payload and throw an error in case the payload couldn&apos;t be mapped. This works well, and any decoding error will be caught and show on the UI. However, an error message like &lt;em&gt;The data couldn&apos;t be read because it is missing&lt;/em&gt; isn’t really user friendly. Instead, let&apos;s try to show a message that is a little bit more meaningful for users, and also suggest to upgrade to the latest version of the app (assuming the server is returning additional data that the new app will be able to leverage).&lt;/p&gt;
&lt;p&gt;To be able to provide more fine-grained informtion about decoding errors, we need to part ways with the &lt;code&gt;decode&lt;/code&gt; operator and fall back to manually mapping the data (don&apos;t worry, thanks to &lt;code&gt;JSONDecoder&lt;/code&gt; and Swift’s &lt;code&gt;Codable&lt;/code&gt; protocol, this is pretty straighforward):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...
.map(\.data)
// .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
.tryMap { data -&amp;gt; UserNameAvailableMessage in
  let decoder = JSONDecoder()
  do {
    return try decoder.decode(UserNameAvailableMessage.self,
                              from: data)
  }
  catch {
    throw APIError.decodingError(error)
  }
}
.map(\.isAvailable)
// ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By conforming &lt;code&gt;APIError&lt;/code&gt; to &lt;code&gt;LocalizedError&lt;/code&gt; and implementing the &lt;code&gt;errorDescription&lt;/code&gt; property, we can provide a more user-friendly error message (I included custom messages for the other error conditions as well):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum APIError: LocalizedError {
  /// Invalid request, e.g. invalid URL
  case invalidRequestError(String)

  /// Indicates an error on the transport layer, e.g. not being able to connect to the server
  case transportError(Error)

  /// Received an invalid response, e.g. non-HTTP result
  case invalidResponse

  /// Server-side validation error
  case validationError(String)

  /// The server sent data in an unexpected format
  case decodingError(Error)

  var errorDescription: String? {
    switch self {
    case .invalidRequestError(let message):
      return &quot;Invalid request: \(message)&quot;
    case .transportError(let error):
      return &quot;Transport error: \(error)&quot;
    case .invalidResponse:
      return &quot;Invalid response&quot;
    case .validationError(let reason):
      return &quot;Validation Error: \(reason)&quot;
    case .decodingError:
      return &quot;The server returned data in an unexpected format. Try updating the app.&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, to make it abundandly clear to the user that they should update the app, we will also display an alert. Here is the code for the alert:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct SignUpScreen: View {
  @StateObject private var viewModel = SignUpScreenViewModel()

  var body: some View {
    Form {
      // ...
    }

    // show update dialog
    .alert(&quot;Please update&quot;, isPresented: $viewModel.showUpdateDialog, actions: {
      Button(&quot;Upgrade&quot;) {
        // open App Store listing page for the app
      }
      Button(&quot;Not now&quot;, role: .cancel) { }
    }, message: {
      Text(&quot;It looks like you&apos;re using an older version of this app. Please update your app.&quot;)
    })

  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll notice that the presentation state of this alert is driven by a published property on the view model, &lt;code&gt;showUpdateDialog&lt;/code&gt;. Let&apos;s update the view model accordingly (1), and also add the Combine pipeline that maps the results of &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; to this new property:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignUpScreenViewModel: ObservableObject {
  // ...

  @Published var showUpdateDialog: Bool = false //(1)

  // ...

  private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Available, Never&amp;gt; = {
    $username
      .debounce(for: 0.8, scheduler: DispatchQueue.main)
      .removeDuplicates()
      .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Available, Never&amp;gt; in
        self.authenticationService.checkUserNameAvailablePublisher(userName: username)
          .asResult()
      }
      .receive(on: DispatchQueue.main)
      .share() //(3)
      .eraseToAnyPublisher()
  }()

  init() {
    // ...

    // decoding error: display an error message suggesting to download a newer version
    isUsernameAvailablePublisher
      .map { result in
        if case .failure(let error) = result {
          if case APIError.decodingError = error  { //(2)
            return true
          }
        }
        return false
      }
      .assign(to: &amp;amp;$showUpdateDialog)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, nothing too fancy - we essentially just take any events coming in from the &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; and convert them into a &lt;code&gt;Bool&lt;/code&gt; that only becomes &lt;code&gt;true&lt;/code&gt; if we receive a &lt;code&gt;.decodingError&lt;/code&gt; (2).&lt;/p&gt;
&lt;p&gt;We’re now using &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; to drive three different Combine pipelines, and I would like to explicitly call out that - since &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; eventually will cause a network request to be fired - it is important to make sure we&apos;re only sending &lt;em&gt;at most&lt;/em&gt; one network request per keystroke. The &lt;a href=&quot;https://peterfriese.dev/posts/swiftui-combine-networking-efficient/&quot;&gt;previous post&lt;/a&gt; in this series explains how to do this in depth, but it&apos;s worth calling out that using &lt;code&gt;.share()&lt;/code&gt; (3) plays a key role.&lt;/p&gt;
&lt;h2&gt;Handling Internal Server Errors&lt;/h2&gt;
&lt;p&gt;In some rare cases, the backend of our app might be having some issues - maybe part of the system is offline for maintenance, some process died, or the server is overwhelmed. Usually, servers will return a HTTP status code in the 5xx range to indicate this.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Simulating error conditions&lt;/strong&gt; The sample server includes code that simulates some of the error
conditions discussed in this article. You can trigger the error conditions by sending specific
&lt;code&gt;username&lt;/code&gt; values: - Any username with less than 4 characters will result in a &lt;code&gt;tooshort&lt;/code&gt;
validation error, signalled via a HTTP 400 status code and a JSON payload containing a detailed
error message. - An empty username will result in a &lt;code&gt;emptyName&lt;/code&gt; error message, indicating the
username mustn’t be empty. - Some usernames are forbidden: &quot;admin&quot; or &quot;superuser&quot; will result in
an &lt;code&gt;illegalName&lt;/code&gt; validation error. - Other usernames such as “peterfriese”, “johnnyappleseed”,
“page”, and “johndoe” are already taken, so the server will tell the client these aren’t available
any more. - Sending “illegalresponse” as the username will return a JSON response that has too few
fields, resulting in a decoding error on the client. - Sending “servererror” will simulate a
database problem (&lt;code&gt;databaseCorrupted&lt;/code&gt;), and will be signalled as a HTTP 500 with no retry hint (as
we assume that this is not a temporary situation, and retrying would be futile). - Sending
“maintenance” as the username will return a &lt;code&gt;maintenance&lt;/code&gt; error, along with a &lt;code&gt;retry-after&lt;/code&gt; header
that indicates the client can retry this call after a period of time (the idea here is that the
server is undergoing scheduled maintenance and will be back up after rebooting).
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;Let’s add the code required to deal with server-side errors. As we did for previous error scenarios, we need to add some code to map the HTTP status code to our &lt;code&gt;APIError&lt;/code&gt; enum:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (200..&amp;lt;300) ~= urlResponse.statusCode {
}
else {
  let decoder = JSONDecoder()
  let apiError = try decoder.decode(APIErrorMessage.self, from: data)

  if urlResponse.statusCode == 400 {
    throw APIError.validationError(apiError.reason)
  }

  if (500..&amp;lt;600) ~= urlResponse.statusCode {
    let retryAfter = urlResponse.value(forHTTPHeaderField: &quot;Retry-After&quot;)
    throw APIError.serverError(statusCode: urlResponse.statusCode,
                               reason: apiError.reason,
                               retryAfter: retryAfter)
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To display a user-friendly error messge in our UI, all we need to do is add a few lines of code to the view model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;isUsernameAvailablePublisher
  .map { result in
    switch result {
    case .failure(let error):
      if case APIError.transportError(_) = error {
        return &quot;&quot;
      }
      else if case APIError.validationError(let reason) = error {
        return reason
      }
      else if case APIError.serverError(statusCode: _, reason: let reason, retryAfter: _) = error {
        return reason ?? &quot;Server error&quot;
      }
      else {
        return error.localizedDescription
      }
    case .success(let isAvailable):
      return isAvailable ? &quot;&quot; : &quot;This username is not available&quot;
    }
  }
  .assign(to: &amp;amp;$usernameMessage)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far, so good.&lt;/p&gt;
&lt;p&gt;For some of the server-side error scenarios, it might be worthwhile to retry the request after a short while. For a example, if the server underwent maintenance, it might be back up again after a few seconds.&lt;/p&gt;
&lt;p&gt;Combine includes a &lt;code&gt;retry&lt;/code&gt; operator that we can use to automatically retry any failing operation. Adding it to our code is a simple one-liner:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return URLSession.shared.dataTaskPublisher(for: url)
  .mapError { ... }
  .tryMap { ... }
  .retry(3)
  .map(\.data)
  .tryMap { ... }
  .map(\.isAvailable)
  .eraseToAnyPublisher()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, as you will notice when you run the app, this will result in &lt;em&gt;any&lt;/em&gt; failed request to be retried three times. This is not what we want - for example, we want any verification errors to bubble up to the view model. Instead, they will be captured by the retry operator as well.&lt;/p&gt;
&lt;p&gt;What&apos;s more, there is no pause between retries. If our goal was to reduce the pressure on a server that is already overwhelmed, we&apos;ve made it even worse by sending not one, but four requests (the original request, plus three retries).&lt;/p&gt;
&lt;p&gt;So how can we make sure that&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We only retry certain types of failiures?&lt;/li&gt;
&lt;li&gt;There is a pause before we retry a failed request?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Our implementation needs to be able to catch any upstream errors, and propagate them down the pipeline to the next operator. When we catch a &lt;code&gt;serverError&lt;/code&gt;, however, we want to pause for a moment, and them start the entire pipeline again so it can retry the URL request.&lt;/p&gt;
&lt;p&gt;Let’s first make sure we can (1) catch all errors, (2) filter out the &lt;code&gt;serverError&lt;/code&gt;, and (3) propagate all other errors along the pipeline. The &lt;code&gt;tryCatch&lt;/code&gt; operator “handles errors from an upstream publisher by either replacing it with another publisher or throwing a new error”. This is exactly what we need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return URLSession.shared.dataTaskPublisher(for: url)
  .mapError { ... }
  .tryMap { ... }
  .tryCatch { error -&amp;gt; AnyPublisher&amp;lt;(data: Data, response: URLResponse), Error&amp;gt; in //(1)
    if case APIError.serverError(_, _, let retryAfter) = error { //(2)
      // ...
    }
    throw error //(3)
  }
  .map(\.data)
  .tryMap { ... }
  .map(\.isAvailable)
  .eraseToAnyPublisher()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we caught a &lt;code&gt;serverError&lt;/code&gt;, we want to wait for a short amount of time, and then restart the pipeline.&lt;/p&gt;
&lt;p&gt;We can do this by firing off a new event (using the &lt;code&gt;Just&lt;/code&gt; publisher), &lt;code&gt;delay&lt;/code&gt;ing it for a few seconds, and then using &lt;code&gt;flatMap&lt;/code&gt; to kick off a new &lt;code&gt;dataTaskPublisher&lt;/code&gt;. Instead of pasting the entire code for the pipeline inside the &lt;code&gt;if&lt;/code&gt; statement, we assign the &lt;code&gt;dataTaskPublisher&lt;/code&gt; to a local variable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let dataTaskPublisher = URLSession.shared.dataTaskPublisher(for: url)
  .mapError { ... }
  .tryMap { ... }

return dataTaskPublisher
  .tryCatch { error -&amp;gt; AnyPublisher&amp;lt;(data: Data, response: URLResponse), Error&amp;gt; in
    if case APIError.serverError = error {
      return Just(()) //(1)
        .delay(for: 3, scheduler: DispatchQueue.global())
        .flatMap { _ in
          return dataTaskPublisher
        }
        .retry(10) //(2)
        .eraseToAnyPublisher()
    }
    throw error
  }
  .map(\.data)
  .tryMap { ... }
  .map(\.isAvailable)
  .eraseToAnyPublisher()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A couple of notes about this code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;Just&lt;/code&gt; publisher expects &lt;em&gt;some&lt;/em&gt; value it can publish. Since it really doesn&apos;t matter which value we use, we can send anything we want. I decided to send an empty tuple, which is often used in situations when you mean “nothing”.&lt;/li&gt;
&lt;li&gt;We retry sending the request 10 times, meaning it will be sent up to 11 times in total (the original call plus the 10 retries).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The only reason why this number is so high is to make it easier to see that the pipeline comes to an end as soon as the server returns a successful result. The demo server can simulate recovering from scheduled maintenance when you send &lt;em&gt;maintenance&lt;/em&gt; as the username: it will throw &lt;code&gt;InternalServerError.maintenance&lt;/code&gt; (which is mapped to HTTP 500) for every first and second request. Every third request, it will return a &lt;code&gt;success&lt;/code&gt; (i.e. HTTP &lt;code&gt;200&lt;/code&gt;). The best way to see this in action is to run the server from inside Xcode (run open the &lt;code&gt;server&lt;/code&gt; project and press the &lt;em&gt;Run&lt;/em&gt; button). Then, create a &lt;em&gt;Sound&lt;/em&gt; breakpoint for the line that contains &lt;code&gt;throw InternalServerError.maintenance:&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Everytime the server receives a request for &lt;code&gt;username=maintenace&lt;/code&gt;, you will hear a sound. Now, run the sample app and enter &lt;em&gt;maintenance&lt;/em&gt; as the username. You will hear the server responding with an error two times, before it will return a success.&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;After using a rather lenient approach to handle errors in the recent episode of this series, we took things a lot more serious this time around.&lt;/p&gt;
&lt;p&gt;In this episode, we used a couple of strategies to handle errors and expose them to the UI. Error handling is an important aspect of developer quality software, and there is a lot of material out there. However, the aspect of how to expose erorrs to the user isn’t often discussed, and I hope this article provided you with a better understanding of how you can achieve this.&lt;/p&gt;
&lt;p&gt;In comparison to the original code, the code became a bit more complicated, and this is something we’re going to address in the next episode when we will look at implementing your own Combine operators. To demonstrate how this works, we will implement an operator that makes handling incremental backoff as easy as adding one line to your Combine pipeline!&lt;/p&gt;
&lt;p&gt;Thanks for reading 🔥&lt;/p&gt;
</content:encoded></item><item><title>Calling asynchronous Firebase APIs from Swift</title><link>https://peterfriese.dev/blog/2022/firebase-async-calls-swift/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2022/firebase-async-calls-swift/</guid><description>Learn the correct way to call Firebase&apos;s APIs from Swift</description><pubDate>Mon, 31 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;Most of Firebase’s APIs are asynchronous.&lt;/p&gt;
&lt;p&gt;This might be confusing at first: you’re making a call, for example to fetch some data from Firestore, but you don’t get a result back.&lt;/p&gt;
&lt;p&gt;Why does this happen, what does “asynchronous API” mean, and how do you even call asynchronous APIs in Swift?&lt;/p&gt;
&lt;p&gt;If you’ve been asking yourself these questions, you’re not alone - quite literally, these are some of the most frequently asked questions about Firebase on StackOverflow and on our GitHub repos.&lt;/p&gt;
&lt;p&gt;In this post, I am going to explain what this all means, and show you three easy ways to call Firebase’s APIs asynchronously from your Swift / SwiftUI app.&lt;/p&gt;
&lt;h2&gt;“Can I please get a skinny latte, extra-hot”&lt;/h2&gt;
&lt;p&gt;To understand the nature of asynchronous APIs, let’s imagine we’re at a coffee bar, and you’ve just placed your order. The barista has just turned around to get the skimmed milk and starts preparing your drink.&lt;/p&gt;
&lt;p&gt;While you’re waiting for your drink, you could either chat to another person in the queue, or check your phone for any important news. So you turn around to me to strike up a conversation about the weather or the latest rumours about that M2 MacBook.&lt;/p&gt;
&lt;p&gt;Once the barista has finished preparing your drink, they hand it over to you:&lt;/p&gt;
&lt;p&gt;“Anything else?”&lt;/p&gt;
&lt;p&gt;“No, thanks”&lt;/p&gt;
&lt;p&gt;“That’ll be 2.49 then”&lt;/p&gt;
&lt;p&gt;You pay, and the barista turns to the next person.&lt;/p&gt;
&lt;p&gt;We’ve just experienced an asynchronous process: while the barista was busy preparing your drink, you didn’t have to stand still, holding your breath. That would’ve been pretty uncomfortable indeed. No, you were able to breathe normally, and even have a conversation with me.&lt;/p&gt;
&lt;h2&gt;Asynchronous APIs&lt;/h2&gt;
&lt;p&gt;The same applies to our apps: we don’t want the foreground process (which drives UI updates) to freeze while the app is waiting for the server to return the result of a network request. Maybe you&apos;ve experienced this in some apps before, and lost your patience after a few seconds. When this happens, users usually either leave or kill the app. And we don’t want that to happen.&lt;/p&gt;
&lt;p&gt;This is why many APIs that might take a bit longer are implemented asynchronously: you call them, and they immediately return control back to the caller while they start their own processing in the background. Once they finish, they will call back to the original caller. Just like in the example with the coffee bar.&lt;/p&gt;
&lt;p&gt;Now, in a coffee bar, the barista knows who they need to get back to, as they either took your name, or they remember your face, or you’re still standing at the counter.&lt;/p&gt;
&lt;p&gt;But how does this work in our apps? Let’s look at three different ways how you can call asynchronous APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;callbacks&lt;/li&gt;
&lt;li&gt;Combine&lt;/li&gt;
&lt;li&gt;async/wait&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using callbacks to call asynchronous APIs&lt;/h2&gt;
&lt;p&gt;Callbacks are probably the most commonly used way to implement asynchronous APIs, and you very likely already used them in your code. They’ve been around since the days of Objective-C (when they were called &lt;em&gt;completion blocks)&lt;/em&gt;. In Swift, we use closures to implement callbacks.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TextField(&quot;Email&quot;, text: $text)
  .onChange(of: self.text, perform: { value in 
    print(value)
  })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swift has always supported trailing closures, making it even more elegant to use callbacks in Swift. Here’s how the above code looks like when using trailing closure syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TextField(&quot;Email&quot;, text: $text)
  .onChange(of: self.text) { value in
    print(value)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how we were able to remove the parameter label and move the closing parenthesis right after the first parameter. This makes the code much easier to read, especially for closures that span several lines.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Internal DSLs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Being able to write code like this almost makes the function call look like it’s part of the language. The use of trailing closures is one of the key enablers for SwiftUIs DSL, and the reason why it feels so natural for writing UI code.
SwiftUI is an internal DSL - it is written in the host language, making it easier to integrate in existing toolchains, and easier to learn for developers who are already used to the host language.
For more details, see Martin Fowler’s article on &lt;a href=&quot;https://martinfowler.com/bliki/InternalDslStyle.html&quot;&gt;internal DSLs&lt;/a&gt;.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To see this in action, let’s look at one of Firebase’s APIs. Here is a call to &lt;code&gt;signIn(withEmail:password:)&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func signIn() {
  print(&quot;Executed before calling .signIn()&quot;) //(2)
  Auth.auth().signIn(withEmail: email, password: password) { authDataResult, error in  //(1)
    print(&quot;Executed once .signIn() finishes, and the closure is called&quot;) //(4)
    if let error = error {
      print(&quot;There was an issue when trying to sign in: \(error)&quot;)
      self.errorMessage = error.localizedDescription
      return
    }
    
    guard let user = authDataResult?.user else {
      print(&quot;No user&quot;)
      self.errorMessage = &quot;User doesn&apos;t exist.&quot;
      return
    }
    
    print(&quot;Signed in as user \(user.uid), with email: \(user.email ?? &quot;&quot;)&quot;)
    self.isSignedIn = true
  }
  print(&quot;Executed immediately after calling .signIn()&quot;) //(3)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s discuss some of the key aspects of this code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In this code snippet, I use a trailing closure to call &lt;code&gt;signIn(withEmail:password:)&lt;/code&gt;. If you look at the method signature, you will notice there is a third parameter, &lt;code&gt;completion:&lt;/code&gt; . Thanks to Swift&apos;s trailing closure syntax, we can move this parameter out of the call signature and append the closure at the end of the call, outside of the parenthesis. This makes it more fluent, and pleasing to the eye.&lt;/li&gt;
&lt;li&gt;When running the code, the console output will look like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;Executed before calling .signIn()
Executed immediately after calling .signIn()
Executed once .signIn() finishes, and the closure is called
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Once the print statement at the end of the function has been executed, the function will be left. If you try to access &lt;code&gt;self.isSignedIn&lt;/code&gt; at this moment, it will still be &lt;code&gt;false&lt;/code&gt;. Only once the user has completed the sign-in flow, and the closure has been called, the property &lt;code&gt;isSignedIn&lt;/code&gt; will be &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Calling asynchronous APIs using Combine&lt;/h2&gt;
&lt;p&gt;Combine is Apple’s reactive framework for handling asynchronous events. It provides a declarative API for describing how events (such as user input or network responses) should be handled.&lt;/p&gt;
&lt;p&gt;Firebase supports Combine for some of its key APIs (such as Firebase Authentication, Cloud Firestore, Cloud Functions, and Cloud Storage).&lt;/p&gt;
&lt;p&gt;To use Combine for Firebase, add the respective module to your target (e.g. &lt;code&gt;FirebaseAuthCombine-Community&lt;/code&gt;) and import it.&lt;/p&gt;
&lt;p&gt;Here’s how you can use Combine to call the &lt;code&gt;signIn(withEmail:password:)&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import FirebaseAuthCombineSwift

// ...

func signIn() {
  Auth.auth().signIn(withEmail: email, password: password) //(1)
    .map { $0.user } //(2)
    .replaceError(with: nil) //(3)
    .print(&quot;User signed in&quot;)
    .map { $0 != nil } //(4)
    .assign(to: &amp;amp;$isSignedIn) //(5)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how the first part of the call is virtually the same as the one we used in the previous section. This is on purpose, to make it easier to switch from one way of calling Firebase’s asynchronous APIs to another.&lt;/p&gt;
&lt;p&gt;In the next steps, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extract the &lt;code&gt;user&lt;/code&gt; object using Combine&apos;s &lt;code&gt;map&lt;/code&gt; operator (2)&lt;/li&gt;
&lt;li&gt;handle errors by replacing them with a &lt;code&gt;nil&lt;/code&gt; value (3)&lt;/li&gt;
&lt;li&gt;check if the value is nil, and return &lt;code&gt;true&lt;/code&gt; if the user object is set (4)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, we assign the result (a &lt;code&gt;Bool&lt;/code&gt; indicating whether the user has successfully signed in) to the &lt;code&gt;isSignedIn&lt;/code&gt; property of our view model (5). As this is a publisher property, assigning a value to it will trigger SwiftUI to redraw the UI.&lt;/p&gt;
&lt;p&gt;This code is much more compact and concise than the one we had to write when using callbacks. Instead of having to telling Swift &lt;em&gt;how&lt;/em&gt; to process the network request and its response, Combine&apos;s declarative programming model allows us to describe &lt;em&gt;what&lt;/em&gt; we want to do.&lt;/p&gt;
&lt;p&gt;Using a declarative framework for describing the data flow in your app (Combine) aligns nicely with using a declarative framework for describing the UI of your app (SwiftUI).&lt;/p&gt;
&lt;h2&gt;Calling APIs asynchronously using async/await&lt;/h2&gt;
&lt;p&gt;The final (and probably most elegant) way to call asynchronous APIs is &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; . This is a new language feature introduced in Swift 5.5 that allows us to call asynchronous code and &lt;em&gt;suspend&lt;/em&gt; the current thread until the called code returns.&lt;/p&gt;
&lt;p&gt;Let’s see how we can call &lt;code&gt;signIn(withEmail:password:)&lt;/code&gt; using async/await:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@MainActor //(5)
func signIn() async { //(2)
  do {
    let authDataResult = try await //(1) 
      Auth.auth().signIn(withEmail: email, password: password) //(3)
    let user = authDataResult.user
    
    print(&quot;Signed in as user \(user.uid), with email: \(user.email ?? &quot;&quot;)&quot;)
    self.isSignedIn = true
  }
  catch { //(4)
    print(&quot;There was an issue when trying to sign in: \(error)&quot;)
    self.errorMessage = error.localizedDescription
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;We need to prefix the call to &lt;code&gt;signIn(withEmail:password:)&lt;/code&gt; with &lt;code&gt;await&lt;/code&gt; to indicate we want to call this method asynchronously. Notice that we don&apos;t have to provide a closure - using &lt;code&gt;await&lt;/code&gt; will suspend the thread and resume execution once the user has signed in our the process has failed for some reason. While the thread is suspended, the app&apos;s foreground thread continues to handle events, so the app will not freeze.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;await&lt;/code&gt; makes our function asynchronous. To let callers know about this, we need to mark it with the &lt;code&gt;async&lt;/code&gt; keyword. The compiler will make sure that this function is only called from another asynchronous context. More about this in a moment.&lt;/li&gt;
&lt;li&gt;Since the call to &lt;code&gt;signIn(withEmail:password:)&lt;/code&gt; can throw an exception, we need to wrap the entire call in a &lt;code&gt;do/try/catch&lt;/code&gt; block (3, 4).&lt;/li&gt;
&lt;li&gt;When assigning the result of the call to the &lt;code&gt;isSignedIn&lt;/code&gt; property on our view model, we need to make sure this happens on the main thread. Instead of wrapping this assignment in a call to &lt;code&gt;DispatchQueue.main.async { }&lt;/code&gt;, we can use the &lt;code&gt;@MainActor&lt;/code&gt; attribute to make sure the entire function is being executed on the main thread (5).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The good news is that you can use async/await in your apps targeting iOS 13 and up - see the &lt;a href=&quot;https://developer.apple.com/documentation/xcode-release-notes/xcode-13_2-release-notes#Swift&quot;&gt;release notes&lt;/a&gt; for Xcode 13.2:&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
You can now use Swift Concurrency in applications that deploy to macOS Catalina 10.15, iOS 13, tvOS 13, and watchOS 6 or newer. This support includes &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, actors, global actors, structured concurrency, and the task APIs. (70738378)
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;Almost all of Firebase’s asynchronous calls are ready for async/await, with a few exceptions that we’re currently fixing. Should you run into any method that you can’t seem to call using async/await, check out our &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk/issues&quot;&gt;issue tracker&lt;/a&gt; to see if we’re working on it already. If not, file an issue and we will look into it.&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;Making sure our apps run snappy and perform smoothly even when performing heavy duty tasks on the network is a top priority for us, no matter which platforms we’re targeting.&lt;/p&gt;
&lt;p&gt;By providing asynchronous APIs, SDKs like Firebase and others ensure that developers can rely on a coherent and consistent programming model. This allows developers to focus on what they care about most: delivering value to their users and inspiring them with great experiences and snappy UIs.&lt;/p&gt;
&lt;p&gt;In this post, I’ve walked you through the three most common ways you can use to access Firebase APIs asynchronously on Apple’s platforms.&lt;/p&gt;
&lt;p&gt;As a rule of thumb, check the signature of the method you want to call. If its last parameter is a completion handler (most commonly named &lt;code&gt;completion&lt;/code&gt;), you&apos;re dealing with an asynchronous method that you can call with any one of the techniques describer in this article.&lt;/p&gt;
&lt;p&gt;If you&apos;re curious how this works in other languages, check out Doug&apos;s article: &lt;a href=&quot;https://medium.com/p/e037a6654a93&quot;&gt;Why are the Firebase API asynchronous?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading! 🔥&lt;/p&gt;
</content:encoded></item><item><title>Optimise your networking layer with Combine</title><link>https://peterfriese.dev/blog/2022/swiftui-combine-networking-efficient/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2022/swiftui-combine-networking-efficient/</guid><description>Learn how to use Combine to make your networking layer up to 10x more efficient!</description><pubDate>Mon, 24 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;With high-speed, low-latency internet being available in most places, it is easy to forget that not all of our users might be on a fast, low latency uplink when they’re using our apps. You don’t even have to go to some remote place to experience patchy and unreliable connectivity. I live in Hamburg, Germany, and even though high speed mobile internet is ubiquitous, there are quite a few spots along some of the main overground transport links that have no or very bad connectivity.&lt;/p&gt;
&lt;p&gt;When building apps that access the internet, we should be mindful of this, and make sure we don’t waste bandwidth.&lt;/p&gt;
&lt;p&gt;In this part of our series, I want to focus on how we can optimise network access in our apps when using Combine.&lt;/p&gt;
&lt;h2&gt;Previously…&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;../swiftui-combine-networking-gettingstarted&quot;&gt;Last time&lt;/a&gt;, we created a Combine pipeline for checking the availability of the username the user chose, and connected this pipeline to a simple sign-in form written in SwiftUI.&lt;/p&gt;
&lt;p&gt;When running the app and inspecting the logs of our test server, we noticed that the &lt;code&gt;isUserNameAvailable&lt;/code&gt; endpoint got called multiple times for each character typed. This is clearly not ideal: not only does it waste CPU cycles on our server (which might become an issues if you&apos;re hosting your server with a cloud provider that charges you by the number of calls or CPU uptime); it also means we&apos;re adding extra network overhead to our application.&lt;/p&gt;
&lt;p&gt;You might not notice this when running the test server locally, but you will notice it when you&apos;re on an Edge connection, talking to a remote instance of your server.&lt;/p&gt;
&lt;p&gt;The problem gets worse if your API endpoints aren’t idempotent: imagine calling an API endpoint for reserving a seat or buying a concert ticket. By sending two (or more) requests instead of one, you would end up reserving more seats than you require, or buying more concert tickets than you wanted.&lt;/p&gt;
&lt;p&gt;So - how can we fix this?&lt;/p&gt;
&lt;h2&gt;Identifying the root cause&lt;/h2&gt;
&lt;p&gt;Well, first of all we need to find out what’s causing all those extra requests.&lt;/p&gt;
&lt;p&gt;An easy way to figure out what’s going on with a Combine pipeline is to add some debugging code. Let&apos;s add the &lt;code&gt;print()&lt;/code&gt; operator to the pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .print(&quot;username&quot;)
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailable(userName: username)
    }
    .receive(on: DispatchQueue.main)
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This operator logs a couple of useful things to the console:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Any life cycle events of the pipeline (e.g. subscriptions being added)&lt;/li&gt;
&lt;li&gt;Any values being sent / received&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can specify a prefix (&lt;code&gt;”username”&lt;/code&gt;) to make the log statements stand out on the console.&lt;/p&gt;
&lt;p&gt;Running the app again, we immediately see the following output - even without typing anything into the text field:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;username: receive subscription: (PublishedSubject)
username: request unlimited
username: receive value: ()
username: receive subscription: (PublishedSubject)
username: request unlimited
username: receive value: ()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This indicates we’ve got &lt;em&gt;two&lt;/em&gt; subscribers for our pipeline!&lt;/p&gt;
&lt;p&gt;Looking at our code, we can spot those subscribers in the initialiser of the view model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;init() {
  isUsernameAvailablePublisher
    .assign(to: &amp;amp;$isValid)
  
  isUsernameAvailablePublisher
    .map { $0 ? &quot;&quot; : &quot;Username not available. Try a different one.&quot;}
    .assign(to: &amp;amp;$usernameMessage)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first subscriber is the pipeline that feeds the &lt;code&gt;isValid&lt;/code&gt; property, which we ultimately use to enable / disable the submit button on the sign in form.&lt;/p&gt;
&lt;p&gt;The second subscriber is the pipeline that produces an error message in case the chosen username is not available. The result of this pipeline will be displayed on the sign in form as well.&lt;/p&gt;
&lt;p&gt;Now that we’ve identified what’s causing multiple subscriptions to our publisher, let’s see what we can do to use only one subscriber.&lt;/p&gt;
&lt;h2&gt;Using the &lt;code&gt;share&lt;/code&gt; operator to share a publisher&lt;/h2&gt;
&lt;p&gt;Having multiple subscribers for a single publisher is a common pattern, especially in UIs, where a single UI element might have an impact on multiple other elements.&lt;/p&gt;
&lt;p&gt;If you need to share the results of a publisher with multiple subscribers, you can use the &lt;code&gt;share()&lt;/code&gt; operator. According to Apple&apos;s documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The publisher returned by this operator supports multiple subscribers, all of whom receive unchanged elements and completion states from the upstream publisher.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is exactly what we need. By applying the &lt;code&gt;share&lt;/code&gt; operator to the end of the pipeline in &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt;, we share the result of the pipeline for each event (i.e., each character the user enters in the username input field) with all subscribers of the publisher:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .print(&quot;username&quot;)
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailable(userName: username)
    }
    .receive(on: DispatchQueue.main)
    .share()
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When running the updated code, we can see that the &lt;code&gt;$username&lt;/code&gt; publisher no longer has two subscribers, but instead just one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;username: receive subscription: (PublishedSubject)
username: request unlimited
username: receive value: ()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, you might be wondering why it’s only one subscriber, since we clearly still have two published properties (&lt;code&gt;isValid&lt;/code&gt; and &lt;code&gt;usernameMessage&lt;/code&gt;) subscribed to the pipeline.&lt;/p&gt;
&lt;p&gt;Well, the answer is simple: the &lt;code&gt;share&lt;/code&gt; operator ultimately is this one subscriber, and it in turn is being subscribed to by &lt;code&gt;isValid&lt;/code&gt; and &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt;. To prove this, let&apos;s add another &lt;code&gt;print()&lt;/code&gt; operator to the pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .print(&quot;username&quot;)
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailable(userName: username)
    }
    .receive(on: DispatchQueue.main)
    .share()
    .print(&quot;share&quot;)
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the resulting output, we can see that &lt;code&gt;share&lt;/code&gt; receives two subscriptions (1, 2), and &lt;code&gt;username&lt;/code&gt; just one (3):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;share: receive subscription: (Multicast) //(1)
share: request unlimited
username: receive subscription: (PublishedSubject) //(3)
username: request unlimited
username: receive value: ()
share: receive subscription: (Multicast) //(2)
share: request unlimited
share: receive value: (true)
share: receive value: (true)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can think of &lt;code&gt;share()&lt;/code&gt; as a fork that receives events from its upstream publisher and multicasts them to all of its subscribers.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Is it a bug or a feature?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Go ahead and type a few characters into the username field, and you will find that for every character you type you will still see two requests being made to the server.
This might be an issue in iOS 15 - I debugged into this a bit, and it seems like &lt;code&gt;TextField&lt;/code&gt; emits every keystroke twice. In prior versions of iOS, this wasn&apos;t the case, and I am inclined to think this is a bug in iOS 15, so I created a sample project to reproduce this issue (see &lt;a href=&quot;https://github.com/peterfriese/AppleFeedback/tree/main/FB9826727&quot;&gt;AppleFeedback/FB9826727 at main · peterfriese/AppleFeedback&lt;/a&gt;), and filed a Feedback (FB9826727) with Apple.
If you agree with me that this is a regression, consider filing a Feedback as well to - the more duplicates a bug receives, the more likely it is it will be addressed.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;debounce&lt;/code&gt; to further optimise the UX&lt;/h2&gt;
&lt;p&gt;When building UIs that communicate with a remote system, we need to keep in mind that the user usually types a lot faster than the system can deliver feedback.&lt;/p&gt;
&lt;p&gt;For example, when picking a username, I usually type my favourite username without stopping to type in the middle of the word. I don’t care if the first few letters of this username are available - I am interested in the full name. Sending the incomplete username over to the server after each single keystroke doesn’t make a lot of sense and seems like a lot of waste.&lt;/p&gt;
&lt;p&gt;To avoid this, we can use Combine’s &lt;code&gt;debounce&lt;/code&gt; operator: it will drop all events until there is a pause. It will then pass on the most recent event to the downstream publisher:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .debounce(for: 0.8, scheduler: DispatchQueue.main)
    .print(&quot;username&quot;)
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailable(userName: username)
    }
    .receive(on: DispatchQueue.main)
    .share()
    .print(&quot;share&quot;)
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By doing so, we tell Combine to disregard all updates to &lt;code&gt;username&lt;/code&gt; until there is a pause of 0.8 seconds, and the send the most recent &lt;code&gt;username&lt;/code&gt; on to the next operator on the pipeline (in this case, the &lt;code&gt;print&lt;/code&gt; operator, which will then pass the unchanged event on to the &lt;code&gt;flatMap&lt;/code&gt; operator).&lt;/p&gt;
&lt;p&gt;This suits a normal user input behaviour much more, and will result in the app sending fewer requests to the server.&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;removeDuplicates&lt;/code&gt; to avoid sending the same request twice&lt;/h2&gt;
&lt;p&gt;Have you ever spoken to a person and asked them the same question twice? It’s a bit of an awkward situation, and the other person probably wonders if you’ve been paying attention to them at all.&lt;/p&gt;
&lt;p&gt;Now, even though AI is making advances, I am certain that computers don’t have emotions, so they won’t hold a grudge if you send the same API request twice. But - in the interest of giving our users the best experience possible, we should try to eliminate sending duplicate requests if we can.&lt;/p&gt;
&lt;p&gt;Combine has an operator for this: &lt;code&gt;removeDuplicates&lt;/code&gt; - it will remove any duplicate events from the stream of events if they follow each other &lt;em&gt;subsequently&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This works really well in conjunction with the &lt;code&gt;debounce&lt;/code&gt; operator, and we can use those two operators combined (sorry, I guess you’ll have to live with the puns) for a little further optimisation of our username availability check:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .debounce(for: 0.8, scheduler: DispatchQueue.main)
    .removeDuplicates()
    .print(&quot;username&quot;)
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailable(userName: username)
    }
    .receive(on: DispatchQueue.main)
    .share()
    .print(&quot;share&quot;)
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Together, they will further reduce the number of requests we send to our server in case the user mistypes and then corrects their spelling.&lt;/p&gt;
&lt;p&gt;Let’s look at an example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jonyive [pause] s [backspace]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will send the following requests:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;jonyive&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;no request for &lt;code&gt;jonyives&lt;/code&gt; (as the s got deleted before the debounce timed out)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;no second request for &lt;code&gt;jonyive&lt;/code&gt;, as this got filtered by &lt;code&gt;removeDuplicates&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It might be a small thing, but every little helps.&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;In this article, we discussed a number of ways how Combine can make communicating with a remote server (or any asynchronous API, in fact) more efficient.&lt;/p&gt;
&lt;p&gt;By using the &lt;strong&gt;&lt;code&gt;share&lt;/code&gt;&lt;/strong&gt; operator, we can attach multiple subscribers to a publisher / pipeline, and avoid running expensive / time-consuming processing for each of those subscribers. This is particularly useful when accessing APIs that have a higher latency than an in-process module, such as a remote server or anything that involves I/O.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;debounce&lt;/code&gt;&lt;/strong&gt; operator allows us to deal more efficiently with any events that occur in short bursts, like user input. Instead of processing every single event coming down the pipeline, we wait for a pause and only operate on the most recent event.&lt;/p&gt;
&lt;p&gt;To avoid processing duplicate events, we can use the &lt;strong&gt;&lt;code&gt;removeDuplicates&lt;/code&gt;&lt;/strong&gt; operator. As the name suggests, it removes any directly subsequent duplicate events, such as the user adding and then removing a character when we also use the &lt;code&gt;debounce&lt;/code&gt; operator.&lt;/p&gt;
&lt;p&gt;Together, these operators can help us build clients that access remote servers and other asynchronous APIs in a more efficient way.&lt;/p&gt;
&lt;p&gt;In the next episode of this series, we’re going to explore the topic of error handling and how to handle status-related server responses using Combine.&lt;/p&gt;
&lt;p&gt;Thanks for reading 🔥&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The header image is based on &lt;a href=&quot;https://thenounproject.com/icon/fast-internet-3845667/&quot;&gt;Fast Internet&lt;/a&gt; by &lt;a href=&quot;https://thenounproject.com/theiconz/&quot;&gt;The Icon Z&lt;/a&gt; from the &lt;a href=&quot;https://thenounproject.com/&quot;&gt;Noun Project&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Networking with Combine and SwiftUI</title><link>https://peterfriese.dev/blog/2022/swiftui-combine-networking-gettingstarted/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2022/swiftui-combine-networking-gettingstarted/</guid><description>Learn how to use Combine to access remote APIs and display the results in SwiftUI</description><pubDate>Mon, 17 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;Not keeping the UI up to date across the different parts of an app can result in an infuriatingly bad user experience, and I am sure we all have at least one or two apps in mind that are notorious for this kind of behaviour.&lt;/p&gt;
&lt;p&gt;Writing apps that keep the state in sync across the UI and the underlying data model has traditionally been a difficult task, and the development community has come up with plenty of approaches to address this challenge in more or less developer-friendly ways.&lt;/p&gt;
&lt;p&gt;Reactive programming is one such approach, and SwiftUI’s reactive state management makes this a lot easier by introducing the notion of a &lt;em&gt;source of truth&lt;/em&gt; that can be shared across your app using SwiftUI’s property wrappers such as &lt;code&gt;@EnvironmentObject&lt;/code&gt;, &lt;code&gt;@ObservedObject&lt;/code&gt;, and &lt;code&gt;@StateObject&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This source of truth usually is your in-memory data model - but as we all know, no application exists in isolation. Most modern apps need to access the network (or other services) at some point, and this means introducing asynchronous behaviour to your app. There are plenty of ways to deal with asynchronous behaviour in our apps: delegate methods, callback handlers, Combine, and async/await, to name just a few.&lt;/p&gt;
&lt;p&gt;In this series, we will look at how to use &lt;strong&gt;Combine&lt;/strong&gt; in the context of SwiftUI to&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;access the network,&lt;/li&gt;
&lt;li&gt;map data,&lt;/li&gt;
&lt;li&gt;handle errors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;… and deal with some advanced scenarios.&lt;/p&gt;
&lt;p&gt;Let’s kick things off by looking into how to use Combine to fetch data from a server and map the result to a Swift &lt;code&gt;struct&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;How to fetch data using URLSession&lt;/h2&gt;
&lt;p&gt;Let’s assume we’re working on a sign up screen for an app, and one of the requirements is to check if the username the user chose is still available in our user database. This requires us to communicate with our authorization server. Here is a request that shows how we might try to find out if the username &lt;em&gt;sjobs&lt;/em&gt; is still available:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET localhost:8080/isUserNameAvailable?userName=sjobs HTTP/1.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server would then reply with a short JSON document stating if the username is still available:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 39
connection: close
date: Thu, 06 Jan 2022 16:09:08 GMT

{&quot;isAvailable&quot;:false, &quot;userName&quot;:&quot;sjobs&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To perform this request in Swift, we can use &lt;code&gt;URLSession&lt;/code&gt;. The traditional way to fetch data from the network using &lt;code&gt;URLSession&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func checkUserNameAvailableOldSchool(userName: String, completion: @escaping (Result&amp;lt;Bool, NetworkError&amp;gt;) -&amp;gt; Void) {
  guard let url = URL(string: &quot;http://127.0.0.1:8080/isUserNameAvailable?userName=\(userName)&quot;) else { //(2)
    completion(.failure(.invalidRequestError(&quot;URL invalid&quot;)))
    return
  }
  
  let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let error = error { //(3)
      completion(.failure(.transportError(error)))
      return
    }
    
    if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) { //(4)
      completion(.failure(.serverError(statusCode: response.statusCode)))
      return
    }
    
    guard let data = data else { //(5)
      completion(.failure(.noData))
      return
    }
    
    do {
      let decoder = JSONDecoder()
      let userAvailableMessage = try decoder.decode(UserNameAvailableMessage.self, from: data)
      completion(.success(userAvailableMessage.isAvailable)) //(1)
    }
    catch {
      completion(.failure(.decodingError(error)))
    }
  }
  
  task.resume() //(6)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And while this code works fine and nothing is inherently wrong with it, it does have a number of issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It’s not immediately clear what the happy path is - the only location that returns a successful result is pretty hidden (1), and developers who are new to using completion handlers might be confused by the fact that the happy path doesn’t even use a &lt;code&gt;return&lt;/code&gt; statement to deliver the result of the network call to the caller.&lt;/li&gt;
&lt;li&gt;Error handling is scattered all over the place (2, 3, 4, 5).&lt;/li&gt;
&lt;li&gt;There are several exit points, and it’s easy to forget one of the &lt;code&gt;return&lt;/code&gt; statements in the &lt;code&gt;if let&lt;/code&gt; conditions.&lt;/li&gt;
&lt;li&gt;Overall, it is hard to read and maintain, even if you’re an experienced Swift developer.&lt;/li&gt;
&lt;li&gt;It’s easy to forget you have to call &lt;code&gt;resume()&lt;/code&gt; to actually perform the request (6). I am pretty sure most of us have been frantically looking for bugs, only to find out we forgot to actually kick off the request using &lt;code&gt;resume&lt;/code&gt;. And yes, I think &lt;code&gt;resume&lt;/code&gt; is not a great name for an API that is inteded to &lt;em&gt;send&lt;/em&gt; the request.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Running the code samples&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You will find all the code samples in the accompanying &lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Combine-Applied&quot;&gt;GitHub repository&lt;/a&gt;, in the &lt;code&gt;Networking&lt;/code&gt; folder. To be able to benefit the most, I&apos;ve also provided a demo server (built with Vapor) in the &lt;code&gt;server&lt;/code&gt; subfolder. To run it on your machine, do the following:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ cd server&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ swift run&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h2&gt;How to fetch data using Combine&lt;/h2&gt;
&lt;p&gt;When they introduced Combine, Apple added publishers for many of their own asynchronous APIs. This is great, as this makes it easier for us to use them in our own Combine pipelines.&lt;/p&gt;
&lt;p&gt;Now, let’s take a look at how the code looks like after refactoring it to make use of Combine.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func checkUserNameAvailable(userName: String) -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; {
  guard let url = URL(string: &quot;http://127.0.0.1:8080/isUserNameAvailable?userName=\(userName)&quot;) else {
    return Just(false).eraseToAnyPublisher()
  }
  
  return URLSession.shared.dataTaskPublisher(for: url) //(1)
    .map { data, response in //(2)
      do {
        let decoder = JSONDecoder()
        let userAvailableMessage = try decoder.decode(UserNameAvailableMessage.self, from: data)
        return userAvailableMessage.isAvailable //(3)
      }
      catch {
        return false //(4)
      }
    }
    .replaceError(with: false) //(5)
    .eraseToAnyPublisher()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a lot easier to read already, and (except for the &lt;code&gt;guard&lt;/code&gt; statement that makes sure we’ve got a valid URL) there is just &lt;em&gt;one&lt;/em&gt; exit point.&lt;/p&gt;
&lt;p&gt;Let’s walk through the code step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We use &lt;code&gt;dataTaskPublisher&lt;/code&gt; to perform the request. This publisher is a one-shot publisher and will emit an event once the requested data has arrived. It&apos;s worth keeping in mind that Combine publishers don&apos;t perform any work if there is no subscriber. This means that this publisher will not perform any call to the given URL unless there is at least one subscriber. I will later show you how to connect this pipeline to the UI and make sure it gets called every time the user enters their preferred username.&lt;/li&gt;
&lt;li&gt;Once the request returns, the publisher emits a value that contains both the &lt;code&gt;data&lt;/code&gt; and the &lt;code&gt;response&lt;/code&gt; . In this line, we use the &lt;code&gt;map&lt;/code&gt; operator to transform this result. As you can see, we can reuse most of the data mapping code from the previous version of the code, except for a couple of small changes:&lt;/li&gt;
&lt;li&gt;Instead of calling the &lt;code&gt;completion&lt;/code&gt; closure, we can return a &lt;code&gt;Boolean&lt;/code&gt; value to indicate whether the username is still available or not. This value will be passed down the pipeline.&lt;/li&gt;
&lt;li&gt;In case the data mapping fails, we catch the error and just return &lt;code&gt;false&lt;/code&gt;, which seems to be a good compromise.&lt;/li&gt;
&lt;li&gt;We do the same for any errors that might occur when accessing the network. This is a simplification that we might need to revisit in the future.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This looks a lot better and easier to read than the initial version, and we could stop here, and integrate this in out application.&lt;/p&gt;
&lt;p&gt;But we can do better. Here are three changes that will make the code more linear and easier to reason about:&lt;/p&gt;
&lt;h3&gt;Destructuring tuples using key paths&lt;/h3&gt;
&lt;p&gt;We often find ourselves in a situation where we need to extract a specific attribute from a variable. In our example, we receive a tuple containing the &lt;code&gt;data&lt;/code&gt; and the &lt;code&gt;response&lt;/code&gt; of the URL request we sent. Here is the respective declaration in &lt;code&gt;URLSession&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public struct DataTaskPublisher : Publisher {

  /// The kind of values published by this publisher.
  public typealias Output = (data: Data, response: URLResponse)
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Combine provides an overloaded version of the &lt;code&gt;map&lt;/code&gt; operator that allows us to destructure the tuple using a key path, and access just the attribute we care for:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return URLSession.shared.dataTaskPublisher(for: url)
  .map(\.data)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Mapping Data more easily&lt;/h3&gt;
&lt;p&gt;Since mapping data is such a common task, Combine comes with dedicated operator to make this easier: &lt;code&gt;decode(type:decoder:)&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return URLSession.shared.dataTaskPublisher(for: url)
  .map(\.data)
  .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will return decode the &lt;code&gt;data&lt;/code&gt; value from the upstream publisher and decode it into a &lt;code&gt;UserNameAvailableMessage&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;And finally, we can use the &lt;code&gt;map&lt;/code&gt; operator again to destructure the &lt;code&gt;UserNameAvailableMessage&lt;/code&gt; and access its &lt;code&gt;isAvailable&lt;/code&gt; attribute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return URLSession.shared.dataTaskPublisher(for: url)
  .map(\.data)
  .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
  .map(\.isAvailable)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Fetching data using Combine, simplified&lt;/h3&gt;
&lt;p&gt;With all these changes in place, we now have version of the pipeline that is easy to read, and has a linear flow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func checkUserNameAvailable(userName: String) -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; {
  guard let url = URL(string: &quot;http://127.0.0.1:8080/isUserNameAvailable?userName=\(userName)&quot;) else {
    return Just(false).eraseToAnyPublisher()
  }
  
  return URLSession.shared.dataTaskPublisher(for: url)
    .map(\.data)
    .decode(type: UserNameAvailableMessage.self, decoder: JSONDecoder())
    .map(\.isAvailable)
    .replaceError(with: false)
    .eraseToAnyPublisher()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;How to connect to SwiftUI&lt;/h2&gt;
&lt;p&gt;Let’s finish off by looking at how to integrate this new Combine pipeline in our hypothetical sign up form.&lt;/p&gt;
&lt;p&gt;Here is a condensed version a sign up form that contains just a username field, a &lt;code&gt;Text&lt;/code&gt; label to display a message, and a sign up button. In a real application, we’d also have some UI elements to provide a password and a password confirmation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct SignUpScreen: View {
  @StateObject private var viewModel = SignUpScreenViewModel()
  
  var body: some View {
    Form {
      // Username
      Section {
        TextField(&quot;Username&quot;, text: $viewModel.username)
          .autocapitalization(.none)
          .disableAutocorrection(true)
      } footer: {
        Text(viewModel.usernameMessage)
          .foregroundColor(.red)
      }
      
      // Submit button
      Section {
        Button(&quot;Sign up&quot;) {
          print(&quot;Signing up as \(viewModel.username)&quot;)
        }
        .disabled(!viewModel.isValid)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All UI elements are connected to a view model to separate concerns and keep the view clean and easy to read:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignUpScreenViewModel: ObservableObject {
  // MARK: Input
  @Published var username: String = &quot;&quot;
  
  // MARK: Output
  @Published var usernameMessage: String = &quot;&quot;
  @Published var isValid: Bool = false
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;@Published&lt;/code&gt; properties are Combine publishers, we can subscribe to them to receive updates whenever their value changes. This allows us to call the &lt;code&gt;checkUserNameAvailable&lt;/code&gt; pipeline we created above.&lt;/p&gt;
&lt;p&gt;Let’s create a reusable publisher that we can use to drive the parts of our UI that need to display information that depends on whether the username is available or not. One way to do this is to create a lazy computed property. This makes sure the pipeline will only be set up once it is needed, and there will be only one instance of the pipeline.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .flatMap { username in
      self.authenticationService.checkUserNameAvailable(userName: username)
    }
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To call another pipeline and then use its result, we can make use of the &lt;code&gt;flatMap&lt;/code&gt; operator. This will take all input events from an upstream publisher (i.e., the values emitted by the &lt;code&gt;$username&lt;/code&gt; published property), and transform them into a new publisher (in our case, the publisher &lt;code&gt;checkUserNameAvailable&lt;/code&gt; in ).&lt;/p&gt;
&lt;p&gt;In the next and final step, we will connect the result of the &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; to the UI. If you take a look at the view model, you will notice we’ve got two properties in the output section of the view model: one for any message related to the username, and another one that holds the overall validation state of the form (remember, in a real sign up form, we might need to validate the password fields as well).&lt;/p&gt;
&lt;p&gt;Combine publishers can be connected to more than one subscriber, so we can connect both &lt;code&gt;isValid&lt;/code&gt; and &lt;code&gt;usernameMessage&lt;/code&gt; to the &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignUpScreenViewModel: ObservableObject {
  ...
  init() {
    isUsernameAvailablePublisher
      .assign(to: &amp;amp;$isValid)
    
    isUsernameAvailablePublisher
      .map { $0 ? &quot;&quot; : &quot;Username not available. Try a different one.&quot;}
      .assign(to: &amp;amp;$usernameMessage)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using this approach allows us to reuse the &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; and use it to drive both the overall &lt;code&gt;isValid&lt;/code&gt; state of the form (which will enable / disable the &lt;em&gt;Submit&lt;/em&gt; button, and the error message label which informs the user whether their chosen username is still available or not.&lt;/p&gt;
&lt;h2&gt;How to handle &lt;code&gt;Publishing changes from background threads is not allowed&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;When you run this code, you will notice a couple of issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The API endpoint gets called several times for each character you type&lt;/li&gt;
&lt;li&gt;Xcode tells you that you shouldn&apos;t update the UI from a background thread&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are going to dive deeper into the reasons for these issues in the next episodes, but for now, let&apos;s address this error message:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[SwiftUI] Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The reason for this error message is that Combine will execute the network request on a background thread. When the request is fulfilled, we assign the result to one of the published properties on the view model. This, in turn, will prompt SwiftUI to update the UI - and this will happen on the foreground thread.&lt;/p&gt;
&lt;p&gt;To prevent this from happening, we need to instruct Combine to switch to the foreground thread once it has received the result of the network request, using the &lt;code&gt;receive(on:)&lt;/code&gt;  operator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private lazy var isUsernameAvailablePublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; = {
  $username
    .flatMap { username -&amp;gt; AnyPublisher&amp;lt;Bool, Never&amp;gt; in
      self.authenticationService.checkUserNameAvailableNaive(userName: username)
    }
||&amp;gt;    .receive(on: DispatchQueue.main)&amp;lt;||
    .eraseToAnyPublisher()
}()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will look deeper into threading in one of the next episodes when we talk about Combine schedulers.&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;In this post, I showed you how to access the network using Combine, and how this enables you to write straight-line code that should be easier to read and maintain than the respective callback-driven counterpart.&lt;/p&gt;
&lt;p&gt;We also looked at how to connect a Combine pipeline that makes network requests to SwiftUI by using a view model, and attaching the pipeline to an &lt;code&gt;@Published&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;Now, you might be wondering why &lt;code&gt;isUsernameAvailablePublisher&lt;/code&gt; uses &lt;code&gt;Never&lt;/code&gt; as its error type - after all, network errors very much are something that we need to deal with.&lt;/p&gt;
&lt;p&gt;We will look into error handling (and custom data mapping) in one of the next episodes. We will also look at ways to optimise our Combine-based networking layer, so stay tuned!&lt;/p&gt;
&lt;p&gt;Thanks for reading 🔥&lt;/p&gt;
</content:encoded></item><item><title>Confirmation Dialogs in SwiftUI</title><link>https://peterfriese.dev/blog/2021/swiftui-confirmation-dialogs/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-confirmation-dialogs/</guid><description>Create a custom view modifier for handling confirmation dialogs in SwiftUI</description><pubDate>Fri, 26 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;In our apps, we often need users to confirm an action they initiated, mostly because it’s a destructive operation. In SwiftUI, this requires the interplay of a number of views and state variables: the view representing the screen itself, some sort of confirmation dialog, potentially a toolbar, and usually a &lt;em&gt;Cancel&lt;/em&gt; and a &lt;em&gt;Done&lt;/em&gt; button, and the state variables that drive the visibility of those views.&lt;/p&gt;
&lt;p&gt;In this post, we will explore how to replicate the confirmation dialog from Apple’s Reminders app, and we’ll learn how to turn this in to a reusable solution using SwiftUI’s custom view modifiers. Along the way, we will also look into preventing users from dismissing sheets, and we’ll discover Apple’s API for doing so lacks an important feature.&lt;/p&gt;
&lt;h2&gt;What we’re going to build&lt;/h2&gt;
&lt;p&gt;The code in this post is based on &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/develop&quot;&gt;Make It So&lt;/a&gt;, a replica of Apple’s Reminders app. My goal is to see if it’s possible to replicate Apple’s Reminders app using just pure SwiftUI and Firebase. You can check out the code from this repository (make sure to use the &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/develop&quot;&gt;&lt;code&gt;develop&lt;/code&gt;&lt;/a&gt; branch) if you want to run the app and follow along.&lt;/p&gt;
&lt;p&gt;Here is the final state of the application, alongside the original behaviour of the Reminders app:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the app will ask the user’s confirmation only if they made a change to the reminder and then try to leave the edit dialog by tapping &lt;em&gt;Cancel&lt;/em&gt; or swiping down.&lt;/p&gt;
&lt;p&gt;Now, let’s see how we can implement this!&lt;/p&gt;
&lt;h2&gt;Detecting Changes&lt;/h2&gt;
&lt;p&gt;Before we can dive into the implementation of the confirmation dialog, we need to detect if the user has actually changed any data in the edit dialog. Since we’re using &lt;code&gt;struct&lt;/code&gt;s to hold our data, this is a lot easier than you might think: &lt;code&gt;struct&lt;/code&gt;s are value types, which means we can use a simple equality check to test if two reminders are the same:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let a = Reminder(id: &quot;build&quot;, title: &quot;Build sample app&quot;)
let b = Reminder(id: &quot;tweet&quot;, title: &quot;Tweet about surprising findings&quot;, flagged: true)
let c = Reminder(id: &quot;build&quot;, title: &quot;Build sample app&quot;)
    
print(a == b) // false
print(a == c) // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make handling data in the edit dialog easier, I’ve created a simple view model that wraps a &lt;code&gt;Reminder&lt;/code&gt; and exposes a property &lt;code&gt;isModified&lt;/code&gt; that performs a simple inequality check to indicate whether the reminder has been edited by the user:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ReminderDetailsViewModel: ObservableObject {
  @Published var reminder: Reminder
  private var original: Reminder
  
  init(reminder: Reminder) {
    self.reminder = reminder
    original = reminder
  }
  
  var isModified: Bool {
    original != reminder
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What’s great about this approach: if the user undoes all their changes, both &lt;code&gt;reminder&lt;/code&gt; and &lt;code&gt;original&lt;/code&gt;  will be equal, and &lt;code&gt;isModified&lt;/code&gt; will return &lt;code&gt;false&lt;/code&gt; - just as expected.&lt;/p&gt;
&lt;h2&gt;Requesting the user’s confirmation&lt;/h2&gt;
&lt;p&gt;Building great user experiences is about removing friction as much as possible, but applying it at the right places.&lt;/p&gt;
&lt;p&gt;For example, if a user makes changes a reminder in the edit dialog, we can assume they did so on purpose. Asking them whether they want to save their changes just adds unnecessary friction.&lt;/p&gt;
&lt;p&gt;However, if the user taps the &lt;em&gt;Cancel&lt;/em&gt; button, it might be worth asking if they want to discard the changes they made - after all, this is a destructive operation, and they will lose the changes. Just imagine they wrote down an important thought in the &lt;em&gt;notes&lt;/em&gt; field of the reminder - it would be rather upsetting to lose this data!&lt;/p&gt;
&lt;p&gt;This is even more important on mobile UIs, where it is easy to accidentally touch the C&lt;em&gt;ancel&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;With iOS 15, Apple introduced a new view modifier &lt;code&gt;confirmationDialog&lt;/code&gt; to create confirmation dialogs. In the spirit of many of SwiftUI’s APIs, this is a cross-platform way to express &lt;em&gt;what&lt;/em&gt; we want to achieve, not &lt;em&gt;how&lt;/em&gt; we want to achieve it. The system will take care of using the most appropriate platform-specific UI elements to render the confirmation dialog.&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at how to create a simple confirmation dialog that we can use to ask the user whether they’d like to discard their edits:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ReminderDetailsView: View {
  @Environment(\.dismiss) private var dismiss
  @ObservedObject private var viewModel: ReminderDetailsViewModel

  @State private var presentingConfirmationDialog: Bool = false

  // ...
  
  var body: some View {
    NavigationView {
      Form {
        Section {
          TextField(&quot;Title&quot;, text: $viewModel.reminder.title)
        }
        // ...
      }
      // ...
      .confirmationDialog(&quot;&quot;, isPresented: $presentingConfirmationDialog) {
        Button(&quot;Discard Changes&quot;, role: .destructive, action: { dismiss() })
        Button(&quot;Cancel&quot;, role: .cancel, action: { })
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we add two simple &lt;code&gt;Button&lt;/code&gt;s to the confirmation dialog:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first one is marked as &lt;code&gt;.destructive&lt;/code&gt;, and when the user taps it, the details dialog will be dismissed (thanks to the &lt;code&gt;dismiss&lt;/code&gt; action we grabbed from the environment).&lt;/li&gt;
&lt;li&gt;The second one is marked as &lt;code&gt;.cancel&lt;/code&gt; , and tapping it will just close the confirmation dialog, so the user can continue editing. iOS will automatically display this button at the bottom of the action sheet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We want to display the confirmation dialog when the user has modified the reminder and then taps the &lt;em&gt;Cancel&lt;/em&gt; button to leave the screen, so let&apos;s add a toolbar with a &lt;em&gt;Cancel&lt;/em&gt; and &lt;em&gt;Done&lt;/em&gt; button:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ReminderDetailsView: View {
  @Environment(\.dismiss) private var dismiss
  @ObservedObject private var viewModel: ReminderDetailsViewModel

  @State private var presentingConfirmationDialog: Bool = false

  // ...
  
  var body: some View {
    NavigationView {
      Form {
        Section {
          TextField(&quot;Title&quot;, text: $viewModel.reminder.title)
        }
        // ...
      }
      .toolbar {
        ToolbarItem(placement: .cancellationAction) {
          Button(&quot;Cancel&quot;, role: .cancel) {
            if viewModel.isModified {
              presentingConfirmationDialog.toggle()
            }
            else {
              dismiss()
            }
          }
        }
        ToolbarItem(placement: .confirmationAction) {
          Button(&quot;Done&quot;) {
            // TODO: save the modified data
            dismiss()
          }
        }
      }
      .confirmationDialog(&quot;&quot;, isPresented: $presentingConfirmationDialog) {
        Button(&quot;Discard Changes&quot;, role: .destructive, action: { dismiss() })
        Button(&quot;Cancel&quot;, role: .cancel, action: { })
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the user taps the &lt;em&gt;Cancel&lt;/em&gt; button, we check the &lt;em&gt;isModified&lt;/em&gt; state of the view model. If the reminder has been modified by the user, we will set &lt;code&gt;presentingConfirmationDialog&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;, which will cause the confirmation dialog to appear.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Side Note&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Previous versions of SwiftUI required us to use the &lt;code&gt;actionSheet&lt;/code&gt; view modifier to implement confirmation dialogs. When comparing the code for creating a confirmation dialog using the new &lt;code&gt;confirmationDialog&lt;/code&gt; view modifier to the code for creating an action sheet using &lt;code&gt;actionSheet&lt;/code&gt;, it seems like the SwiftUI team has been working on streamlining some of SwiftUI&apos;s APIs. For example, instead of using API-specific inner structs (such as &lt;code&gt;Alert.Button&lt;/code&gt;) and their custom instances like &lt;code&gt;.destructive&lt;/code&gt;, &lt;code&gt;.cancel&lt;/code&gt;, we can now use regular &lt;code&gt;Button&lt;/code&gt;s with their &lt;code&gt;role&lt;/code&gt; attribute set to the respective enum case. Buttons are a cross-cutting concern in SwiftUI, and they can appear in many different shapes and forms. While cleaning up the SwiftUI DSL might be inconvenient for those of us who have a lot of code written in SwiftUI already, this has some major benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By using the same concept across different concerns, it becomes easier to move things around. For example, it’s now possible to use the same code for creating a &lt;code&gt;Button&lt;/code&gt; on a &lt;code&gt;Toolbar&lt;/code&gt; or inside a &lt;code&gt;confirmationDialog&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You have to learn fewer APIs. No need to learn the syntax for adding buttons to an &lt;code&gt;actionSheet&lt;/code&gt; any more - knowing how a plain old &lt;code&gt;Button&lt;/code&gt; works is enough. This reduces mental load and makes developers more efficient.&lt;/li&gt;
&lt;li&gt;Overall, this will make SwiftUI more scalable and future-proof.
To learn more about DSLs and cross-cutting concerns, check out the excellent DSL Engineering book by Markus Voelter (&lt;a href=&quot;https://voelter.de/books.html&quot;&gt;site&lt;/a&gt;, &lt;a href=&quot;https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf&quot;&gt;PDF&lt;/a&gt;).
&amp;lt;/InfoBox&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Committing to the user’s will&lt;/h2&gt;
&lt;p&gt;Once the user decides whether they want to commit, discard, or cancel, we need to act on their decision.&lt;/p&gt;
&lt;p&gt;So far, we’ve already covered the following cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The user wants to continue editing (the confirmation dialog will automatically disappear once the user taps on its &lt;em&gt;Cancel&lt;/em&gt; button).&lt;/li&gt;
&lt;li&gt;The user wants to leave the edit dialog and discard any changes they made - we just dismiss the edit dialog.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only thing left is to save any changes if the user taps the &lt;em&gt;Done&lt;/em&gt; button. To implement this in a flexible and reusable way, we add a callback closure (named &lt;code&gt;onCommit&lt;/code&gt;) to the edit dialog&apos;s initialiser:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ReminderDetailsView: View {
  @Environment(\.dismiss) private var dismiss
  @ObservedObject private var viewModel: ReminderDetailsViewModel
  
  private var onCommit: (Reminder) -&amp;gt; Void
  
  @State private var presentingConfirmationDialog: Bool = false
  
  init(reminder: Reminder, onCommit: @escaping (Reminder) -&amp;gt; Void) {
    self.viewModel = ReminderDetailsViewModel(reminder: reminder)
    self.onCommit = onCommit
  }
  
  func doCommit() {
    onCommit(viewModel.reminder)
    dismiss()
  }

  var body: some View {
    NavigationView {
      Form {
        // ...
      }
      // ...
      .toolbar {
        // ...
        ToolbarItem(placement: .confirmationAction) {
          Button(&quot;Done&quot;, action: doCommit)
        }
      }
      // ...
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This closure takes one parameter, &lt;code&gt;reminder&lt;/code&gt;, which will contain the modified data. To send the modified data back to the caller (in our case the list view displaying the user&apos;s reminders), all we need to do is call &lt;code&gt;doCommit&lt;/code&gt; from the &lt;em&gt;Done&lt;/em&gt; button on the edit dialog&apos;s toolbar.&lt;/p&gt;
&lt;h2&gt;Preventing interactive dismissal&lt;/h2&gt;
&lt;p&gt;With this in place, we can now make sure the user doesn’t accidentally discard the changes they made.&lt;/p&gt;
&lt;p&gt;There is one little snag, though: swiping down the sheet which is hosting the edit dialog will dismiss the edit dialog and discard any changes the user made. This is not at all what the user expects, so we need to fix this.&lt;/p&gt;
&lt;p&gt;In UIKit, you can implement &lt;code&gt;UIAdaptivePresentationControllerDelegate&lt;/code&gt; to control whether the user can dismiss a sheet. Up to now, there was no pure SwiftUI equivalent. The good news is that Apple introduced the &lt;code&gt;interactiveDismissDisabled&lt;/code&gt; view modifier in iOS 15 - this view modifier allows us to turn of this feature either completely, or based on a boolean.&lt;/p&gt;
&lt;p&gt;So to prevent the user from swiping down and involuntarily losing their changes, we can apply &lt;code&gt;interactiveDismissDisabled&lt;/code&gt; and pass in the &lt;code&gt;isModified&lt;/code&gt; attribute of the view model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
  NavigationView {
    Form { /* ... */ }
    .navigationTitle(&quot;Details&quot;)
    .navigationBarTitleDisplayMode(.inline)
    .toolbar { /* ... */ }
    .interactiveDismissDisabled(viewModel.isModified)
    .confirmationDialog(&quot;&quot;, isPresented: $presentingConfirmationDialog) { /* ... */ }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means user will still be able to dismiss the edit dialog by swiping down - but only if they didn’t make any changes to the reminder. If they made any changes, the view model is marked as &lt;em&gt;modified&lt;/em&gt;, and swiping down will be disabled.&lt;/p&gt;
&lt;h2&gt;Confirming interactive dismissal&lt;/h2&gt;
&lt;p&gt;This works great, and in many cases it might be exactly what you need. Apple’s Reminders app takes it one step further and shows a confirmation dialog when the user tries to dismiss the edit dialog by swiping down. It’s the same the app shows when the user taps on the &lt;em&gt;Cancel&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;Unfortunately, it turns our that implementing this behaviour isn’t possible with the current version of &lt;code&gt;interactiveDismissDisabled&lt;/code&gt;, as there is no way to react to the user&apos;s &lt;em&gt;attempt&lt;/em&gt; to dismiss the sheet. In UIKit apps, we can use &lt;code&gt;UIAdaptivePresentationControllerDelegate&lt;/code&gt; to implement this behaviour - this protocol has a method &lt;code&gt;presentationControllerDidAttemptToDismiss&lt;/code&gt; that will be called when the user tries to dismiss the view controller. It is safe to assume that Apple will eventually make this functionality available via the &lt;code&gt;interactiveDismissDisabled&lt;/code&gt; view modifier, but in the meantime, I thought it&apos;d be a fun exercise to implement a drop-in solution that we can use while we wait for Apple to resolve FB9782213 (which I filed to request the addition of this behaviour to the view modifier).&lt;/p&gt;
&lt;p&gt;My solution is inspired by a couple of answers to this &lt;a href=&quot;https://stackoverflow.com/questions/56615408/prevent-dismissal-of-modal-view-controller-in-swiftui&quot;&gt;StackOverflow question&lt;/a&gt; and &lt;a href=&quot;https://gist.github.com/mobilinked/9b6086b3760bcf1e5432932dad0813c0&quot;&gt;this gist&lt;/a&gt;, and I’ve tried to model it like I believe the SwiftUI team at Apple would. The best way to predict the future is to invent it, they say - but Apple still might choose a different API and behaviour.&lt;/p&gt;
&lt;p&gt;The core of the solution is a view that conforms to &lt;code&gt;UIViewControllerRrepresentable&lt;/code&gt;, which allows us to respond to &lt;code&gt;presentationControllerShouldDismiss&lt;/code&gt; and &lt;code&gt;presentationControllerDidAttemptToDismiss&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private struct InteractiveDismissableView&amp;lt;T: View&amp;gt;: UIViewControllerRepresentable {
  let view: T
  let isDisabled: Bool
  let onAttemptToDismiss: (() -&amp;gt; Void)?
  
  func makeUIViewController(context: Context) -&amp;gt; UIHostingController&amp;lt;T&amp;gt; {
    UIHostingController(rootView: view)
  }
  
  func updateUIViewController(_ uiViewController: UIHostingController&amp;lt;T&amp;gt;, context: Context) {
    context.coordinator.dismissableView = self
    uiViewController.rootView = view
    uiViewController.parent?.presentationController?.delegate = context.coordinator
  }
  
  func makeCoordinator() -&amp;gt; Coordinator {
    Coordinator(self)
  }
  
  class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate {
    var dismissableView: InteractiveDismissableView
    
    init(_ dismissableView: InteractiveDismissableView) {
      self.dismissableView = dismissableView
    }
    
    func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -&amp;gt; Bool {
      !dismissableView.isDisabled
    }
    
    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
      dismissableView.onAttemptToDismiss?()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make this accessible on any view, I’ve added an extension on &lt;code&gt;View&lt;/code&gt; that contains two overloaded versions of &lt;code&gt;interactiveDismissDisabled&lt;/code&gt;. The first one takes an additional parameter that is a closure. By implementing this closure, you can react to the user attempting to dismiss the sheet. The second overloaded version takes a binding to a &lt;code&gt;Bool&lt;/code&gt; as its second parameter. This makes it even easier to use this method to drive the display state of a confirmation dialog. Here&apos;s how you can use this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
  NavigationView {
    Form { /* ... */ }
    .navigationTitle(&quot;Details&quot;)
    .navigationBarTitleDisplayMode(.inline)
    .toolbar { /* ... */ }
    // Option 1: use a closure to handle the attempt to dismiss
    .interactiveDismissDisabled(isModified) {
      presentingConfirmationDialog.toggle()
    }
    // Option 2: bind attempt to dismiss to a boolean state variable that drives the UI
    .interactiveDismissDisabled(isModified, attemptToDismiss: $presentingConfirmationDialog)
    .confirmationDialog(&quot;&quot;, isPresented: $presentingConfirmationDialog) { /* ... */ }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Obviously, you only need to use one of the two options!)&lt;/p&gt;
&lt;p&gt;I’ve extracted this code into a &lt;a href=&quot;https://gist.github.com/peterfriese/8fb3d76bdbe21b84495b79b3a86bf898&quot;&gt;gist&lt;/a&gt;, including a simple sample app - feel free to use my implementation in your apps, but please be aware that my solution will (hopefully) be sherlocked in the not-too-distant future, and I will only be maintaining this on a best-effort basis.&lt;/p&gt;
&lt;h2&gt;A reusable confirmation dialog&lt;/h2&gt;
&lt;p&gt;As a final step in this post, let’s turn what we’ve got so far into a reusable solution. Being able to easily reuse this solution in other screens of our app will allow us to create a more cohesive experience for our users.&lt;/p&gt;
&lt;p&gt;If you use SwiftUI, you have used its built-in view modifiers. In the following snippet, &lt;code&gt;font()&lt;/code&gt;, &lt;code&gt;padding()&lt;/code&gt;, and &lt;code&gt;foregroundColor()&lt;/code&gt; all are view modifiers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Hello World&quot;)
  .font(.caption2)
  .padding(10)
  .foregroundColor(Color.blue)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View modifiers are a key reason why building UIs with SwiftUI is such a pleasant experience. If we didn’t have view modifiers, we’d have to use a view’s initialiser parameters to configure the view, which is not a very scalable approach at all.&lt;/p&gt;
&lt;p&gt;SwiftUI also allows use to create custom view modifiers ourselves, and maybe you’ve done this before to make your code easier to read.&lt;/p&gt;
&lt;p&gt;To implement a custom view modifier , we need to create a struct that conforms to the &lt;code&gt;ViewModifier&lt;/code&gt; protocol. The only requirement of this protocol is the &lt;code&gt;body&lt;/code&gt; method. It has a similar role as a view&apos;s &lt;code&gt;body&lt;/code&gt; method - in fact, the result of both methods is &lt;code&gt;some View&lt;/code&gt;, as both return a view. However, a view modifier&apos;s &lt;code&gt;body&lt;/code&gt; method has a single parameter &lt;code&gt;content&lt;/code&gt;. This parameter contains the view the view modifier is applied to.&lt;/p&gt;
&lt;p&gt;Here is the declaration of &lt;code&gt;ViewModifier&lt;/code&gt; (taken from the &lt;code&gt;SwiftUI.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol ViewModifier {
  /// The type of view representing the body.
  associatedtype Body : View

  /// Gets the current body of the caller.
  ///
  /// `content` is a proxy for the view that will have the modifier
  /// represented by `Self` applied to it.
  @ViewBuilder func body(content: Self.Content) -&amp;gt; Self.Body

  /// The content view type passed to `body()`.
  typealias Content
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s implement a view modifier the confirmation dialog implementation we’ve created so far. It should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use a toolbar to display the &lt;em&gt;Done&lt;/em&gt; and &lt;em&gt;Cancel&lt;/em&gt; buttons&lt;/li&gt;
&lt;li&gt;provide a way for the caller to signal if the user has made any changes to the data on the screen&lt;/li&gt;
&lt;li&gt;display a confirmation dialog if the user tries to cancel the dialog after having modified data on the screen&lt;/li&gt;
&lt;li&gt;prevents interactive dismissal of the sheet if the data has been modified&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll start by creating the skeleton for the view modifier and moving all the code we wrote for the &lt;code&gt;toolbar&lt;/code&gt;, &lt;code&gt;confirmationDialog&lt;/code&gt;, and &lt;code&gt;interactiveDismissDisabled&lt;/code&gt; into the &lt;code&gt;body&lt;/code&gt; method of the new modifier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ConfirmationDialog: ViewModifier {

  // ...

  func body(content: Content) -&amp;gt; some View {
    NavigationView {
      content
        .toolbar {
          ToolbarItem(placement: .cancellationAction) {
            Button(&quot;Cancel&quot;, role: .cancel) {
              if isModified {
                presentingConfirmationDialog.toggle()
              }
              else {
                doCancel()
              }
            }
          }
          ToolbarItem(placement: .confirmationAction) {
            Button(&quot;Done&quot;, action: doCommit)
          }
        }
        .confirmationDialog(&quot;&quot;, isPresented: $presentingConfirmationDialog) {
          Button(&quot;Discard Changes&quot;, role: .destructive, action: doCancel)
          Button(&quot;Cancel&quot;, role: .cancel, action: { })
        }
    }
    .interactiveDismissDisabled(isModified, attemptToDismiss: $presentingConfirmationDialog)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we’re using the &lt;code&gt;content&lt;/code&gt; parameter of the &lt;code&gt;body&lt;/code&gt; function where we previously had the view for the edit screen.&lt;/p&gt;
&lt;p&gt;To make the above code work, we need to add some missing bits and pieces:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ConfirmationDialog: ViewModifier {
  @Environment(\.dismiss) private var dismiss
  @State private var presentingConfirmationDialog: Bool = false
  
  var isModified: Bool
  var onCancel: (() -&amp;gt; Void)?
  var onCommit: () -&amp;gt; Void
  
  private func doCancel() {
    onCancel?()
    dismiss()
  }
  
  private func doCommit() {
    onCommit()
    dismiss()
  }
  
  func body(content: Content) -&amp;gt; some View {
    // ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like normal views, view modifiers can access the environment, which allows us to access the &lt;code&gt;dismiss&lt;/code&gt; action, making is easy to dismiss the dialog once the user taps the &lt;em&gt;Done&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;It’s also worth noting that view modifiers can hold state, which is why we are able to use &lt;code&gt;presentingConfirmationDialog&lt;/code&gt; to show / hide the confirmation dialog as needed.&lt;/p&gt;
&lt;p&gt;Swift will automatically synthesise an initialiser for us, based on the uninitialised properties &lt;code&gt;isModified&lt;/code&gt;, &lt;code&gt;onCancel&lt;/code&gt; , and &lt;code&gt;onCommit&lt;/code&gt;. It&apos;s important to keep the properties in exactly this order, as this will be their order on the parameter list of the initialiser.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isModified&lt;/code&gt; is a property that the caller can use to indicate if the data shown in the dialog has been modified.&lt;/p&gt;
&lt;p&gt;We can now use the view modifier like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Form {
  Section {
    TextField(&quot;Title&quot;, text: $viewModel.reminder.title)
  }
  // ...
}
.navigationTitle(&quot;Details&quot;)
.navigationBarTitleDisplayMode(.inline)
.modifier(ConfirmationDialog(isModified: viewModel.isModified) {
  onCommit(viewModel.reminder)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As this is a bit cumbersome to write, view modifiers usually go along an extension on &lt;code&gt;View&lt;/code&gt; that defines a convenience method that makes applying the view modifier easier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension View {
  func confirmationDialog(isModified: Bool, onCancel: (() -&amp;gt; Void)? = nil, onCommit: @escaping () -&amp;gt; Void) -&amp;gt; some View {
    self.modifier(ConfirmationDialog(isModified: isModified,  onCancel: onCancel, onCommit: onCommit))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes calling the view modifier look a lot more pleasant to the eye:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Form {
  Section {
    TextField(&quot;Title&quot;, text: $viewModel.reminder.title)
  }
  // ...
}
.navigationTitle(&quot;Details&quot;)
.navigationBarTitleDisplayMode(.inline)
.confirmationDialog(isModified: viewModel.isModified) {
  onCommit(viewModel.reminder)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;Providing confirmation prompts is important for any changes that might be hard to revert, such as in a complex edit form, or when deleting data.&lt;/p&gt;
&lt;p&gt;In this article, I walked you through an implementation of a confirmation dialog for the edit screen in MakeItSo, an application that tries to replicate Apple’s Reminders app as closely as possible. As you saw, we were able to replicate most of the behaviour using SwiftUI’s built-in features, and most of the implementation was straightforward and didn’t require a lot of code.&lt;/p&gt;
&lt;p&gt;For example, it was easy to detect whether the user made any changes since our data model makes use of structs: since structs are value objects, we can detect changes by comparing the currently edited todo item to the original one using a simple equality check. And since we set up a view model for the edit screen, we were able to nicely encapsulate this logic inside the view model and this keep the view code clean and tidy.&lt;/p&gt;
&lt;p&gt;In iOS 15, Apple has deprecated a few view modifiers, and provided new ones. In the past you might have used &lt;code&gt;actionSheet&lt;/code&gt;, which is now deprecated, and will be superseded by &lt;code&gt;confirmationDialog&lt;/code&gt;. The API has changed in a few subtle ways, making it slightly easier to use. The ability to hide the title is a welcome addition, and also the fact SwiftUI will automatically use the platform specific behaviour.&lt;/p&gt;
&lt;p&gt;Leaving an edit dialog should always be a conscious decision, and thanks to the new &lt;code&gt;interactiveDismissDisabled&lt;/code&gt; view modifier, it is now possible to prevent users from leaving a sheet by swiping down if there are some unsaved changes (or any other unfinished business). Unfortunately, it’s not possible to display a confirmation dialog when the user tries to dismiss a dirty edit dialog. I showed you how to implement a fallback solution for this shortcoming, based on UIKit. However, as we can expect Apple to fill this gap in the near future (maybe even with the next beta version), we should only see this as a temporary solution, and I&apos;ve filed a feedback with Apple to address this.&lt;/p&gt;
&lt;p&gt;I hope you enjoyed this little detour into implementing a confirmation dialog as much as I did researching this topic and implementing a solution.&lt;/p&gt;
&lt;p&gt;Thanks for reading 🔥&lt;/p&gt;
</content:encoded></item><item><title>Using View Modifiers to Display Empty State</title><link>https://peterfriese.dev/blog/2021/swiftui-empty-state/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-empty-state/</guid><description>Create a custom view modifier to manage empty state in your SwiftUI app</description><pubDate>Fri, 12 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Empty state is an &lt;a href=&quot;https://www.toptal.com/designers/ux/empty-state-ux-design&quot;&gt;important aspect of UX&lt;/a&gt; - it&apos;s what users see when they first open your app, and as the saying goes, there is no second chance to leave a first impression - so it better be good. Showing users a meaningful empty state will make them feel welcome and is a great opportunity for you to educate them about how to get started with the app.&lt;/p&gt;
&lt;p&gt;So far, Make It So doesn’t display a meaningful empty state, and in this article, we will look at a couple of options to implement this.&lt;/p&gt;
&lt;h2&gt;What we&apos;re going to build&lt;/h2&gt;
&lt;p&gt;To get started, let’s take a look at Apple’s Reminder app (since this is the app that we’re trying to replicate):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;A simple way to implement empty state is to use Xcode’s &lt;em&gt;Make Conditional&lt;/em&gt; refactoring (&lt;em&gt;CMD + Click&lt;/em&gt; on a view, then choose &lt;em&gt;Make Conditional&lt;/em&gt;). This will wrap the view in an &lt;code&gt;if ... else&lt;/code&gt; statement and wrap the entire structure in a &lt;code&gt;VStack&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ContentView: View {
  @State var isEmpty = true
  var body: some View {
    VStack {
      if isEmpty {
        Text(&quot;Hello, World!&quot;)
      } else {
        EmptyView()
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not too bad, but this adds visual noise to the code for our view - let’s see if we can improve this.&lt;/p&gt;
&lt;h2&gt;Using a ViewModifier to Manage Empty State&lt;/h2&gt;
&lt;p&gt;View Modifiers are one of the main features of SwiftUI that make writing SwiftUI code an enjoyable experience. Without view modifiers, the only way to configure views would be their initialisers, resulting in a pretty terrible developer experience.&lt;/p&gt;
&lt;p&gt;Thanks to view modifiers, we can configure views like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Hello, World!&quot;)
  .foregroundColor(.red)
  .font(.title)
  .opacity(75)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;instead of this (hypothetical code):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Hello, World!&quot;, foregroundColor: .red, font: .title, opacity: 0.75)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the call site, a view modifier for adding empty state to a view might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Hello, World!&quot;)
  .emptyState($isEmpty) {
    Text(&quot;Sorry - no content available&quot;)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s take a look at how to build this. View modifiers usually consist of two parts: the view modifier itself, and an extension to make it easier to use.&lt;/p&gt;
&lt;p&gt;Let’s start with the view modifier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct EmptyStateViewModifier&amp;lt;EmptyContent&amp;gt;: ViewModifier where EmptyContent: View {
  var isEmpty: Bool
  let emptyContent: () -&amp;gt; EmptyContent

  func body(content: Content) -&amp;gt; some View {
    if isEmpty {
      emptyContent()
    }
    else {
      content
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All view modifiers can access the view they operate on via the &lt;code&gt;content: Content&lt;/code&gt; parameter of the &lt;code&gt;body&lt;/code&gt; function. In addition, we&apos;ve declared two properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isEmpty&lt;/code&gt; lets the caller indicate whether or not to display the empty state&lt;/li&gt;
&lt;li&gt;&lt;code&gt;emptyContent&lt;/code&gt; is a closure that returns the view we want to display in case &lt;code&gt;isEmpty&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depending on the state of the &lt;code&gt;isEmpty&lt;/code&gt; property, we will either display the &lt;code&gt;emptyContent&lt;/code&gt;, or the original view, which we can access via the &lt;code&gt;content&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;To use this modifier on a view, we’d have to write the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Text(&quot;Hello, World!&quot;)
  .modifier(EmptyStateViewModifier(isEmpty: isEmpty, emptyContent: {
    Text(&quot;Sorry - no content available&quot;)
  }))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which doesn’t look very user friendly.&lt;/p&gt;
&lt;h2&gt;Adding an Extension to Improve the Developer Experience&lt;/h2&gt;
&lt;p&gt;Let’s define an extension on &lt;code&gt;View&lt;/code&gt; to make this easier to use:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension View {
  func emptyState&amp;lt;EmptyContent&amp;gt;(_ isEmpty: Bool,
                                emptyContent: @escaping () -&amp;gt; EmptyContent) -&amp;gt; some View where EmptyContent: View {
    modifier(EmptyStateViewModifier(isEmpty: isEmpty, emptyContent: emptyContent))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we can use the view modifier as expected. Here is the main list view of Make It So, with the view modifier applied:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  ForEach($viewModel.tasks) { $task in
    TaskListRowView(task: $task)
      .focused($focusedTask, equals: .row(id: task.id))
      .onSubmit {
        viewModel.createNewTask()
      }
      .swipeActions {
        Button(role: .destructive, action: { viewModel.deleteTask(task) }) {
          Label(&quot;Delete&quot;, systemImage: &quot;trash&quot;)
        }
        Button(action: { viewModel.flagTask(task) }) {
          Label(&quot;Flag&quot;, systemImage: &quot;flag&quot;)
        }
        .tint(Color(UIColor.systemOrange))
        Button(action: {}) {
          Label(&quot;Details&quot;, systemImage: &quot;ellipsis&quot;)
        }
        .tint(Color(UIColor.systemGray))
      }
  }
}
.emptyState($viewModel.tasks.isEmpty) {
  Text(&quot;No Reminders&quot;)
    .font(.title3)
    .foregroundColor(Color.secondary)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can find the source code for Make It So, including the view modifier, in the &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/develop&quot;&gt;develop&lt;/a&gt; branch of &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/develop&quot;&gt;this GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’re interested in following along with the further development of the app, &lt;a href=&quot;https://www.getrevue.co/profile/peterfriese&quot;&gt;subscribe to my newsletter&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/peterfriese/status/1453467058302291975&quot;&gt;follow this Twitter thread&lt;/a&gt; - and feel free to join the conversation!&lt;/p&gt;
&lt;p&gt;Thanks for reading! 🔥&lt;/p&gt;
</content:encoded></item><item><title>Managing Focus in SwiftUI List Views</title><link>https://peterfriese.dev/blog/2021/swiftui-list-focus/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-list-focus/</guid><description>How to manage focus in SwiftUI List Views</description><pubDate>Fri, 05 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;&lt;/p&gt;
&lt;p&gt;import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;import MakeItSoResult from &apos;./swiftui-list-focus/MakeItSo-result.mp4&apos;&lt;/p&gt;
&lt;p&gt;Managing focus is an important aspect for almost any sort of UI - getting this right helps your users navigate your app faster and more efficiently. In desktop UIs, we have come to expect being able to navigate through the input fields on a form by pressing the tab key, and on mobile it’s no less important. In Apple’s Reminders app, for example, the cursor will automatically be placed in any new reminder you create, and will advance to the next row when you tap the enter key. This way, you can add new elements very efficiently.&lt;/p&gt;
&lt;p&gt;Apple added support for handling focus in the latest version of SwiftUI - this includes both setting and observing focus.&lt;/p&gt;
&lt;p&gt;Most examples both in &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/focusstate&quot;&gt;Apple’s own documentation&lt;/a&gt; and on other people’s &lt;a href=&quot;https://swiftwithmajid.com/2020/12/02/focus-management-in-swiftui/&quot;&gt;blogs&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/watch?v=GqXVFXnLVH4&quot;&gt;videos&lt;/a&gt; only discuss how to use this in simple forms, such as a login form. Advanced use cases, such as managing focus in an editable list, aren’t covered.&lt;/p&gt;
&lt;p&gt;In this article, I will show you how to manage focus state in an app that allows users to edit elements in a list. As an example, I am going to use Make It So, a to-do list app I am working on. Make It So is a replica of Apple’s Reminders app, and the idea is to figure out how close we can get to the original using only SwiftUI and Firebase.&lt;/p&gt;
&lt;h2&gt;How to manage focus in SwiftUI&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;A word of warning:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The following will only work in SwiftUI 3 on iOS 15.2, so you will need Xcode 13.2 beta. At the time of this writing, there wasn&apos;t a build of iOS 15.2 for physical devices, so you&apos;ll only be able to use this on the Simulator - for now. I am confident Apple will make this available soon, and they might even ship a bug fix to current versions of iOS.&lt;/p&gt;
&lt;p&gt;To synchronise the focus state between the view model and the view, we can&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;add the &lt;code&gt;@FocusState&lt;/code&gt; back to the view&lt;/li&gt;
&lt;li&gt;mark &lt;code&gt;focusedReminder&lt;/code&gt; as an &lt;code&gt;@Published&lt;/code&gt; property on the view model&lt;/li&gt;
&lt;li&gt;and sync them using &lt;code&gt;onChange(of:)&lt;/code&gt;
&amp;lt;/InfoBox&amp;gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At WWDC 2021, Apple introduced &lt;code&gt;@FocusState&lt;/code&gt;, a property wrapper that can be used to track and modify focus within a scene.&lt;/p&gt;
&lt;p&gt;You can either use a &lt;code&gt;Bool&lt;/code&gt; or an &lt;code&gt;enum&lt;/code&gt; to track which element of your UI is focused.&lt;/p&gt;
&lt;p&gt;The following example makes use of an &lt;code&gt;enum&lt;/code&gt; with two cases to track focus for a simple user profile form. As you can see in the &lt;code&gt;Button&lt;/code&gt;&apos;s closure, we can programmatically set the focus, for example if the user forgot to fill out a mandatory field.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum FocusableField: Hashable {
  case firstName
  case lastName
}

struct FocusUsingEnumView: View {
  @FocusState private var focus: FocusableField?
  
  @State private var firstName = &quot;&quot;
  @State private var lastName = &quot;&quot;
  
  var body: some View {
    Form {
      TextField(&quot;First Name&quot;, text: $firstName)
        .focused($focus, equals: .firstName)
      TextField(&quot;Last Name&quot;, text: $lastName)
        .focused($focus, equals: .lastName)
      
      Button(&quot;Save&quot;) {
        if firstName.isEmpty {
          focus = .firstName
        }
        else if lastName.isEmpty {
          focus = .lastName
        }
        else {
          focus = nil
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach works fine for simple input forms that have all but a few input elements, but it’s not feasible for &lt;code&gt;List&lt;/code&gt; views or other dynamic views that display an unbounded number of elements.&lt;/p&gt;
&lt;h2&gt;How to manage focus in Lists&lt;/h2&gt;
&lt;p&gt;To manage focus in &lt;code&gt;List&lt;/code&gt; views, we can make use of the fact that Swift &lt;code&gt;enum&lt;/code&gt;s support associated values. This allows us to define an &lt;code&gt;enum&lt;/code&gt; that can hold the &lt;code&gt;id&lt;/code&gt; of a list element we want to focus:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum Focusable: Hashable {
  case none
  case row(id: String)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, we can define a local variable &lt;code&gt;focusedReminder&lt;/code&gt; that is an instance of the &lt;code&gt;Focusable&lt;/code&gt; enum and wrap it using &lt;code&gt;@FocusState&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Reminder: Identifiable {
  var id: String = UUID().uuidString
  var title: String
}

struct FocusableListView: View {
  @State var reminders: [Reminder] = Reminder.samples
  
  @FocusState var focusedReminder: Focusable?
  
  var body: some View {
    List {
      ForEach($reminders) { $reminder in
        TextField(&quot;&quot;, text: $reminder.title)
          .focused($focusedReminder, equals: .row(id: reminder.id))
      }
    }
    .toolbar {
      ToolbarItemGroup(placement: .bottomBar) {
        Button(action: { createNewReminder() }) {
          Text(&quot;New Reminder&quot;)
        }
      }
    }
  }

  // ...
  
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the user taps the &lt;em&gt;New Reminder&lt;/em&gt; toolbar button, we add a new &lt;code&gt;Reminder&lt;/code&gt; to the &lt;code&gt;reminders&lt;/code&gt; array. To set the focus into the row for this newly created reminder, all we need to do is create an instance of the &lt;code&gt;Focusable&lt;/code&gt; enum using the new reminder&apos;s &lt;code&gt;id&lt;/code&gt; as the associated value, and assign it to the &lt;code&gt;focusedReminder&lt;/code&gt; property:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct FocusableListView: View {

  // ...

  func createNewReminder() {
    let newReminder = Reminder(title: &quot;&quot;)
    reminders.append(newReminder)
    focusedReminder = .row(id: newReminder.id)
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that is pretty much everything you need to implement basic focus management in SwiftUI &lt;code&gt;List&lt;/code&gt; views!&lt;/p&gt;
&lt;h2&gt;Handling the &lt;em&gt;Enter&lt;/em&gt; Key&lt;/h2&gt;
&lt;p&gt;Let’s now turn our focus to another feature of Apple’s Reminder app that will improve the UX of our application: adding new elements (and focusing them) when the user hits the &lt;em&gt;Enter&lt;/em&gt; key.&lt;/p&gt;
&lt;p&gt;We can use the &lt;code&gt;.onSubmit&lt;/code&gt; view modifier to run code when the user submits a value to a view. By default, this will be triggered when the user taps the &lt;em&gt;Enter&lt;/em&gt; key:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;... 
TextField(&quot;&quot;, text: $reminder.title)
  .focused($focusedTask, equals: .row(id: reminder.id))
  .onSubmit {
    createNewTask()
  }
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works fine, but all new elements will be added to the &lt;em&gt;end&lt;/em&gt; of the list. This is a bit unexpected in case the user was just editing a to-do at the beginning or in the middle of the list.&lt;/p&gt;
&lt;p&gt;Let’s update our code for inserting new items and make sure new items are inserted directly after the currently focused element:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;...
func createNewTask() {
  let newReminder = Reminder(title: &quot;&quot;)
  
  // if any row is focused, insert the new task after the focused row
  if case .row(let id) = focusedTask {
    if let index = reminders.firstIndex(where: { $0.id == id } ) {
      reminders.insert(newReminder, at: index + 1)
    }
  }
  // no row focused: append at the end of the list
  else {
    reminders.append(newReminder)
  }
  
  // focus the new task
  focusedTask = .row(id: newReminder.id)
}
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works great, but there is a small issue with this: if the user hits the &lt;em&gt;Enter&lt;/em&gt; key several times in a row without entering any text, we will end up with a bunch of empty rows - not ideal. The Reminders app automatically removes empty rows, so let&apos;s see if we can implement this as well.&lt;/p&gt;
&lt;p&gt;If you’ve followed along, you might notice another issue: the code for our view is getting more and more crowded, and we’re mixing declarative UI code with a lot of imperative code.&lt;/p&gt;
&lt;h2&gt;What about MVVM?&lt;/h2&gt;
&lt;p&gt;Now those of you who have been following my blog and &lt;a href=&quot;https://www.youtube.com/c/PeterFriese&quot;&gt;my videos&lt;/a&gt; know that I am a fan of using the MVVM approach in SwiftUI, so let’s take a look at how we can introduce a view model to declutter the view code &lt;em&gt;and&lt;/em&gt; implement a solution for removing empty rows at the same time.&lt;/p&gt;
&lt;p&gt;Ideally, the view model should contain the array of &lt;code&gt;Reminder&lt;/code&gt;s, the focus state, and the code to create a new reminder:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ReminderListViewModel: ObservableObject {
  @Published var reminders: [Reminder] = Reminder.samples
  
  @FocusState var focusedReminder: Focusable?
  
  func createNewReminder() {
    let newReminder = Reminder(title: &quot;&quot;)

    // if any row is focused, insert the new reminder after the focused row
    if case .row(let id) = focusedReminder {
      if let index = reminders.firstIndex(where: { $0.id == id } ) {
        reminders.insert(newReminder, at: index + 1)
      }
    }
    // no row focused: append at the end of the list
    else {
      reminders.append(newReminder)
    }
    
    // focus the new reminder
    focusedReminder = .row(id: newReminder.id)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how we’re accessing the &lt;code&gt;focusedReminder&lt;/code&gt; focus state inside of &lt;code&gt;createNewReminder&lt;/code&gt; to find out where to insert the new reminder, and then set the focus on the newly added / inserted reminder.&lt;/p&gt;
&lt;p&gt;Obviously, the &lt;code&gt;FocusableListView&lt;/code&gt; view needs to be updated as well to reflect the fact that we&apos;re no longer using a local &lt;code&gt;@State&lt;/code&gt; variable, but an &lt;code&gt;@ObservableObject&lt;/code&gt; instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct FocusableListView: View {
  @StateObject var viewModel = ReminderListViewModel()
  
  var body: some View {
    List {
      ForEach($viewModel.reminders) {
        TextField(&quot;&quot;, text: $reminder.title)
          .focused(viewModel.$focusedReminder, equals: .row(id: reminder.id))
          .onSubmit {
            viewModel.createNewReminder()
          }
      }
    }
    .toolbar {
      ToolbarItem(placement: .bottomBar) {
        Button(action: { viewModel.createNewReminder() }) {
          Text(&quot;New Reminder&quot;)
        }
      }
    }
  } 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This all looks great, but when running this code, you will notice the focus handling no longer works, and instead we receive a SwiftUI runtime warning that says &lt;em&gt;Accessing FocusState&apos;s value outside of the body of a View. This will result in a constant Binding of the initial value and will not update&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is because &lt;code&gt;@FocusState&lt;/code&gt; conforms to &lt;code&gt;DynamicProperty&lt;/code&gt;, which can only be used inside views.&lt;/p&gt;
&lt;p&gt;So we need to find another way to synchronise the focus state between the view and the view model. One way to react to changes on properties of views is the &lt;code&gt;.onChange(of:)&lt;/code&gt; view modifier.&lt;/p&gt;
&lt;p&gt;To synchronise the focus state between the view model and the view, we can&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;add the &lt;code&gt;@FocusState&lt;/code&gt; back to the view&lt;/li&gt;
&lt;li&gt;mark &lt;code&gt;focusedReminder&lt;/code&gt; as an &lt;code&gt;@Published&lt;/code&gt; property on the view model&lt;/li&gt;
&lt;li&gt;and sync them using &lt;code&gt;onChange(of:)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ReminderListViewModel: ObservableObject {
  @Published var reminders: [Reminder] = Reminder.samples
  
  @Published var focusedReminder: Focusable?
  // ...
}

struct FocusableListView: View {
  @StateObject var viewModel = ReminderListViewModel()
  
  @FocusState var focusedReminder: Focusable?
  
  var body: some View {
    List {
      ForEach($viewModel.reminders) { $reminder in
        // ...
      }
    }
    .onChange(of: focusedReminder)  { viewModel.focusedReminder = $0 }
    .onChange(of: viewModel.focusedReminder) { focusedReminder = $0 }
    // ...
  } 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Side note:&lt;/strong&gt; this can be cleaned up even further by extracting the code for syncing into an extension on &lt;code&gt;View&lt;/code&gt;.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;And with this, we’ve cleaned up our implementation - the view focuses on the display aspects, whereas the view model handles updating the data model and translating between the view and the model&lt;/p&gt;
&lt;h2&gt;Eliminating empty elements&lt;/h2&gt;
&lt;p&gt;Using a view model gives us another nice benefit - since the &lt;code&gt;focusedReminder&lt;/code&gt; property on the view model is a published property, we can attach a Combine pipeline to it and react to changes of the property. This will allow us to detect when the previously focused element is an empty element and consequently remove it.&lt;/p&gt;
&lt;p&gt;To do this, we will need an additional property (&lt;code&gt;previousFocusedReminder&lt;/code&gt; (1)) on the view model to keep track of the previously focused &lt;code&gt;Reminder&lt;/code&gt;, and then install a Combine pipeline that removes empty &lt;code&gt;Reminder&lt;/code&gt;s once their row loses focus (2):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ReminderListViewModel: ObservableObject {
  @Published var reminders: [Reminder] = Reminder.samples
  
  @Published var focusedReminder: Focusable?
  var previousFocusedReminder: Focusable? //(1)
  
  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()
  
  init() {
    $focusedReminder
      .compactMap { focusedReminder -&amp;gt; Int? in
        defer { self.previousFocusedReminder = focusedReminder }
        
        guard focusedReminder != nil else { return nil }
        guard case .row(let previousId) = self.previousFocusedReminder else { return nil }
        guard let previousIndex = self.reminders.firstIndex(where: { $0.id == previousId } ) else { return nil }
        guard self.reminders[previousIndex].title.isEmpty else { return nil }
        
        return previousIndex
      }
      .delay(for: 0.01, scheduler: RunLoop.main) // &amp;lt;-- this helps reduce the visual jank
      .sink { index in
        self.reminders.remove(at: index) //(2)
      }
      .store(in: &amp;amp;cancellables)
  }

  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a whirlwind overview of how to implement focus management for SwiftUI &lt;code&gt;List&lt;/code&gt;s. The result looks pretty compelling:&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={MakeItSoResult} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;To see how this code can be used in a larger context, check out the &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/develop&quot;&gt;repo for MakeItSo&lt;/a&gt;. MakeItSo’s UI is much closer to the original - after all, it’s an attempt to replicate the Reminders app as closely as possible.&lt;/p&gt;
&lt;p&gt;The code lives in the &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/develop&quot;&gt;develop branch&lt;/a&gt;, and here are the two commits that contain the code we discussed in this blog post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/commit/fbcc56fe167c70d96ccd83a656cb4401b90fd940&quot;&gt;✨ Implement focus management · peterfriese/MakeItSo@fbcc56f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/commit/0dd0b7274ef56ecbda6d20aa3562aa4a9fc0d495&quot;&gt;✨ Remove empty tasks when cell loses focus · peterfriese/MakeItSo@0dd0b72&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to &lt;a href=&quot;https://twitter.com/peterfriese/status/1453467058302291975&quot;&gt;follow along&lt;/a&gt; as I continue developing MakeItSo, &lt;a href=&quot;https://www.getrevue.co/profile/peterfriese&quot;&gt;subscribe to my newsletter&lt;/a&gt;, or &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading! 🔥&lt;/p&gt;
</content:encoded></item><item><title>Swipe Actions in SwiftUI 3</title><link>https://peterfriese.dev/blog/2021/swiftui-listview-part4/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-listview-part4/</guid><description>Easily add Swipe Actions to your UIs using SwiftUI</description><pubDate>Mon, 18 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;In this part of The Ultimate Guide to List Views, we will look at Swipe Actions. Swipe Actions are used in many apps, most prominently in Apple’s own Mail app. They provide a well-known and easy-to-use UI affordance to allow users to perform actions on list items.&lt;/p&gt;
&lt;p&gt;UIKit has supported Swipe Actions since iOS 11, but SwiftUI didn’t support Swipe Actions until WWDC 2021.&lt;/p&gt;
&lt;p&gt;In this post, we will look at the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Swipe-to-delete&lt;/strong&gt; using the &lt;code&gt;onDelete&lt;/code&gt; modifier&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deleting and moving items&lt;/strong&gt; using &lt;code&gt;EditButton&lt;/code&gt; and the &lt;code&gt;.editMode&lt;/code&gt; environmental value&lt;/li&gt;
&lt;li&gt;Using &lt;strong&gt;Swipe Actions&lt;/strong&gt; (this is the most flexible approach, which also gives us a wealth of styling options)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Swipe-to-delete&lt;/h2&gt;
&lt;p&gt;This feature was available in SwiftUI right from the beginning. It is pretty straight-forward to use, but also pretty basic (or rather inflexible). To add swipe-to-delete to a &lt;code&gt;List&lt;/code&gt;view, all you need to do is apply the &lt;code&gt;onDelete&lt;/code&gt; modifier to a &lt;code&gt;ForEach&lt;/code&gt; loop inside a &lt;code&gt;List&lt;/code&gt; view. This modifier expects a closure with one parameter that contains an &lt;code&gt;IndexSet&lt;/code&gt;, indicating which rows to delete.&lt;/p&gt;
&lt;p&gt;Here is a code snippet that shows a simple &lt;code&gt;List&lt;/code&gt; with an &lt;code&gt;onDelete&lt;/code&gt; modifier. When the user swipes to delete, the closure will be called, which will consequently remove the respective row from the array of items backing the &lt;code&gt;List&lt;/code&gt; view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct SwipeToDeleteListView: View {
  @State fileprivate var items = [
    Item(title: &quot;Puzzle&quot;, iconName: &quot;puzzlepiece&quot;, badge: &quot;Nice!&quot;),
    Item(title: &quot;Controller&quot;, iconName: &quot;gamecontroller&quot;, badge: &quot;Clicky!&quot;),
    Item(title: &quot;Shopping cart&quot;, iconName: &quot;cart&quot;, badge: &quot;$$$&quot;),
    Item(title: &quot;Gift&quot;, iconName: &quot;giftcard&quot;, badge: &quot;:-)&quot;),
    Item(title: &quot;Clock&quot;, iconName: &quot;clock&quot;, badge: &quot;Tick tock&quot;),
    Item(title: &quot;People&quot;, iconName: &quot;person.2&quot;, badge: &quot;2&quot;),
    Item(title: &quot;T-Shirt&quot;, iconName: &quot;tshirt&quot;, badge: &quot;M&quot;)
  ]

  var body: some View {
    List {
      ForEach(items) { item in
        Label(item.title, systemImage: item.iconName)
      }
      .onDelete { indexSet in
        items.remove(atOffsets: indexSet)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s actually quite convenient that &lt;code&gt;onDelete&lt;/code&gt; passes an &lt;code&gt;IndexSet&lt;/code&gt; to indicate which item(s) should be deleted, as &lt;code&gt;Array&lt;/code&gt; provides a method &lt;code&gt;remove(atOffsets:)&lt;/code&gt; that takes an &lt;code&gt;IndexSet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is worth noting that you cannot apply &lt;code&gt;onDelete&lt;/code&gt; to &lt;code&gt;List&lt;/code&gt; directly - you need to use a &lt;code&gt;ForEach&lt;/code&gt; loop instead and nest it inside a &lt;code&gt;List&lt;/code&gt;. I am not entirely sure why the SwiftUI team decided to implement it this way - if you have any clue (or work on the SwiftUI team), please get in touch with me!&lt;/p&gt;
&lt;h2&gt;Moving and Deleting items using EditMode&lt;/h2&gt;
&lt;p&gt;For some applications, it makes sense to let users rearrange items by dragging them across the list. SwiftUI makes implementing this super easy - all you need to do is apply the &lt;code&gt;onMove&lt;/code&gt; view modifier to a &lt;code&gt;List&lt;/code&gt; and then update the underlying data structure accordingly.&lt;/p&gt;
&lt;p&gt;Here is a snippet that shows how to implement this for a simple array:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  ForEach(items) { item in
    Label(item.title, systemImage: item.iconName)
  }
  .onDelete { indexSet in
    items.remove(atOffsets: indexSet)
  }
  .onMove { indexSet, index in
    items.move(fromOffsets: indexSet, toOffset: index)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, this is made easy thanks to &lt;code&gt;Array.move&lt;/code&gt;, which expects exactly the parameters that we receive in &lt;code&gt;onDelete&lt;/code&gt;’s closure.&lt;/p&gt;
&lt;p&gt;To turn on edit mode for a &lt;code&gt;List&lt;/code&gt;, there are two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;.editMode&lt;/code&gt; environment value&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;EditButton&lt;/code&gt; view&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Under the hood, both approaches make use of SwiftUI’s environment. The following snippet demonstrates how to use the &lt;code&gt;EditButton&lt;/code&gt; to allow the user to turn on edit mode for the list:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  ForEach(items) { item in
    Label(item.title, systemImage: item.iconName)
  }
  .onDelete { indexSet in
    items.remove(atOffsets: indexSet)
  }
  .onMove { indexSet, index in
    items.move(fromOffsets: indexSet, toOffset: index)
  }
}
.toolbar {
  EditButton()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Swipe Actions&lt;/h2&gt;
&lt;p&gt;For anything that goes beyond swipe-to-delete and &lt;code&gt;EditMode&lt;/code&gt;, SwiftUI now supports Swipe Actions. This new API gives us a lot of control over how to display swipe actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can define different swipe actions per row&lt;/li&gt;
&lt;li&gt;We can specify the text, icon and tint color to use for each individual action&lt;/li&gt;
&lt;li&gt;We can add add actions to the leading and trailing edge of a row&lt;/li&gt;
&lt;li&gt;We can enable of disable full swipe for the first action on either end of the row, allowing users to trigger the action by completely swiping the row to the respective edge&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Basic Swipe Actions&lt;/h3&gt;
&lt;p&gt;Let’s look at a simple example how to use this new API. To register a Swipe Action for a &lt;code&gt;List&lt;/code&gt; row, we need to call the &lt;code&gt;swipeActions&lt;/code&gt; view modifier. Inside the closure of the view modifier, we can set up one (or more) &lt;code&gt;Button&lt;/code&gt;s to implement the action itself.&lt;/p&gt;
&lt;p&gt;The following code snippet demonstrates how to add a simple swipe action to a &lt;code&gt;List&lt;/code&gt; view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(viewModel.items) { item in
  Text(item.title)
    .fontWeight(item.isRead ? .regular : .bold)
    .swipeActions {
      Button (action: { viewModel.markItemRead(item) }) {
        if let isRead = item.isRead, isRead == true {
          Label(&quot;Read&quot;, systemImage: &quot;envelope.badge.fill&quot;)
        }
        else {
          Label(&quot;Unread&quot;, systemImage: &quot;envelope.open.fill&quot;)
        }
      }
      .tint(.blue)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s worth noting that the &lt;code&gt;swipeActions&lt;/code&gt; modifier is invoked on the view that represents the row. In this case, it is a simple &lt;code&gt;Text&lt;/code&gt; view, but for more advanced lists, this might as well be a &lt;code&gt;HStack&lt;/code&gt; or &lt;code&gt;VStack&lt;/code&gt;. This is different from the &lt;code&gt;onDelete&lt;/code&gt; modifier (which needs to be applied to a &lt;code&gt;ForEach&lt;/code&gt; loop inside a &lt;code&gt;List&lt;/code&gt; view) and it gives us the flexibility to apply a different set of actions depending on the row.&lt;/p&gt;
&lt;p&gt;Also note that each swipe action is represented by a &lt;code&gt;Button&lt;/code&gt;. If you use any other view, SwiftUI will not register it, and no action will be shown. Likewise, if you try to apply the &lt;code&gt;swipeActions&lt;/code&gt; modifier to the &lt;code&gt;List&lt;/code&gt; or a &lt;code&gt;ForEach&lt;/code&gt; loop, the modifier will be ignored.&lt;/p&gt;
&lt;h3&gt;Specifying the edge&lt;/h3&gt;
&lt;p&gt;By default, swipe actions will be added to the trailing edge of the row. This is why, in the previous example, the &lt;em&gt;mark as read/unread&lt;/em&gt; action was added to the trailing edge. To add the action to the leading edge (just like in Apple’s Mail app), all we have to do is specify the &lt;code&gt;edge&lt;/code&gt; parameter, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(viewModel.items) { item in
  Text(item.title)
    .fontWeight(item.isRead ? .regular : .bold)
    .swipeActions(edge: .leading) {
      Button (action: { viewModel.markItemRead(item) }) {
       if let isRead = item.isRead, isRead == true {
         Label(&quot;Read&quot;, systemImage: &quot;envelope.badge.fill&quot;)
       }
       else {
         Label(&quot;Unread&quot;, systemImage: &quot;envelope.open.fill&quot;)
       }
     }
   .tint(.blue)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add actions to either edge, we can call the &lt;code&gt;swipeActions&lt;/code&gt; modifier multiple times, specifying the edge we want to add the actions to.&lt;/p&gt;
&lt;p&gt;If you add swipe actions to both the leading and trailing edge, it is a good idea to be explicit about where you want to add the actions. In the following code snippet, we add add one action to the leading edge, and another one to the trailing edge.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(viewModel.items) { item in
  Text(item.title)
    .fontWeight(item.isRead ? .regular : .bold)
    .swipeActions(edge: .leading) {
      Button (action: { viewModel.markItemRead(item) }) {
        if let isRead = item.isRead, isRead == true {
          Label(&quot;Read&quot;, systemImage: &quot;envelope.badge.fill&quot;)
        }
        else {
          Label(&quot;Unread&quot;, systemImage: &quot;envelope.open.fill&quot;)
        }
      }
      .tint(.blue)
    }
    .swipeActions(edge: .trailing) {
      Button(role: .destructive, action: { viewModel.deleteItem(item) } ) {
        Label(&quot;Delete&quot;, systemImage: &quot;trash&quot;)
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might notice that we used the &lt;code&gt;role&lt;/code&gt; parameter on the &lt;code&gt;Button&lt;/code&gt; to indicate it is &lt;code&gt;.destructive&lt;/code&gt; - this instructs SwiftUI to use a red background colour for this button. We still have to implement deleting the item ourselves, though. And since the &lt;code&gt;action&lt;/code&gt; closure of the &lt;code&gt;Button&lt;/code&gt; is inside the scope of the current row, it is now much easier directly access the current list &lt;code&gt;item&lt;/code&gt; - another advantage of this API design over the previous design for &lt;code&gt;onDelete&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Swipe Actions and &lt;code&gt;onDelete&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;After reading the previous code snippet, you might be wondering why we didn’t use the &lt;code&gt;onDelete&lt;/code&gt; view modifier instead of implementing a delete action ourselves. The answer is quite simple: as stated in the documentation, SwiftUI will stop synthesising the delete functionality once you use the &lt;code&gt;swipeActions&lt;/code&gt; modifier.&lt;/p&gt;
&lt;h3&gt;Adding more Swipe Actions&lt;/h3&gt;
&lt;p&gt;To add multiple swipe actions to either edge, we can call the &lt;code&gt;swipeActions&lt;/code&gt; modifier multiple times:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(viewModel.items) { item in
  Text(item.title)
    .fontWeight(item.isRead ? .regular : .bold)
    .swipeActions(edge: .leading) {
      Button (action: { viewModel.markItemRead(item) }) {
        if let isRead = item.isRead, isRead == true {
          Label(&quot;Read&quot;, systemImage: &quot;envelope.badge.fill&quot;)
        }
        else {
          Label(&quot;Unread&quot;, systemImage: &quot;envelope.open.fill&quot;)
        }
      }
      .tint(.blue)
    }
    .swipeActions(edge: .trailing) {
      Button(role: .destructive, action: { viewModel.deleteItem(item) } ) {
        Label(&quot;Delete&quot;, systemImage: &quot;trash&quot;)
      }
    }
    .swipeActions(edge: .trailing) {
      Button (action: { selectedItem = item  } ) {
        Label(&quot;Tag&quot;, systemImage: &quot;tag&quot;)
      }
      .tint(Color(UIColor.systemOrange))
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this makes you feel uneasy, you can also add multiple buttons to the same &lt;code&gt;swipeActions&lt;/code&gt; modifier. The following code snippet results in the same UI as the previous one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(viewModel.items) { item in
  Text(item.title)
    .fontWeight(item.isRead ? .regular : .bold)
    .badge(item.badge)
    .swipeActions(edge: .leading) {
      Button (action: { viewModel.markItemRead(item) }) {
        if let isRead = item.isRead, isRead == true {
          Label(&quot;Read&quot;, systemImage: &quot;envelope.badge.fill&quot;)
        }
        else {
          Label(&quot;Unread&quot;, systemImage: &quot;envelope.open.fill&quot;)
        }
      }
      .tint(.blue)
    }
    .swipeActions(edge: .trailing) {
      Button(role: .destructive, action: { viewModel.deleteItem(item) } ) {
        Label(&quot;Delete&quot;, systemImage: &quot;trash&quot;)
      }
      Button (action: { selectedItem = item  } ) {
        Label(&quot;Tag&quot;, systemImage: &quot;tag&quot;)
      }
      .tint(Color(UIColor.systemOrange))
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you add multiple swipe actions to the same edge, they will be shown from the outside in. I.e. the first button will always appear closest to the respective edge.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Please note&lt;/strong&gt; that, although there doesn’t seem to be any limit as to how many swipe actions you
can add to either edge of a row, the number of actions that a user can comfortably use depends on
their device. For example, a list row on an iPhone 13 in portrait orientation can fit up to five
swipe actions, but they completely fill up the entire row, which not only looks strange, but also
leads to some issues when trying to tap the right button. Smaller devices, like an iPhone 6 or
even and iPhone 5, can fit even fewer swipe actions. Three or four swipe actions seem to be a
sensible limit that should work on most devices.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h3&gt;Full Swipe&lt;/h3&gt;
&lt;p&gt;By default, the first action for any given swipe direction can be invoked by using a full swipe. You can deactivate this behaviour by setting the &lt;code&gt;allowsFullSwipe&lt;/code&gt; parameter to &lt;code&gt;false&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.swipeActions(edge: .trailing, allowsFullSwipe: false) {
  Button(role: .destructive, action: { viewModel.deleteItem(item) } ) {
    Label(&quot;Delete&quot;, systemImage: &quot;trash&quot;)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Styling Your Swipe Actions&lt;/h3&gt;
&lt;p&gt;As mentioned before, setting the &lt;code&gt;role&lt;/code&gt; of a swipe action&apos;s &lt;code&gt;Button&lt;/code&gt; to &lt;code&gt;.destructive&lt;/code&gt; will automatically tint the button red. If you don&apos;t specify a &lt;code&gt;role&lt;/code&gt;, the &lt;code&gt;Button&lt;/code&gt; will be tinted in light grey. You can specify any other colour by using the &lt;code&gt;tint&lt;/code&gt; modifier on a swipe action&apos;s &lt;code&gt;Button&lt;/code&gt; - like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.swipeActions(edge: .trailing) {
  Button (action: { selectedItem = item  } ) {
    Label(&quot;Tag&quot;, systemImage: &quot;tag&quot;)
  }
  .tint(Color(UIColor.systemOrange))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the &lt;code&gt;Button&lt;/code&gt;, you can display both text labels and / or icons, using &lt;code&gt;Image&lt;/code&gt;, &lt;code&gt;Text&lt;/code&gt;, or &lt;code&gt;Label&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;SwiftUI makes it easy to implement Swipe Actions, a very popular UI pattern that allows users to invoke contextual actions on list items. In comparison to force-touching or long-pressing a list row to invoke a context menu, Swipe Actions offer a more convenient, fluent, way to interact with your app’s UI.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and hope to see you again for the final part of the series when we talk about nested lists (a.k.a trees)!&lt;/p&gt;
</content:encoded></item><item><title>Styling List Views</title><link>https://peterfriese.dev/blog/2021/swiftui-listview-part3/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-listview-part3/</guid><description>Learn how to style SwiftUI List views</description><pubDate>Wed, 29 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Lists offer a wide range of styling options, and with SwiftUI 3, it is now possible to configure almost all aspects of list views:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the overall appearance of the list itself (i.e. the list style)&lt;/li&gt;
&lt;li&gt;the look of the list cells&lt;/li&gt;
&lt;li&gt;the dividers (finally!)&lt;/li&gt;
&lt;li&gt;… and much more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this part of the series, we’re going to look at what’s possible.&lt;/p&gt;
&lt;h2&gt;List Styles&lt;/h2&gt;
&lt;p&gt;The overall look and feel of list views can be controlled with the &lt;code&gt;.listStyle&lt;/code&gt; view modifier. SwiftUI supports five different looks:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;.automatic&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.grouped&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.inset&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.insetGrouped&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.plain&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.sidebar&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;List(items) { item in
  Text(&quot;\(item.label)&quot;)
}
.listStyle(.plain)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don’t provide a style, SwiftUI will assume &lt;code&gt;.automatic&lt;/code&gt;. On iOS, &lt;code&gt;.automatic&lt;/code&gt; and &lt;code&gt;.insetGrouped&lt;/code&gt; have the same look.&lt;/p&gt;
&lt;p&gt;Instead of trying to describe in words how each of the styles look like, here is an image that shows each of the styles.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Headers and footers&lt;/h2&gt;
&lt;p&gt;All of the &lt;code&gt;List&lt;/code&gt; view styles support headers and footers. To specify a header and / or footer for a section, use one of the constructors that take a &lt;code&gt;header&lt;/code&gt; or &lt;code&gt;footer&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;It seems like my favourite way of creating headers and footers has been marked for deprecation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Section where Parent : View, Content : View, Footer : View {

    /// Creates a section with a header, footer, and the provided section content.
    /// - Parameters:
    ///   - header: A view to use as the section&apos;s header.
    ///   - footer: A view to use as the section&apos;s footer.
    ///   - content: The section&apos;s content.
    @available(iOS, deprecated: 100000.0, renamed: &quot;Section(content:header:footer:)&quot;)
    @available(macOS, deprecated: 100000.0, renamed: &quot;Section(content:header:footer:)&quot;)
    @available(tvOS, deprecated: 100000.0, renamed: &quot;Section(content:header:footer:)&quot;)
    @available(watchOS, deprecated: 100000.0, renamed: &quot;Section(content:header:footer:)&quot;)
    public init(header: Parent, footer: Footer, @ViewBuilder content: () -&amp;gt; Content)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is my preferred way of setting up header and footer for a section:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  Section(header: Text(&quot;Fruits&quot;), footer: Text(&quot;\(fruits.count) fruits&quot;)) {
    ForEach(fruits, id: \.self) { fruit in
      Label(fruit, systemImage: &quot;\(fruits.firstIndex(of: fruit) ?? 0).circle.fill&quot; )
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the new way of doing the same:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  Section {
    ForEach(fruits, id: \.self) { fruit in
      Label(fruit, systemImage: &quot;\(fruits.firstIndex(of: fruit) ?? 0).circle.fill&quot; )
    }
  } header: {
    Text(&quot;Fruits&quot;)
  } footer: {
    Text(&quot;\(fruits.count) fruits&quot;)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;ll leave it up to you to decide which one looks cleaner - when executed, they do exactly the same.&lt;/p&gt;
&lt;h2&gt;List Cells&lt;/h2&gt;
&lt;p&gt;Designing custom cells used to be a pretty complicated affair in the early days of &lt;code&gt;UITableViewController&lt;/code&gt;, and thankfully, things have gotten a lot easier since then.&lt;/p&gt;
&lt;p&gt;In SwiftUI, designing custom &lt;code&gt;List&lt;/code&gt; rows is easy to get started (just use a plain &lt;code&gt;Text&lt;/code&gt; view to represent the current item), but the possibilities are endless, as you can make use of SwiftUI’s flexible stack-based layout system. For a general introduction to building custom &lt;code&gt;List&lt;/code&gt; rows, check out the &lt;a href=&quot;../swiftui-listview-part1/&quot;&gt;first part of this series&lt;/a&gt;, which covers some basic techniques.&lt;/p&gt;
&lt;p&gt;In addition, SwiftUI supports a number of ways for styling common aspects of &lt;code&gt;List&lt;/code&gt; rows, such as their background, their inset, accent color, tint, and badges.&lt;/p&gt;
&lt;p&gt;Here is a code snippet that shows how you can configure a list row:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(items, id: \.title) { item in
  Label(item.title, systemImage: item.iconName)
    .badge(item.badge)
    // listItemTint and foregroundColor are mutually exclusive
    // .listItemTint(listItemTintColor)
    .foregroundColor(foregroundColor)
    .listRowSeparator(showSeparators == true ? .visible : .hidden)
    .listRowSeparatorTint(separatorTintColor)
    .listRowBackground(rowBackgroundColor)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Separators&lt;/h2&gt;
&lt;p&gt;As any designer will be able to tell you, the space between items is as important as the items themselves. With SwiftUI 3, it is now possible to influence the style of row separators and section separators: both the tint color and the visibility can be controlled. SwiftUI’s flexible DSL makes it easy to control this for an entire &lt;code&gt;List&lt;/code&gt; view or for individual rows and sections.&lt;/p&gt;
&lt;p&gt;To control the appearance of row separators, you can use &lt;code&gt;.listRowSeparator()&lt;/code&gt; and &lt;code&gt;.listRowSeparatorTint()&lt;/code&gt;. You can specify which edges (&lt;code&gt;.top&lt;/code&gt; or &lt;code&gt;.bottom&lt;/code&gt;) you want to configure. If you don&apos;t provide any value for the &lt;code&gt;edges&lt;/code&gt; parameter, both top &lt;em&gt;and&lt;/em&gt; bottom will be modified.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  Text(&quot;Row 1&quot;)
  Text(&quot;Row 2 (separators hidden)&quot;)
    .listRowSeparator(.hidden)
  Text(&quot;Row 3&quot;)
  Text(&quot;Row 4 (separators tinted red)&quot;)
    .listRowSeparatorTint(.red)
  Text(&quot;Row 5&quot;)
  Text(&quot;Row 6 (bottom separator hidden)&quot;)
    .listRowSeparator(.hidden, edges: .bottom)
  Text(&quot;Row 7&quot;)
  Text(&quot;Row 8 (top separator tinted blue)&quot;)
    .listRowSeparatorTint(.blue, edges: .top)
  Text(&quot;Row 9&quot;)
  Text(&quot;Row 10&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To control the appearance of section separators, use &lt;code&gt;.listSectionSeparator()&lt;/code&gt; and &lt;code&gt;.listSectionSeparatorTint()&lt;/code&gt;. Just like the view modifiers for list rows, both of these view modifiers support specifying the edges you&apos;d like to modify.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  Section(header: Text(&quot;Section 1&quot;), footer: Text(&quot;Section 1 - no styling&quot;)) {
    Text(&quot;Row 1&quot;)
  }
  Section(header: Text(&quot;Section 2&quot;), footer: Text(&quot;Section 2 - section separators hidden&quot;)) {
    Text(&quot;Row 1&quot;)
  }
  .listSectionSeparator(.hidden)

  Section(header: Text(&quot;Section 3&quot;), footer: Text(&quot;Section 3 - section separator tinted red&quot;)) {
    Text(&quot;Row 1&quot;)
  }
  .listSectionSeparatorTint(.red)

  Section(header: Text(&quot;Section 4&quot;), footer: Text(&quot;Section 4 - section separators tinted green&quot;)) {
    Text(&quot;Row 1&quot;)
  }
  .listSectionSeparatorTint(.green, edges: [.top, .bottom])

  Section(header: Text(&quot;Section 5&quot;), footer: Text(&quot;Section 5 - section separator (bottom) hidden&quot;)) {
    Text(&quot;Row 1&quot;)
  }
  .listSectionSeparator(.hidden, edges: .bottom)

  Section(&quot;Section 6&quot;) {
    Text(&quot;Row 1&quot;)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;If you’ve worked with SwiftUI since its early days, you will appreciate that it now supports styling all aspects of &lt;code&gt;List&lt;/code&gt; views, from the overall look and feel down to the tint color and visibility of the list item separators.&lt;/p&gt;
&lt;p&gt;In the next episode, we will look at a feature that puts a lot of power into your user’s hands (or rather fingertips): swipe actions! Implementing swipe actions used to be notoriously difficult in the past, and it doesn’t come at any surprise that SwiftUI is making this easier than ever before.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and until next time!&lt;/p&gt;
</content:encoded></item><item><title>Building Dynamic Lists in SwiftUI</title><link>https://peterfriese.dev/blog/2021/swiftui-listview-part2/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-listview-part2/</guid><description>SwiftUI&apos;s List View is a very powerful UI component. Learn how to use it to asynchronously fetch data, and add features like pull-to-refresh, searching, and make your list items editable.</description><pubDate>Mon, 06 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;../swiftui-listview-part1/&quot;&gt;Previously&lt;/a&gt;, we looked at how to use &lt;code&gt;List&lt;/code&gt; views to create &lt;em&gt;static&lt;/em&gt; list views. Static list views are useful for creating menus or settings screens in iOS apps, but &lt;code&gt;List&lt;/code&gt; views become a lot more useful when we connect them to a data source.&lt;/p&gt;
&lt;p&gt;Today, we’re going to look at a couple of examples how you can use &lt;code&gt;List&lt;/code&gt; views to display a &lt;em&gt;dynamic&lt;/em&gt; list of data, such as a list of books. We will also learn how to use some of the new features that Apple added to the latest version of SwiftUI in iOS 15, such as pull-to-refresh, a search UI, and an easy way to use async/await to fetch data from asynchronous APIs, such as remote services.&lt;/p&gt;
&lt;h2&gt;Displaying a list of elements&lt;/h2&gt;
&lt;p&gt;There are a number of ways to create lists, and as we will see later in this series, you can create both &lt;em&gt;flat&lt;/em&gt; lists as well as &lt;em&gt;hierarchical, nested&lt;/em&gt; lists. Since all list rows are computed on demand, &lt;code&gt;List&lt;/code&gt; views perform well even for collections with many items.&lt;/p&gt;
&lt;p&gt;The easiest way to create a &lt;code&gt;List&lt;/code&gt; view based on a collection of elements is to use its constructor that takes a &lt;code&gt;RandomAccessCollection&lt;/code&gt; and a view builder for the row content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List(collection) { element in
  // use SwiftUI views to render an individual row to display `element`
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the view builder, we get access to the individual elements of the collection in a type-safe way. This means we can access the properties of the collection elements and use SwiftUI views like &lt;code&gt;Text&lt;/code&gt; to render the individual rows, like in the following example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Book: Identifiable {
  var id = UUID()
  var title: String
  var author: String
  var isbn: String
  var pages: Int
  var isRead: Bool = false
}

extension Book {
  static let samples = [
    Book(title: &quot;Changer&quot;, author: &quot;Matt Gemmell&quot;, isbn: &quot;9781916265202&quot;, pages: 476),
    Book(title: &quot;SwiftUI for Absolute Beginners&quot;, author: &quot;Jayant Varma&quot;, isbn: &quot;9781484255155&quot;, pages: 200),
    Book(title: &quot;Why we sleep&quot;, author: &quot;Matthew Walker&quot;, isbn: &quot;9780141983769&quot;, pages: 368),
    Book(title: &quot;The Hitchhiker&apos;s Guide to the Galaxy&quot;, author: &quot;Douglas Adams&quot;, isbn: &quot;9780671461492&quot;, pages: 216)
  ]
}

private class BooksViewModel: ObservableObject {
  @Published var books: [Book] = Book.samples
}

struct BooksListView: View {
  @StateObject fileprivate var viewModel = BooksViewModel()
  var body: some View {
    List(viewModel.books) { book in
      Text(&quot;\(book.title) by \(book.author)&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As this view acts as the owner of the data we want to display, we use a &lt;code&gt;@StateObject&lt;/code&gt; to hold the view model. The view model exposes a published property which holds the list of books. In the interest of simplicity, this is a static list, but in a real-world application you would fetch this data from a remote API or a local database.&lt;/p&gt;
&lt;p&gt;Notice how we can access the properties of the &lt;code&gt;Book&lt;/code&gt; elements inside the &lt;code&gt;List&lt;/code&gt; by writing &lt;code&gt;book.title&lt;/code&gt; or &lt;code&gt;book.author&lt;/code&gt;. Here, we use a &lt;code&gt;Text&lt;/code&gt; view to display the title and the author of a book using string interpolation.&lt;/p&gt;
&lt;p&gt;Thanks to SwiftUI’s declarative syntax, we can easily build more complex custom UIs to present data - just like we saw last time.&lt;/p&gt;
&lt;p&gt;Let’s replace the &lt;code&gt;Text&lt;/code&gt; view in the above snippet with a more elaborate row that displays the book cover, title, author and number of pages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...
List(viewModel.books) { book in
  HStack(alignment: .top) {
    Image(book.mediumCoverImageName)
      .resizable()
      .aspectRatio(contentMode: .fit)
      .frame(height: 90)
    VStack(alignment: .leading) {
      Text(book.title)
        .font(.headline)
      Text(&quot;by \(book.author)&quot;)
        .font(.subheadline)
      Text(&quot;\(book.pages) pages&quot;)
        .font(.subheadline)
    }
    Spacer()
  }
}
// ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using Xcode’s refactoring tools for SwiftUI, we can extract this code into a custom view, to make our code easier to read.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private struct BookRowView: View {
  var book: Book

  var body: some View {
    HStack(alignment: .top) {
      Image(book.mediumCoverImageName)
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(height: 90)
      VStack(alignment: .leading) {
        Text(book.title)
          .font(.headline)
        Text(&quot;by \(book.author)&quot;)
          .font(.subheadline)
        Text(&quot;\(book.pages) pages&quot;)
          .font(.subheadline)
      }
      Spacer()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out my video on building SwiftUI views to see this (and other) refactoring in action:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;youtube https://www.youtube.com/watch?v=UhDdtdeW63k&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Since we’re not planning to modify the data inside the list row (or inside a details view), we pass the list item to the row as a simple reference. If we want to modify the data inside the list row (e.g. by marking a book as a favourite, or passing it on to an child screen where the user can edit the book details), we’ll have to use a list binding.&lt;/p&gt;
&lt;h2&gt;Using List Bindings to allow modifying list items&lt;/h2&gt;
&lt;p&gt;Normally, data inside a view is unmodifiable. To modify data, it needs to be managed as a &lt;code&gt;@State&lt;/code&gt; property or a &lt;code&gt;@ObservedObject&lt;/code&gt; view model. To allows users to modify data in a child view (e.g. a &lt;code&gt;TextField&lt;/code&gt; or a details screen), we need to use a binding to connect the data in the child view to the state in the parent view.&lt;/p&gt;
&lt;p&gt;Until SwiftUI 3, there wasn’t a direct way to get a binding to the elements of the list, so people had to come up with their own solutions. I’ve written about this before in this &lt;a href=&quot;https://peterfriese.dev/swiftui-list-item-bindings-behind-the-scenes/&quot;&gt;blog post&lt;/a&gt;, in which I contrasted incorrect and correct ways to do this.&lt;/p&gt;
&lt;p&gt;With SwiftUI 3, Apple has introduced a straight-forward way to access list items as bindings, using the following syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List($collection) { $element in
  TextField(&quot;Name&quot;, text: $element.name)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To allow users of our sample app to edit the title of a book inline in the list view, all we have to do is to update the book list view as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List($viewModel.books) { $book in
  TextField(&quot;Book title&quot;,
            text: $book.title,
            prompt: Text(&quot;Enter the book title&quot;))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, this also works for custom views. Here is how to update the &lt;code&gt;BookRowView&lt;/code&gt; to make the book title editable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct EditableBooksListView: View {
  // ...

  var body: some View {
    List($viewModel.books) { $book in
      EditableBookRowView(book: $book)
    }
  }
}


private struct EditableBookRowView: View {
  @Binding var book: Book

  var body: some View {
    HStack(alignment: .top) {
      Image(book.mediumCoverImageName)
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(height: 90)
      VStack(alignment: .leading) {
        TextField(&quot;Book title&quot;, text: $book.title, prompt: Text(&quot;Enter the book title&quot;))
          .font(.headline)
        Text(&quot;by \(book.author)&quot;)
          .font(.subheadline)
        Text(&quot;\(book.pages) pages&quot;)
          .font(.subheadline)
      }
      Spacer()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key point here is to use &lt;code&gt;@Binding&lt;/code&gt; in the child view. By doing so, the parent view retains ownership of the data that you pass in to the child view, while letting the child view modify the data. The &lt;em&gt;source of truth&lt;/em&gt; is the &lt;code&gt;@Published&lt;/code&gt; property on the &lt;code&gt;ObservableObject&lt;/code&gt; in the parent view.&lt;/p&gt;
&lt;p&gt;To read more about list bindings, and how this feature works under the hood, check out my article &lt;a href=&quot;https://peterfriese.dev/swiftui-list-item-bindings-behind-the-scenes/&quot;&gt;SwiftUI List Bindings&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Asynchronously fetching data&lt;/h2&gt;
&lt;p&gt;The next sections of this article have one thing in common - they’re all based on Apple’s new APIs for handling asynchronous code.&lt;/p&gt;
&lt;p&gt;At WWDC 21, Apple introduced Swift’s new concurrency model as part of Swift 5.5. This next version of Swift is currently in beta, but you can already get yours hands on it and experiment with the new features by downloading the beta versions of Xcode 13 from &lt;a href=&quot;https://developer.apple.com/download/&quot;&gt;https://developer.apple.com/download/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the previous examples, we used a static list of data. The advantage of this approach is that we didn’t have to fetch (and wait for) this data, as it was already in memory. This was fine for the examples, as it allowed us to focus on what’s relevant, but it doesn’t reflect reality. In a real-world application, we usually display data from remote APIs, and this usually means performing asynchronous calls: while we’re waiting for the results to come in from the remote API, the app needs to continue updating the UI. If it didn’t do so, users might get the impression the app was hanging or even crashed.&lt;/p&gt;
&lt;p&gt;So in the next examples, I’m going to demonstrate how to make use of Swift’s new concurrency model to handle asynchronous code.&lt;/p&gt;
&lt;p&gt;A good moment to fetch data is when the user navigates to a new screen and the screen just appears. In previous versions of SwiftUI, using the &lt;code&gt;.onAppear&lt;/code&gt; view modifier was a good place to request data. Starting with iOS 15, SwiftUI includes a new view modifier that makes this even easier: &lt;code&gt;.task&lt;/code&gt;. It will start an asynchronous &lt;code&gt;Task&lt;/code&gt; when the view appears, and will cancel this task once the view disappears (if the task is still running). This is useful if your task is a long-running download that you automatically want to abort when the user leaves the screen.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;.task&lt;/code&gt; is as easy as applying it to your &lt;code&gt;List&lt;/code&gt; view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct AsyncFetchBooksListView: View {
  @StateObject fileprivate var viewModel = AsyncFetchBooksViewModel()

  var body: some View {
    List(viewModel.books) { book in
      AsyncFetchBookRowView(book: book)
    }
    .overlay {
      if viewModel.fetching {
        ProgressView(&quot;Fetching data, please wait...&quot;)
          .progressViewStyle(CircularProgressViewStyle(tint: .accentColor))
      }
    }
    .animation(.default, value: viewModel.books)
    .task {
      await viewModel.fetchData()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the view model, you can then use asynchronous APIs to fetch data. In this example, I’ve mocked the backend to make the code a bit easier to read, and added an artificial delay:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private class AsyncFetchBooksViewModel: ObservableObject {
  @Published var books = [Book]()
  @Published var fetching = false

  func fetchData() async {
    fetching = true

    await Task.sleep(2_000_000_000)
    books = Book.samples

    fetching = false
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’d try and run the code like this, you would end up with a runtime warning, saying that &lt;em&gt;&quot;Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The reason for this runtime error is that the code inside &lt;code&gt;refreshable&lt;/code&gt; is not executed on the main thread. However, UI updates &lt;em&gt;must&lt;/em&gt; be executed on the main thread. In the past, we would’ve had to use &lt;code&gt;DispatchQueue.main.async {  … }&lt;/code&gt; to make sure any UI updates are executed on the main thread. However, with Swift’s new concurrency model, there is an easier way: all we have to do is to any methods (or classes) that perform UI updates using the &lt;code&gt;@MainActor&lt;/code&gt; property wrapper. This instructs the compiler to switch to the main actor when executing this code, and thus make sure any UI updates run on the main thread. Here&apos;s the updated code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private class AsyncFetchBooksViewModel: ObservableObject {
  @Published var books = [Book]()
  @Published var fetching = false

  @MainActor
  func fetchData() async {
    fetching = true

    await Task.sleep(2_000_000_000)
    books = Book.samples

    fetching = false
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more about Swift’s new concurrency model, check out &lt;a href=&quot;https://www.youtube.com/playlist?list=PLsnLd2esiGRQ1qkp__tAfXI0qb-TgwtJj&quot;&gt;this video series on YouTube&lt;/a&gt;, as well as the following articles on my blog:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://peterfriese.dev/swiftui-concurrency-essentials-part1/&quot;&gt;Getting Started with async/await in SwiftUI - SwiftUI Concurrency Essentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://peterfriese.dev/swiftui-concurrency-essentials-part2/&quot;&gt;Cooperative Task Cancellation - SwiftUI Concurrency Essentials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Pull to refresh&lt;/h2&gt;
&lt;p&gt;Unless you use an SDK like &lt;a href=&quot;https://firebase.google.com/docs/firestore&quot;&gt;Cloud Firestore&lt;/a&gt; that allows you to listen to updates in your backend in realtime, you will want to add some UI affordances to your app that make it easy for your users to request the latest data. One of the most common ways to let users refresh data is &lt;em&gt;pull to refresh&lt;/em&gt;, made popular in &lt;a href=&quot;https://www.imore.com/hall-fame-loren-brichter-and-tweetie&quot;&gt;2008 by Loren Brichter in the Tweetie app&lt;/a&gt; (later acquired by Twitter and relaunched as Twitter for iOS).&lt;/p&gt;
&lt;p&gt;SwiftUI makes it easy to add this functionality to your app with just a few lines of code, thanks to its declarative nature. And - as mentioned above - this feature also makes use of Swift’s new concurrency model, to ensure that your app’s UI remains responsive even while it needs to wait for any updates to arrive.&lt;/p&gt;
&lt;p&gt;Adding the &lt;code&gt;refreshable&lt;/code&gt; view modifier to your &lt;code&gt;List&lt;/code&gt; view is all it takes to add &lt;em&gt;pull to refresh&lt;/em&gt; to your app:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct RefreshableBooksListView: View {
  @StateObject var viewModel = RefreshableBooksViewModel()
  var body: some View {
    List(viewModel.books) { book in
      RefreshableBookRowView(book: book)
    }
    .refreshable {
      await viewModel.refresh()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As indicated by the &lt;code&gt;await&lt;/code&gt; keyword, &lt;code&gt;refreshable&lt;/code&gt; opens an asynchronous execution context. This requires that the code you&apos;re calling from within &lt;code&gt;refreshable&lt;/code&gt; can execute asynchronously (if the code you&apos;re calling can execute non-asynchronously, because it returns immediately, that&apos;s fine as well, but more often than not you’ll want to communicate with a remote API that requires being called asynchronously).&lt;/p&gt;
&lt;p&gt;To give you an idea of how this might look like, I’ve created a view model that simulates an asynchronous remote API by adding some artificial wait time:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class RefreshableBooksViewModel: ObservableObject {
  @Published var books: [Book] = Book.samples

  private func generateNewBook() -&amp;gt; Book {
    let title = Lorem.sentence
    let author = Lorem.fullName
    let pageCount = Int.random(in: 42...999)
    return Book(title: title, author: author, isbn: &quot;9781234567890&quot;, pages: pageCount)
  }

  func refresh() async {
    // in Xcode 13 b1 and b2, this crashes.
    // Add SWIFT_DEBUG_CONCURRENCY_ENABLE_COOPERATIVE_QUEUES = NO
    // to the target&apos;s environment values as a workaround
    await Task.sleep(2_000_000_000)
    let book = generateNewBook()
    books.insert(book, at: 0)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s take a look at this code to understand what’s going on.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As in the previous samples, &lt;code&gt;books&lt;/code&gt; is a published property that the view subscribes to.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;generateNewBook&lt;/code&gt; is a local function that produces a random new &lt;code&gt;Book&lt;/code&gt; instance using the excellent &lt;a href=&quot;https://github.com/lukaskubanek/LoremSwiftum&quot;&gt;LoremSwiftum&lt;/a&gt; library.&lt;/li&gt;
&lt;li&gt;Inside &lt;code&gt;refresh&lt;/code&gt;, we call &lt;code&gt;generateBook&lt;/code&gt; to produce a new book and then insert it into the published property &lt;code&gt;books&lt;/code&gt;, but before we do so, we tell the app to sleep for 2 seconds, using the &lt;code&gt;Task.sleep&lt;/code&gt; call. This is an asynchronous call, so we need to use &lt;code&gt;await&lt;/code&gt; to call it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Just the same as in the previous example, this code will produce a purple runtime warning: &lt;em&gt;&quot;Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates&quot;,&lt;/em&gt; so we need to use &lt;code&gt;@MainActor&lt;/code&gt; to ensure all updates happen on the main actor. This time, instead of marking just the &lt;code&gt;refresh&lt;/code&gt; method, we&apos;re going to mark the entire view model as &lt;code&gt;@MainActor&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@MainActor
class RefreshableBooksViewModel: ObservableObject {
  // ...

  func refresh() async {
    // ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One final adjustment before we can wrap up this section: you will notice that, when adding new items to the list by pulling to refresh, the newly added items will appear instantly, without a smooth transitions.&lt;/p&gt;
&lt;p&gt;Thanks to SwiftUI’s declarative syntax, adding animations to make this feel more natural is super easy: all we need to do is adding an &lt;code&gt;animation&lt;/code&gt; view modifier to the &lt;code&gt;List&lt;/code&gt; view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...
List(viewModel.books) { book in
  RefreshableBookRowView(book: book)
}
.animation(.default, value: viewModel.books)
// ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By providing the &lt;code&gt;value&lt;/code&gt; parameter, we can make sure this animation is only run when the contents of the list view changes, for example when new items are inserted or removed.&lt;/p&gt;
&lt;p&gt;To perfect the animations, we’ll also add a short pause to the end of the &lt;code&gt;refresh&lt;/code&gt; function on the view model - this makes sure that the new rows appear with a smooth transition before the progress spinner disappears:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func refresh() async {
  // in Xcode 13 b1 and b2, this crashes. Add SWIFT_DEBUG_CONCURRENCY_ENABLE_COOPERATIVE_QUEUES = NO to the target&apos;s environment values as a workaround
  await Task.sleep(2_000_000_000)
  let book = generateNewBook()
  books.insert(book, at: 0)

  // the following line, in combination with the `.animation` modifier, makes sure we have a smooth animation
  await Task.sleep(500_000_000)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Searching&lt;/h2&gt;
&lt;p&gt;SwiftUI makes it easy to implement search in &lt;code&gt;List&lt;/code&gt; views - all you need to do is apply the &lt;code&gt;.searchable&lt;/code&gt; view modifier to the list view, and SwiftUI will handle all the UI aspects for you automatically: it will display a search field (and make sure it is off screen when you first display the list view, just like you&apos;d expect from a native app). It also has all the UI affordances to trigger the search and clear the search field).&lt;/p&gt;
&lt;p&gt;The only thing that’s left to do is to actually perform the search and provide the appropriate result set.&lt;/p&gt;
&lt;p&gt;Generally speaking, a search screen can either act locally (i.e. filter the items being displayed in a list view), or remotely (i.e. perform a query against a remote API, and only display the results of this call).&lt;/p&gt;
&lt;p&gt;For this section, we’re going to look at filtering the elements being displayed in the list view. To do so, we’ll be using a combination of async/await and Combine.&lt;/p&gt;
&lt;p&gt;To get started, we’ll build a simple &lt;code&gt;List&lt;/code&gt; view that displays a list of books from a view model. This should look very familiar to you, as we’re in fact reusing much of the code we’ve used for the previous examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct SearchableBooksListView: View {
  @StateObject var viewModel = SearchableBooksViewModel()
  var body: some View {
    List(viewModel.books) { book in
      SearchableBookRowView(book: book)
    }
  }
}

struct SearchableBookRowView: View {
  var book: Book

  var body: some View {
    HStack(alignment: .top) {
      Image(book.mediumCoverImageName)
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(height: 90)
      VStack(alignment: .leading) {
        Text(book.title)
          .font(.headline)
        Text(&quot;by \(book.author)&quot;)
          .font(.subheadline)
        Text(&quot;\(book.pages) pages&quot;)
          .font(.subheadline)
      }
      Spacer()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The view model is very similar to the ones we used previously, with one important difference: the collection of books is empty initially:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SearchableBooksViewModel: ObservableObject {
  @Published var books = [Book]()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add a search UI to &lt;code&gt;SearchableBooksListView&lt;/code&gt;, we apply the &lt;code&gt;.searchable&lt;/code&gt; view modifier, and bind its &lt;code&gt;text&lt;/code&gt; parameter to a new &lt;code&gt;searchTerm&lt;/code&gt; property on the view model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SearchableBooksViewModel: ObservableObject {
  @Published var books = [Book]()
  @Published var searchTerm: String = &quot;&quot;
}

struct SearchableBooksListView: View {
  @StateObject var viewModel = SearchableBooksViewModel()
  var body: some View {
    List(viewModel.books) { book in
      SearchableBookRowView(book: book)
    }
    .searchable(text: $viewModel.searchTerm)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install the search UI in the &lt;code&gt;List&lt;/code&gt; view, but if you run this code, nothing will happen. In fact, you won’t even see any books in the list view.&lt;/p&gt;
&lt;p&gt;To change this, we will add a new private property to the view model which holds the original unfiltered list of books. And finally, we will set up a Combine pipeline that filters this list based on the search term entered by the user:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SearchableBooksViewModel: ObservableObject {
  @Published private var originalBooks = Book.samples
  @Published var books = [Book]()
  @Published var searchTerm: String = &quot;&quot;

  init() {
    Publishers.CombineLatest($originalBooks, $searchTerm) //(1)
      .map { books, searchTerm in //(2)
        books.filter { book in //(3)
          searchTerm.isEmpty ? true : (book.title.matches(searchTerm) || book.author.matches(searchTerm))
        }
      }
      .assign(to: &amp;amp;$books)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How does this Combine pipeline work?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We use &lt;code&gt;Publishers.CombineLatest&lt;/code&gt; to take the latest state of the two publishers, &lt;code&gt;$originalBooks&lt;/code&gt; and &lt;code&gt;$searchTerm&lt;/code&gt;. In a real-world application, we might receive updates to the collection of books in the background, and we’ll want these to be included in the search result as well. The &lt;code&gt;CombineLatest&lt;/code&gt; publisher will publish a new tuple containing the latest value of &lt;code&gt;originalBooks&lt;/code&gt; and &lt;code&gt;searchTerm&lt;/code&gt; every time one of those publishers send a new event.&lt;/li&gt;
&lt;li&gt;We then use the &lt;code&gt;.map&lt;/code&gt; operator to transform the &lt;code&gt;(books, searchTerm)&lt;/code&gt; tuple into an array of books that we eventually assign to the published &lt;code&gt;$books&lt;/code&gt; property, which is connected to the &lt;code&gt;SearchableBooksListView&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Inside the &lt;code&gt;.map&lt;/code&gt; closure, we use &lt;code&gt;filter&lt;/code&gt; to return only the books that contain the search term either in their title or in the author&apos;s name. This part of the process actually is not Combine-specific - &lt;code&gt;filter&lt;/code&gt; is a method on &lt;code&gt;Array&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you run this code, you will notice that everything you type into the search field will be auto-capitalised. To prevent this, we can apply the &lt;code&gt;. autocapitalization&lt;/code&gt; view modifier - &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;searchable&lt;/code&gt; view modifier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct SearchableBooksListView: View {
  @StateObject var viewModel = SearchableBooksViewModel()
  var body: some View {
    List(viewModel.books) { book in
      SearchableBookRowView(book: book)
    }
    .searchable(text: $viewModel.searchTerm)
    .autocapitalization(.none)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;List views in SwiftUI are very powerful, and with the features announced at WWDC 21, Apple is closing many of the gaps that existed between UIKit and SwiftUI’s &lt;code&gt;List&lt;/code&gt; API.&lt;/p&gt;
&lt;p&gt;Some of the features are a bit hidden, but thankfully, the official documentation is catching up, and it always pays off to read the source documentation in the SwiftUI header file. It’d be even better if we had access to SwiftUI’s source code, but I wouldn’t hold my breath for this one.&lt;/p&gt;
&lt;p&gt;In the meantime, I hope this series of articles helps to shed some light into what’s probably one of the most used user interface paradigms on Apple’s platforms. Feel free to connect with me on &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;Twitter&lt;/a&gt;, and if you have any questions or remarks, don&apos;t hesitate to send a tweet or DM.&lt;/p&gt;
&lt;p&gt;In the next episode, we will look into styling &lt;code&gt;List&lt;/code&gt; views - Apple has added some exciting styling capabilities in the latest release, making it easier to adopt &lt;code&gt;List&lt;/code&gt;s to your app&apos;s branding.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and until next time!&lt;/p&gt;
</content:encoded></item><item><title>Building Static Lists in SwiftUI</title><link>https://peterfriese.dev/blog/2021/swiftui-listview-part1/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-listview-part1/</guid><description>List views are a staple of iOS UIs. Learn how SwiftUI makes implementing them super easy.</description><pubDate>Mon, 30 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;List views are probably one of the most important UI structures in iOS apps, and you’ll be hard-pressed to find an app that doesn’t use some sort of list.&lt;/p&gt;
&lt;p&gt;SwiftUI makes it particularly easy to build list views: it just takes three lines to create a simple list! At the same time, SwiftUI’s &lt;code&gt;List&lt;/code&gt; view is extremely powerful and versatile, so it pays off to get to know it in a little bit more detail. In this series, I am going to teach you everything you need to know about &lt;code&gt;List&lt;/code&gt; views, from simple lists, styling lists and their items, displaying collections of data in list views, implementing actions on lists and individual list items, to building nested outlines and implementing three-column drill-down navigation UIs that work across iOS, iPadOS, watchOS, and macOS.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Probably the simplest way to build a list is to create a new SwiftUI view and wrap the &lt;em&gt;Hello World&lt;/em&gt; text in a &lt;code&gt;List:&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct StaticListView: View {
  var body: some View {
    List {
      Text(&quot;Hello, world!&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will show a static text inside a list view:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To add more items to the list, we can just add another line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List {
  Text(&quot;Hello, world!&quot;)
  Text(&quot;Hello, SwiftUI!&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using other SwiftUI views inside list rows&lt;/h2&gt;
&lt;p&gt;The cool thing about the &lt;code&gt;List&lt;/code&gt; view is that you can use any type of SwiftUI view as a list row, not just &lt;code&gt;Text&lt;/code&gt;. Labels, Sliders, Steppers, Toggles, TextFields, SecureFields for entering passwords, ProgressViews, and Pickers - you name it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct StaticListView2: View {
  @State var number: Int = 42
  @State var degrees: Double = 37.5
  @State var toggle = true
  @State var name = &quot;Peter&quot;
  @State var secret = &quot;s3cr3t!&quot;

  var fruits = [&quot;Apples&quot;, &quot;Bananas&quot;, &quot;Mangoes&quot;]
  @State var fruit = &quot;Mangoes&quot;

  var body: some View {
    List {
      Text(&quot;Hello, world!&quot;)
      Label(&quot;The answer&quot;, systemImage: &quot;42.circle&quot;)
      Slider(value: $degrees, in: 0...50) {
        Text(&quot;\(degrees)&quot;)
      } minimumValueLabel: {
        Text(&quot;min&quot;)
      } maximumValueLabel: {
        Text(&quot;max&quot;)
      }

      Stepper(value: $number, in: 0...100) {
        Text(&quot;\(number)&quot;)
      }
      Toggle(isOn: $toggle) {
        Text(&quot;Checked&quot;)
      }
      TextField(&quot;Name&quot;, text: $name)
      SecureField(&quot;Secret&quot;, text: $secret)
      ProgressView(value: 0.3)
      Picker(selection: $fruit, label: Text(&quot;Pick your favourite fruit&quot;)) {
        ForEach(fruits, id: \.self) { fruit in
          Text(fruit)
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Building Custom List Rows&lt;/h2&gt;
&lt;p&gt;And - thanks to SwiftUI&apos;s stack-based layout system, you can easily create custom rows as well. In this example, we’re using &lt;code&gt;VStack&lt;/code&gt; to layout two &lt;code&gt;Text&lt;/code&gt; views in a vertical stack, replicating the typical title and details layout that is widely used in many iOS apps.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct StaticListWithSimpleCustomRowView: View {
  var body: some View {
    List {
      VStack(alignment: .leading) {
        Text(&quot;Apples&quot;)
          .font(.headline)
        Text(&quot;Eat one a day&quot;)
          .font(.subheadline)
      }
      VStack(alignment: .leading) {
        Text(&quot;Bananas&quot;)
          .font(.headline)
        Text(&quot;High in potassium&quot;)
          .font(.subheadline)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding custom rows like this is quick and easy, but the code will grow rapidly as we add more rows, and this will make it harder to understand and update it when we need to make changes. To prevent this from happening, we can extract the code for the list rows into a separate view, making it reusable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct StaticListWithSimpleCustomRowView: View {
  var body: some View {
    List {
      CustomRowView(title: &quot;Apples&quot;, subtitle: &quot;Eat one a day&quot;)
      CustomRowView(title: &quot;Bananas&quot;, subtitle: &quot;High in potassium&quot;)
    }
  }
}

private struct CustomRowView: View {
  var title: String
  var subtitle: String

  var body: some View {
    VStack(alignment: .leading) {
      Text(title)
        .font(.headline)
      Text(subtitle)
        .font(.subheadline)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To learn more about refactoring SwiftUI code, check out this video in which I show the process of refactoring SwiftUI views in more detail:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;youtube https://www.youtube.com/watch?v=UhDdtdeW63k&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;More Complex List Rows&lt;/h2&gt;
&lt;p&gt;SwiftUI’s layout system is both flexible and easy to use, and makes it easy to create even complex layouts using a combination of &lt;code&gt;HStack&lt;/code&gt;, &lt;code&gt;VStack&lt;/code&gt;, &lt;code&gt;ZStack&lt;/code&gt;, and other SwiftUI views. Here is how you can create list rows with a title, a subtitle, a leading image, and a trailing number:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct StaticListWithCustomRowView: View {
  var body: some View {
    List {
      CustomRowView(&quot;Apple&quot;, description: &quot;Eat one a day&quot;, titleIcon: &quot;🍏&quot;, count: 2)
      CustomRowView(&quot;Banana&quot;, description: &quot;High in potassium&quot;, titleIcon: &quot;🍌&quot;, count: 3)
      CustomRowView(&quot;Mango&quot;, description: &quot;Soft and sweet&quot;, titleIcon: &quot;🥭&quot;)
    }
  }
}

private struct CustomRowView: View {
  var title: String
  var description: String?
  var titleIcon: String
  var count: Int

  init(_ title: String, description: String? = nil, titleIcon: String, count: Int = 1) {
    self.title = title
    self.description = description
    self.titleIcon = titleIcon
    self.count = count
  }

  var body: some View {
    HStack {
      Text(titleIcon)
        .font(.title)
        .padding(4)
        .background(Color(UIColor.tertiarySystemFill))
        .cornerRadius(10)
      VStack(alignment: .leading) {
        Text(title)
          .font(.headline)
        if let description = description {
          Text(description)
            .font(.subheadline)
        }
      }
      Spacer()
      Text(&quot;\(count)&quot;)
        .font(.title)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how we made use of a custom initialiser for &lt;code&gt;CustomRowView&lt;/code&gt;, allowing us to get rid of the parameter name for the &lt;code&gt;title&lt;/code&gt; property, and to define defaults for some of the properties. As a result, it is now more convenient to use the custom row view.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Lists are a very popular UI element in iOS apps, and thanks to SwiftUI’s declarative syntax, it&apos;s easier than ever before to quickly build rich list UIs.&lt;/p&gt;
&lt;p&gt;In the next part of this series, we will look into displaying data from a collection in a list.&lt;/p&gt;
&lt;p&gt;Feel free to connect with me on &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;Twitter&lt;/a&gt;, and if you have any questions or remarks, don&apos;t hesitate to send a tweet or DM.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and stay tuned for the next episode! 🔥&lt;/p&gt;
</content:encoded></item><item><title>Cooperative Task Cancellation</title><link>https://peterfriese.dev/blog/2021/swiftui-concurrency-essentials-part2/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-concurrency-essentials-part2/</guid><description>Using async/await in SwiftUI apps</description><pubDate>Sun, 11 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;import demo from &apos;./swiftui-concurrency-essentials-part2/demo.mp4&apos;;&lt;/p&gt;
&lt;p&gt;Swift’s new concurrency features makes it much easier to write correct concurrent code. This is achieved in a number of ways, most prominently by adding features like async/await to the Swift language itself, allowing the compiler to perform flow analysis and providing meaningful feedback to the developer.&lt;/p&gt;
&lt;p&gt;As Doug points out in &lt;a href=&quot;https://forums.swift.org/t/concurrency-in-swift-5-and-6/49337&quot;&gt;this post&lt;/a&gt; on the Swift forums, “&lt;em&gt;Swift has always been designed to be safe-by-default&lt;/em&gt;”, and “&lt;em&gt;an explicit goal of the concurrency effort for Swift 6 is to make safe-by-default also extend to data races&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
Swift has always been designed to be safe-by-default.&lt;/p&gt;
&lt;p&gt;In this series of articles and videos, my goal is to walk you through the key aspects of Swift’s new concurrency model that you need in the context of building SwiftUI apps specifically. &lt;a href=&quot;https://peterfriese.dev/swiftui-concurrency-essentials-part1/&quot;&gt;Last time&lt;/a&gt;, I showed you how to fetch data from an asynchronous API using &lt;code&gt;URLSession&lt;/code&gt; , how async/await helps us to get rid of the pyramid of doom, and &lt;a href=&quot;https://peterfriese.dev/swiftui-concurrency-essentials-part1/#calling-asynchronous-code-from-swiftui-&quot;&gt;how to call asynchronous code&lt;/a&gt; from a synchronous context.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;Today, I want to focus on task cancellation. This is an important feature in Swift’s concurrency model that allows us to avoid unexpected behaviour in our apps.&lt;/p&gt;
&lt;h2&gt;Out-of-order search results&lt;/h2&gt;
&lt;p&gt;To understand why this is important, let’s take a look at the sample app I built for this post (available in the &lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Concurrency-Essentials&quot;&gt;GitHub repository&lt;/a&gt; for the series). It’s a simple search screen that makes use of SwiftUI’s new &lt;code&gt;searchable&lt;/code&gt; view modifier. The search field is bound to the &lt;code&gt;searchTerm&lt;/code&gt; published property of view model, and we use the &lt;code&gt;onReceive&lt;/code&gt; view modifier to listen to any changes to this property. Inside the closure, we create a new asynchronous task and call the &lt;code&gt;executeQuery&lt;/code&gt; method on the view model.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BookSearchView: View {
  @StateObject
  fileprivate var viewModel = ViewModel()
  
  var body: some View {
    List(viewModel.result) { book in
      BookSearchRowView(book: book)
    }
    .overlay {
      if viewModel.isSearching {
        ProgressView()
      }
    }
    .searchable(text: $viewModel.searchTerm)
    .onReceive(viewModel.$searchTerm) { searchTerm in
      Task {
        await viewModel.executeQuery()
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means we run the query every time the user types a character, which essentially gives us a live search experience.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@MainActor
fileprivate class ViewModel: ObservableObject {
  @Published var searchTerm: String = &quot;&quot;
  
  @Published private(set) var result: [Book] = []
  @Published private(set) var isSearching = false
  
  func executeQuery() async {
    let currentSearchTerm = searchTerm.trimmingCharacters(in: .whitespaces)
    if currentSearchTerm.isEmpty {
      result = []
      isSearching = false
    }
    else {
      Task {
        isSearching = true
        result = await searchBooks(matching: searchTerm)
        isSearching = false
      }
    }
  }
  
  private func searchBooks(matching searchTerm: String) async -&amp;gt; [Book] { ... }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you pay close attention to the recording of the app, you will notice something strange: the user types a search term (“&lt;em&gt;Hitchhiker&lt;/em&gt;”), and after a short moment, the result for this search term appears. But soon after, the search result is replaced with a different search result, which is a bit unexpected. If you take a closer look, you will notice that this second result is for the search term “&lt;em&gt;Hitchhike&lt;/em&gt;”.&lt;/p&gt;
&lt;p&gt;Why is that?&lt;/p&gt;
&lt;p&gt;Well, it turns out that the &lt;a href=&quot;https://openlibrary.org/developers&quot;&gt;OpenLibrary API&lt;/a&gt; has a rather slow response time (which is why it’s such a great showcase for our example). In addition, shorter search terms seem to take longer time to fetch, and this is why the results for earlier (shorter) search terms arrive &lt;em&gt;after&lt;/em&gt; the results for longer search terms.&lt;/p&gt;
&lt;p&gt;So when the results for the search term &quot;&lt;em&gt;Hitchhiker&lt;/em&gt;&quot; arrive, the requests for all the other previous search terms are still outstanding:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;When they eventually finish, the ones that took a longer time to complete will overwrite the quicker ones. This results in the unexpected UX, and we definitely need to fix this. The OpenLibrary API might be an extreme example due to its slow response time, but you will observe similar behaviours in many other APIs as well.&lt;/p&gt;
&lt;h2&gt;Can we fix this?&lt;/h2&gt;
&lt;p&gt;Now, if you’ve used Combine before, you might recall the &lt;code&gt;debounce&lt;/code&gt; operator - this operator will publish events only if a specified time has elapsed between two events. Since published properties are Combine publishers, it is actually pretty simple to make use of the &lt;code&gt;debounce&lt;/code&gt; operator, so let&apos;s see if this solves the problem:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.onReceive(viewModel.$searchTerm.debounce(for: 0.8, scheduler: RunLoop.main)) { searchTerm in
  Task {
    await viewModel.executeQuery()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By making this change, the &lt;code&gt;debounce&lt;/code&gt; operator will only send the latest value of the &lt;code&gt;searchTerm&lt;/code&gt; property to the receiver (in this case, the closure) when the user stops typing for 0.8 seconds.&lt;/p&gt;
&lt;p&gt;And indeed, this does solve the problem when the user types their search term without making a pause. But we will run into the same problem again if they start typing, then pause to think for a short moment, and then continue typing before the result arrives for what they’d entered before the pause .&lt;/p&gt;
&lt;p&gt;So - even though this is better (mostly because we reduce the number of requests we’re sending to the API, which also helps prevent thrashing), it’s not perfect.&lt;/p&gt;
&lt;h2&gt;Scratch this&lt;/h2&gt;
&lt;p&gt;To really improve the UX of our application, we need to make sure that any outstanding requests are cancelled before sending a new one.&lt;/p&gt;
&lt;p&gt;Swift’s new concurrency features allow us to implement this with just a few additional lines of code. In &lt;code&gt;executeQuery&lt;/code&gt;, we create a new &lt;code&gt;Task&lt;/code&gt; to launch a new asynchronous task from a synchronous context. &lt;code&gt;Task&lt;/code&gt; has a number of methods to interact with an active task - for example, we can use a task handle to cancel a task (&lt;a href=&quot;https://developer.apple.com/documentation/swift/task/3851218-cancel&quot;&gt;see the docs&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Here is the updated &lt;code&gt;executeQuery&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private var searchTask: Task&amp;lt;Void, Never&amp;gt;?
  
@MainActor
func executeQuery() async {
  searchTask?.cancel()
  let currentSearchTerm = searchTerm.trimmingCharacters(in: .whitespaces)
  if currentSearchTerm.isEmpty {
    result = []
    isSearching = false
  }
  else {
    searchTask = Task {
      isSearching = true
      result = await searchBooks(matching: searchTerm)
      if !Task.isCancelled {
        isSearching = false
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;To make this work, we create a private property to hold a reference to a &lt;code&gt;Task&amp;lt;Void, Never&amp;gt;?&lt;/code&gt; - this means our task doesn&apos;t return a value, and it will never fail (i.e. it doesn&apos;t throw).&lt;/li&gt;
&lt;li&gt;Inside the &lt;code&gt;executeQuery&lt;/code&gt; function, we first cancel any previously launched task.&lt;/li&gt;
&lt;li&gt;Next, we store the task we create for the new search request in the &lt;code&gt;searchTask&lt;/code&gt; property (so we can cancel it later, if needed)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that’s basically it! This works because &lt;code&gt;URLSession&lt;/code&gt;’s async methods support Swift’s new concurrency model. To learn more about using async/await with URLSession, check out Apple’s official &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10095/?time=284&quot;&gt;WWDC video&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that we guard updating the &lt;code&gt;isSearching&lt;/code&gt; property by checking if the current task has been cancelled. This property is used to drive the progress spinner on the UI, and we want to make sure the spinner is visible as long as any search request is outstanding. This is why we may only set this property to &lt;code&gt;false&lt;/code&gt; if a search request has successfully completed, or the search term is empty.&lt;/p&gt;
&lt;p&gt;If you are using APIs that participate in cooperative task cancellation, you’re all set now. However, if you are the author of an API yourself, there are some extra steps you need to take to make sure your code takes part in cooperative task cancellation.&lt;/p&gt;
&lt;h2&gt;Cooperative Task Cancellation&lt;/h2&gt;
&lt;p&gt;The documentation for &lt;a href=&quot;https://developer.apple.com/documentation/swift/task/3851218-cancel&quot;&gt;&lt;code&gt;cancel()&lt;/code&gt;&lt;/a&gt; contains a very important note:&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
Whether this function has any effect is task-dependent.
For a task to respect cancellation it must cooperatively check for it while running. Many tasks will check for cancellation before beginning their “actual work”, however this is not a requirement nor is it guaranteed how and when tasks check for cancellation in general.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;This means you are responsible for stopping any work as soon as possible when a caller requests to cancel the task your code is running on.&lt;/p&gt;
&lt;p&gt;Consider the following code snippet. It is an iterative implementation of a function that computes the nth Fibonacci number. I’ve artificially slowed down the algorithm by adding &lt;code&gt;Task.sleep()&lt;/code&gt; to the inner loop.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private func fiboncacci(nth: Int, progress: ((Int) -&amp;gt; Void)? = nil) async throws -&amp;gt; Int {
  var last = 0
  var current = 1
    
  for i in 0..&amp;lt;nth {
    // this is the real computation
    (current, last) = (current + last, current)
      
    // simulate compute-intensive behaviour (pause for 0.75 sec)
    await Task.sleep(75_000_000)
      
    // report progress
    progress?(i)
      
    // cooperative cancellation
    try Task.checkCancellation()
  }
    
  return last
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By calling &lt;code&gt;Task.checkCancellation()&lt;/code&gt;, the function checks if the caller has requested to cancel the task. &lt;code&gt;Task.checkCancellation()&lt;/code&gt; will check is &lt;code&gt;Task.isCancelled&lt;/code&gt; is true, and will throw &lt;code&gt;Task.CancellationError&lt;/code&gt; if that’s the case. This way, the &lt;code&gt;fibonacci&lt;/code&gt; function can stop any ongoing work after each iteration of its inner loop.&lt;/p&gt;
&lt;p&gt;If you’d rather not throw &lt;code&gt;Task.CancellationError&lt;/code&gt;, you can use &lt;code&gt;Task.isCancelled&lt;/code&gt; to check if the current task has been cancelled and stop any ongoing work.&lt;/p&gt;
&lt;p&gt;Throwing an error is just one way to respond to cancellation. Depending on the kind of work your code performs, you should choose which of the following options works best:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Throwing an error (as demonstrated above)&lt;/li&gt;
&lt;li&gt;Returning &lt;code&gt;nil&lt;/code&gt; or an empty collection&lt;/li&gt;
&lt;li&gt;Returning the partially completed work&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SwiftUI’s new task() view modifier&lt;/h2&gt;
&lt;p&gt;SwiftUI features a new view modifier that lets you run code in an asynchronous task as soon as the view appears. It will automatically cancel the task once the view disappears.&lt;/p&gt;
&lt;p&gt;Here is a snippet from the sample in the &lt;a href=&quot;https://peterfriese.dev/swiftui-concurrency-essentials-part1/&quot;&gt;previous article in this series&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct WordSearchView: View {
  @StateObject var viewModel = WordsAPIViewModel()
  var body: some View {
    List { ... }
    .task {
      viewModel.searchTerm = &quot;Swift&quot;
      await viewModel.executeQuery()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to call asynchronous code when your view appears, favour using &lt;code&gt;task { }&lt;/code&gt; over &lt;code&gt;onAppear&lt;/code&gt; / &lt;code&gt;onDisappear&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Yielding&lt;/h2&gt;
&lt;p&gt;One final piece of advice: if you’re writing computationally intensive code, you should call &lt;code&gt;Task.yield()&lt;/code&gt; every now and then to yield to the system and give it the opportunity to perform any other work, such as updating the UI. If you don&apos;t do this, your app might appear to be frozen to the user, even though it is actively running some computationally intensive code.&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;Swift’s new concurrency model makes it easy to write well-structured apps that handle concurrency in a predictable and structured way. Personally, I like the way how the concepts have been added to the language in a domain-specific way - this falls in line with many other areas of Swift that make use of DSL approaches as well (such as SwiftUI itself, for example).&lt;/p&gt;
&lt;p&gt;I hope this post helped you understand how to use task cancellation to make your UIs more predictable and increase their usability. If you’ve got any questions, &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;reach out to me on Twitter&lt;/a&gt;, or leave a &lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Concurrency-Essentials/discussions/categories/q-a&quot;&gt;comment on the repository&lt;/a&gt; for this post.&lt;/p&gt;
&lt;p&gt;Thanks for reading! 🔥&lt;/p&gt;
</content:encoded></item><item><title>Getting Started with async/await in SwiftUI</title><link>https://peterfriese.dev/blog/2021/swiftui-concurrency-essentials-part1/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-concurrency-essentials-part1/</guid><description>Using async/await in SwiftUI apps</description><pubDate>Thu, 24 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;Without a doubt, one of the biggest announcements at WWDC21 was &lt;a href=&quot;https://developer.apple.com/news/?id=2o3euotz&quot;&gt;Swift Concurrency&lt;/a&gt;, most notably the support for &lt;em&gt;async/await&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As developers, we’re constantly faced with the asynchronous nature of the apps we write. Reading a file from disk, fetching data from a remote API on the web, and even getting input from the user - all of these are asynchronous operations. Throughout the years, many different ways to deal with asynchronous code have been implemented - most iOS developers will be familiar with Grand Central Dispatch, completion handlers, or delegates.&lt;/p&gt;
&lt;p&gt;The new Swift Concurrency model builds on threads, but abstracts away from them.&lt;/p&gt;
&lt;p&gt;At WWDC 2021, Apple did a fantastic job at at explaining all the concepts behind async/await and Structured Concurrency. There are no less than 9 videos, plus an &lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html&quot;&gt;entire chapter about Concurrency&lt;/a&gt; in the Swift Language Guide, as well as a ton of sample code (in case you haven’t tried it, check out the copy code feature in the Developer app).&lt;/p&gt;
&lt;p&gt;But because there is so much information out there, it is sometimes hard to see the forest before the trees.&lt;/p&gt;
&lt;p&gt;This article aims at giving you everything you need to write SwiftUI apps that make use of the new concurrency features in Swift.&lt;/p&gt;
&lt;p&gt;So - let’s cut to the chase!&lt;/p&gt;
&lt;h2&gt;The sample app&lt;/h2&gt;
&lt;p&gt;When looking for an easy-to-use API to for our sample app, I came across &lt;a href=&quot;https://www.wordsapi.com/&quot;&gt;WordsAPI.com&lt;/a&gt;. It&apos;s a fun API that provides a ton of interesting information about words (in the English language). You send it a word, such as &quot;Swift&quot;, and it will return a bunch of information, such as the different meanings of the word (for example, &lt;em&gt;“moving very fast”&lt;/em&gt;, &lt;em&gt;“a small bird that resembles a swallow”&lt;/em&gt;, or &lt;em&gt;“an English satirist born in Ireland”&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;The sample app displays a bunch of words in a &lt;code&gt;List&lt;/code&gt; view. When the user taps on one of those words, the app will fetch the meanings of this word from WordAPI.com and display them in a details screen.&lt;/p&gt;
&lt;p&gt;Later on in the article, we will add another feature that fetches a random word from WordAPI.com and adds it to the main list when the user pulls to refresh.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Using URLSession and async/await&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;URLSession&lt;/code&gt; is among the many APIs that have been upgraded to support async/await, so fetching data is now a simple one-liner:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let (data, response) = try await URLSession.shared.data(for: urlRequest)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With some minimal level of error handling and JSON parsing (powered by Codable), the code for fetching the details about a word looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private func search(for searchTerm: String) async -&amp;gt; Word {
  // build the request
  let request = buildURLRequest(for: searchTerm)
    
  do {
    let (data, response) = try await URLSession.shared.data(for: request)
    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
      throw WordsAPI.invalidServerResponse
    }
    let word = try JSONDecoder().decode(Word.self, from: data)
    return word
  }
  catch {
    return Word.empty
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we marked our &lt;code&gt;search(for searchTerm: String)&lt;/code&gt; method as asynchronous by appending &lt;code&gt;async&lt;/code&gt; in the method signature. This means we’ll have to use &lt;code&gt;await&lt;/code&gt; whenever we call this method. Doing so indicates a so-called suspension point, and gives the runtime the opportunity to suspend the currently executing function. A suspended function is “put on hold” until the function it called returns. While the function is suspended, the thread it was executing on can be used to execute other code in your application.&lt;/p&gt;
&lt;h2&gt;Updating &lt;code&gt;@Published&lt;/code&gt; properties&lt;/h2&gt;
&lt;p&gt;The sample app uses a view model to act as a central point for fetching data from the WordsAPI endpoint. It is implemented as an &lt;code&gt;ObservableObject&lt;/code&gt;, so it can leverage SwiftUI&apos;s state management system:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Word {
  let word: String
  let definitions: [Definition]
}

class WordDetailsViewModel: ObservableObject {
  // output
  @Published var result = Word.empty //(1)
  @Published var isSearching = false //(2)
  
  @MainActor //(4)  
  func executeQuery(for searchTerm: String) async { //(3)
    isSearching = true
    result = await search(for: searchTerm)
    isSearching = false
  }
  
  private func buildURLRequest(for searchTerm: String) -&amp;gt; URLRequest { ... }
  
  private func search(for searchTerm: String) async -&amp;gt; Word { ... }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s discuss what’s going on here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;result&lt;/code&gt; is a published property that contains the result of the API call (once this call returns)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isSearching&lt;/code&gt; indicates whether we&apos;re currently performing a search. This property will be bound to a progress indicator to give the user some visual feedback that we&apos;re waiting for results.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;executeQuery&lt;/code&gt; manages the state of &lt;code&gt;isSearching&lt;/code&gt;, and then calls the actual code for performing the API call. Note that this function also needs to be marked as &lt;code&gt;async&lt;/code&gt;, as it calls &lt;code&gt;search(for:)&lt;/code&gt;, which is a function that might suspend (as &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10132?time=1208&quot;&gt;mentioned in&lt;/a&gt; &lt;em&gt;Meet async/await in Swift&lt;/em&gt;, functions that may suspend, suspend their callers too).&lt;/li&gt;
&lt;li&gt;and finally, &lt;code&gt;executeQuery&lt;/code&gt; is marked as &lt;code&gt;@MainActor&lt;/code&gt;, to indicate that code within must be run on the main actor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This last point is crucial: since published properties update the UI, any code that tries to update a published property must be run on the main thread. By marking a function with the &lt;code&gt;@MainActor&lt;/code&gt; attribute, the Swift compiler will guarantee that it will run on the main actor. So by annotating &lt;code&gt;executeQuery&lt;/code&gt; with the &lt;code&gt;@MainActor&lt;/code&gt; annotation, we make sure all updates happen on the main thread.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I strongly recommend using view models, as they help to keep your code clean. However, it is definitely possible to forgo view models and make the &lt;code&gt;search(for searchTerm: String)&lt;/code&gt; function a member of your SwiftUI view. To see how you would do that, check out &lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Concurrency-Essentials/blob/main/WordBrowser/WordBrowser/Views/WordDetailsViewNoViewModel.swift&quot;&gt;WordDetailsViewNoViewModel&lt;/a&gt; in the GitHub repository for this article.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h2&gt;Calling asynchronous code from SwiftUI …&lt;/h2&gt;
&lt;p&gt;The final piece of the puzzle is how to call our asychronous code from SwiftUI. There are many different places you might call from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the view appears&lt;/li&gt;
&lt;li&gt;When the user taps on a button&lt;/li&gt;
&lt;li&gt;When the user pulls to refresh&lt;/li&gt;
&lt;li&gt;In response to a search request&lt;/li&gt;
&lt;li&gt;Based on a notification&lt;/li&gt;
&lt;li&gt;Based on a timer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s look at a few of these to understand the different mechanisms we can use.&lt;/p&gt;
&lt;h3&gt;… when the view appears&lt;/h3&gt;
&lt;p&gt;This is probably the most common time to fetch data, and you might have been using the &lt;code&gt;onAppear&lt;/code&gt; view modifier in your existing SwiftUI apps to trigger fetching data. We can still use &lt;code&gt;onAppear&lt;/code&gt;, but the compiler will complain that we cannot call an asynchronous function here.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;One solution to this problem is to create a new &lt;code&gt;Task&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct WordDetailsView: View {
  ...  
  var body: some View {
    List {
      ...
    }
    .navigationTitle(word)
    .onAppear {
      Task {
        await viewModel.executeQuery(for: word)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works well, but there is an even better solution: because fetching data when a view appears is so common, SwiftUI has a new view modifier that will automatically create a new &lt;code&gt;Task&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; cancel the task when the view disappears:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct WordDetailsView: View {
  ...
  var body: some View {
    List {
      ...
    }
    .navigationTitle(word)
    .task {
      await viewModel.executeQuery(for: word)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;… when the user taps a button&lt;/h3&gt;
&lt;p&gt;Sometimes, we want to execute asynchronous code in response to a button tap. In Xcode 13b1, most of the button action handlers do not support calling asynchronous code, so we need to create a new asynchronous context by calling &lt;code&gt;async&lt;/code&gt; ourselves:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.toolbar {
  ToolbarItem(placement: .primaryAction) {
    Button(&quot;Refresh&quot;) {
      Task {
        await viewModel.refresh()
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;… when the user pulls to refresh&lt;/h3&gt;
&lt;p&gt;Pull-to-refresh is another welcome addition to this year’s release of SwiftUI. By simply adding the &lt;code&gt;refreshable&lt;/code&gt; view modifier, a view receives pull-to-refresh capabilities. Inside the closure, we can update the data that&apos;s displayed in the view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct LibraryView: View {
  ...  
  var body: some View {
    List {
      ...
    }
    .searchable(text: $viewModel.searchText)
    .refreshable {
      await viewModel.refresh()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;… in response to a search request&lt;/h3&gt;
&lt;p&gt;This year’s release of SwiftUI also brought us the &lt;code&gt;searchable()&lt;/code&gt; view modifier - by applying this view modifier to a list view, you get a platform-specific search UI. The first parameter of the view modifer is a binding to a &lt;code&gt;String&lt;/code&gt;,  which you can then use to drive the search. If you don&apos;t want to use Combine to listen to changes on the search string, you can use the &lt;code&gt;onSubmit(of:)&lt;/code&gt; view modifier to react to certain key presses. For example, to trigger the search when the user taps the &lt;em&gt;Search&lt;/em&gt; button on their on-screen keyboard (or hits the enter key), you can use this code snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct WordSearchView: View {
  @StateObject var viewModel = WordsAPIViewModel()
  var body: some View {
    List {
      ...
    }
    .searchable(text: $viewModel.searchTerm)
    .onSubmit(of: .search) {
      Task {
        await viewModel.executeQuery()
      }
    }
    .navigationTitle(&quot;Search&quot;)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you might have noticed, &lt;code&gt;onSubmit&lt;/code&gt;‘s closure isn&apos;t marked as asynchronous, which is why we need to create a new &lt;code&gt;Task&lt;/code&gt; using the now familiar &lt;code&gt;Task { }&lt;/code&gt; syntax.&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;To summarise:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use asynchronous versions of Apple’s APIs (if that’s not possible, there is a workaround, which we will learn about in one of the following posts)&lt;/li&gt;
&lt;li&gt;Mark functions that call asynchronous code as &lt;code&gt;async&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mark functions that update published properties with the &lt;code&gt;@MainActor&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;To fetch data when a view appears, use the &lt;code&gt;task&lt;/code&gt; view modifier&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;refreshable&lt;/code&gt; view modifier to use pull-to-refresh to fetch data asynchronously&lt;/li&gt;
&lt;li&gt;Wrap asynchronous code in a new &lt;code&gt;Task&lt;/code&gt; when calling from a synchronous context, such as a &lt;code&gt;Button&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I hope you found this useful. If you’ve got any questions or suggestions, &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;reach out to me on Twitter&lt;/a&gt;, or file a pull request on the &lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Concurrency-Essentials&quot;&gt;repository&lt;/a&gt; for this article.&lt;/p&gt;
&lt;p&gt;Thanks for reading! 🔥&lt;/p&gt;
</content:encoded></item><item><title>SwiftUI List Bindings</title><link>https://peterfriese.dev/blog/2021/swiftui-list-item-bindings-behind-the-scenes/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/swiftui-list-item-bindings-behind-the-scenes/</guid><description>How to write asynchronous code without deeply nested callbacks</description><pubDate>Thu, 10 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;Lists are probably one of the most popular UI elements in iOS apps, and we’ve come a long way since &lt;code&gt;UITableViewController&lt;/code&gt; was first introduced. Creating lists in UIKit wasn&apos;t exactly rocket science, but it did require some ceremony.&lt;/p&gt;
&lt;p&gt;SwiftUI has made creating lists really easy - check out this snippet that displays a list of todos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct StaticTodoList: View {
  var todos: [TodoItem]

  var body: some View {
    List(todos) { item in
      Text(item.title)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With just three lines of code, we’re able to create a simple list view.&lt;/p&gt;
&lt;p&gt;Things get slightly more complicated once we try to make the items in the list rows editable. To modify data in SwiftUI, we need to use a binding. Bindings allow us to create a &lt;em&gt;two-way connection between a property that stores data, and a view that displays and changes the data&lt;/em&gt; (&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/binding&quot;&gt;see the Apple docs&lt;/a&gt; for &lt;code&gt;Binding&lt;/code&gt;). For example, here is how a &lt;code&gt;TextField&lt;/code&gt; binds to the model attribute we want to edit:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TextField(&quot;Todo&quot;, text: $todo.title)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, until now, SwiftUI didn’t provide a simple way to provide access to collection items using bindings.&lt;/p&gt;
&lt;p&gt;And this is why we ended up building code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct TodoList: View {
  @Binding var todos: [TodoItem]

  var body: some View {
    List(0..&amp;lt;todos.count) { index in
      TextField(&quot;Todo&quot;, text: $todos[index].title)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not only does this code look a lot more complicated than it needs to - it also forces SwiftUI to re-render the entire list even if we change just one element in the list, and this might result in slow UI updates and flickering.&lt;/p&gt;
&lt;p&gt;To learn more about why iterating over the items in this way is wrong, check out the &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10022/&quot;&gt;Demystify SwiftUI&lt;/a&gt; talk (and check out &lt;a href=&quot;https://twitter.com/zntfdr&quot;&gt;Federico Zanetello’s&lt;/a&gt; &lt;a href=&quot;https://www.wwdcnotes.com/notes/wwdc21/10022/&quot;&gt;WWDC Notes for it&lt;/a&gt;), where the SwiftUI team talks in detail about view identity, which is a key aspect for correct and performant SwiftUI code&lt;/p&gt;
&lt;p&gt;As of WWDC 2021, SwiftUI supports bindings for list elements. To use this feature, all we need to do is pass a binding to the collection into the list, and SwiftUI will hand us a binding to the current element into the closure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BetterTodoList: View {
  @Binding var todos: [TodoItem]

  var body: some View {
    List($todos) { $todo in
      TextField(&quot;Todo&quot;, text: $todo.title)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code is easier to read, and in fact looks similar to the original code we started with. And the best part: you can back-deploy this code to any release of iOS that supports SwiftUI.&lt;/p&gt;
&lt;h2&gt;Behind the scenes&lt;/h2&gt;
&lt;p&gt;I am sure you’ll appreciate the increased simplicity this brings to your code. Simpler code means fewer errors, and this also much easier to read and write.&lt;/p&gt;
&lt;p&gt;But how does it work?&lt;/p&gt;
&lt;p&gt;Well, there are a few things that come together to make this possible. Let’s first look at what makes it possible to use bindings in &lt;code&gt;List&lt;/code&gt; (and &lt;code&gt;ForEach&lt;/code&gt;, by the way).&lt;/p&gt;
&lt;p&gt;Let&apos;s jump to the definition of the &lt;code&gt;List&lt;/code&gt; initialiser (by using either &lt;code&gt;⌃ + ⌘ + J&lt;/code&gt; or &lt;code&gt;⌃ + ⌘ + Click&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension List where SelectionValue == Never {

    /// Creates a list that computes its rows on demand from an underlying
    /// collection of identifiable data.
    ///
    /// - Parameters:
    ///   - data: A collection of identifiable data for computing the list.
    ///   - rowContent: A view builder that creates the view for a single row of
    ///     the list.
    public init&amp;lt;Data, RowContent&amp;gt;(_ data: Binding&amp;lt;Data&amp;gt;, @ViewBuilder rowContent: @escaping (Binding&amp;lt;Data.Element&amp;gt;) -&amp;gt; RowContent) where Content == ForEach&amp;lt;LazyMapSequence&amp;lt;Data.Indices, (Data.Index, Data.Element.ID)&amp;gt;, Data.Element.ID, HStack&amp;lt;RowContent&amp;gt;&amp;gt;, Data : MutableCollection, Data : RandomAccessCollection, RowContent : View, Data.Element : Identifiable, Data.Index : Hashable

    /// Creates a list that identifies its rows based on a key path to the
    /// identifier of the underlying data.
    ///
    /// - Parameters:
    ///   - data: The data for populating the list.
    ///   - id: The key path to the data model&apos;s identifier.
    ///   - rowContent: A view builder that creates the view for a single row of
    ///     the list.
    public init&amp;lt;Data, ID, RowContent&amp;gt;(_ data: Binding&amp;lt;Data&amp;gt;, id: KeyPath&amp;lt;Data.Element, ID&amp;gt;, @ViewBuilder rowContent: @escaping (Binding&amp;lt;Data.Element&amp;gt;) -&amp;gt; RowContent) where Content == ForEach&amp;lt;LazyMapSequence&amp;lt;Data.Indices, (Data.Index, ID)&amp;gt;, ID, HStack&amp;lt;RowContent&amp;gt;&amp;gt;, Data : MutableCollection, Data : RandomAccessCollection, ID : Hashable, RowContent : View, Data.Index : Hashable
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A quick look at the &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/list?changes=latest_minor&quot;&gt;documentation&lt;/a&gt; (with API diffs turned on) shows that this is an addition from 12.5 to 13.0b1.&lt;/p&gt;
&lt;p&gt;Let’s unwrap what this new initialiser signature tells us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first parameter (&lt;code&gt;data: Binding&amp;lt;Data&amp;gt;&lt;/code&gt;) defines that the initialiser expects a binding that is generic over &lt;code&gt;Data&lt;/code&gt; (e.g. an array, like in our case).&lt;/li&gt;
&lt;li&gt;The second parameter (&lt;code&gt;@ViewBuilder rowContent: @escaping (Binding&amp;lt;Data.Element&amp;gt;) -&amp;gt; RowContent&lt;/code&gt;) , defines the trailing closure in which we define the body of our list. This signature shows us that the closure will receive a binding that is generic over the type of the elements of the data collection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is what allows us to pass in a collection of bindings and receive them, one by one, in the body of the &lt;code&gt;List&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But it’s only part of the equation! Why can we use the &lt;code&gt;$&lt;/code&gt; sign syntax?&lt;/p&gt;
&lt;p&gt;To understand this, we need to do some more API archeology (or get a &lt;a href=&quot;https://twitter.com/hollyborla/status/1402453516124229637&quot;&gt;hint from someone who worked&lt;/a&gt; on this - thanks, &lt;a href=&quot;https://twitter.com/hollyborla&quot;&gt;Holly&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0293-extend-property-wrappers-to-function-and-closure-parameters.md&quot;&gt;SE-0293&lt;/a&gt; (Extend Property Wrappers to Function and Closure Parameters) discusses extending property wrappers to function and closure parameters. This is why we are able to use &lt;code&gt;Binding&lt;/code&gt; on the trailing closure in the first place. What makes the &lt;code&gt;$&lt;/code&gt; sign possible is discussed in the &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0293-extend-property-wrappers-to-function-and-closure-parameters.md#closures&quot;&gt;Closures section&lt;/a&gt; of the proposal:&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
For closures that take in a projected value, the property-wrapper attribute is not necessary if
the backing property wrapper and the projected value have the same type, such as the
&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/binding&quot;&gt;&lt;code&gt;@Binding&lt;/code&gt;&lt;/a&gt; property wrapper from
SwiftUI. If &lt;code&gt;Binding&lt;/code&gt; implemented &lt;code&gt;init(projectedValue:)&lt;/code&gt;, it could be used as a property-wrapper
attribute on closure parameters without explicitly writing the attribute:
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let useBinding: (Binding&amp;lt;Int&amp;gt;) -&amp;gt; Void = { $value in
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And - sure enough - if we look at the API diff for &lt;code&gt;Binding&lt;/code&gt;, there it is: &lt;code&gt;init(projectedValue: Binding&amp;lt;Value&amp;gt;)&lt;/code&gt; is an &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/binding/?changes=latest_minor&quot;&gt;addition from Xcode 12.5 to Xcode 13.0b1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So - there you have it: we can use bindings inside of &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;ForEach&lt;/code&gt; thanks to the work done in SE-0293, and the addition of the respective initialisers to &lt;code&gt;List&lt;/code&gt; (see the documentation &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/list/init(_:rowcontent:)-25c5f?changes=latest_minor&quot;&gt;here&lt;/a&gt;), &lt;code&gt;ForEach&lt;/code&gt; (see the documentation &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/foreach/init(_:content:)-96gx9?changes=latest_minor&quot;&gt;here&lt;/a&gt;) and &lt;code&gt;Binding&lt;/code&gt; (see the documentation &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/binding/init(projectedvalue:)?changes=latest_minor&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;As Holly mentioned &lt;a href=&quot;https://twitter.com/hollyborla/status/1402386082633633794&quot;&gt;here&lt;/a&gt;, the new &lt;code&gt;$&lt;/code&gt; syntax for closure parameters is a compile-time aspect of the language, and works as long as you&apos;re compiling with Swift 5.5.&lt;/p&gt;
&lt;h2&gt;Back-deploying&lt;/h2&gt;
&lt;p&gt;Which brings us to the question: why can this be &lt;a href=&quot;https://twitter.com/luka_bernardi/status/1402363202923491332&quot;&gt;back-deployed&lt;/a&gt; to earlier iOS versions? The new &lt;code&gt;$&lt;/code&gt; syntax for closure parameters is just one piece of the puzzle, but we also have new APIs (the additional initialisers) that need to be made available on previous versions of the OS.&lt;/p&gt;
&lt;p&gt;How can we make new APIs available to earlier versions of the runtime?&lt;/p&gt;
&lt;p&gt;There is an entire document on the Swift repo that discusses the topic of &lt;a href=&quot;https://github.com/apple/swift/blob/main/docs/LibraryEvolution.rst#always-emit-into-client&quot;&gt;Library Evolution&lt;/a&gt;, the &lt;em&gt;‘ability to change a library without breaking source or binary compatibility’.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The document is a bit vague about &lt;code&gt;@_alwaysEmitIntoClient&lt;/code&gt;, but it seems that annotating a function, computed property, or subscript with this attribute will result in the code being emitted into the client binary (i.e. our app), thereby making it possible to use new APIs on previous, ABI-stable versions of the Swift runtime, and it seems like this is what the team did for this specific feature.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Please note&lt;/strong&gt; that - as the underscore indicates - &lt;code&gt;@_alwaysEmitIntoClient&lt;/code&gt; is a private
compiler feature, and generally is only useful for system framework authors.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;If you want to dig deeper into this topic, the &lt;a href=&quot;https://swift.org/blog/library-evolution/&quot;&gt;Library Evolution blog post&lt;/a&gt; provides more details about the underlying concepts, and when it is a good idea to enable library evolution support (and when not).&lt;/p&gt;
&lt;h2&gt;Closure&lt;/h2&gt;
&lt;p&gt;I always find it fascinating how much engineering goes into &lt;a href=&quot;https://twitter.com/peterfriese/status/1402362507881336843&quot;&gt;seemingly small&lt;/a&gt; features of the language and framework features we often take for granted. &lt;em&gt;Thanks to everyone who worked on this!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As &lt;a href=&quot;https://twitter.com/ricketson_&quot;&gt;Matt&lt;/a&gt; mentions in &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10018/&quot;&gt;What’s New in SwiftUI&lt;/a&gt; (at the 7:37 mark), this feature will help us build more error-free (and performant!) apps, so now is a good time to go through your code and update all your lists to use the new features.&lt;/p&gt;
&lt;p&gt;Thanks for reading! 🔥&lt;/p&gt;
</content:encoded></item><item><title>Mapping Firestore Data in Swift</title><link>https://peterfriese.dev/blog/2021/firestore-codable-the-comprehensive-guide/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/firestore-codable-the-comprehensive-guide/</guid><description>Learn everything about how to map Firestore documents to Swift and back</description><pubDate>Tue, 23 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;Swift’s Codable API, introduced in Swift 4, enables us to leverage the power of the compiler to make it easier to map data from serialised formats to Swift types.&lt;/p&gt;
&lt;p&gt;You might have been using Codable to map data from a web API to your app’s data model (and vice versa), but it is much more flexible than that.&lt;/p&gt;
&lt;p&gt;In this article, we’re going to look at how Codable can be used to map data from &lt;a href=&quot;https://firebase.google.com/products/firestore&quot;&gt;Firestore&lt;/a&gt; to Swift types and vice versa.&lt;/p&gt;
&lt;p&gt;If you’re new to Cloud Firestore - it is a &lt;a href=&quot;https://www.youtube.com/watch?v=v_hR4K4auoQ&amp;amp;t=1s&quot;&gt;horizontally scaling NoSQL document database in the cloud&lt;/a&gt;, part of Firebase’s &lt;a href=&quot;https://firebase.google.com/products-build&quot;&gt;Backend-as-a-Service products&lt;/a&gt;. Data is stored in documents that contain fields mapping to values. Documents are stored in collections, which you can think of as containers that you can use to organise your data. Documents support many &lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/data-types&quot;&gt;data types&lt;/a&gt; such as strings and numbers, but also complex, nested objects. You can even create subcollections within documents, and build hierarchical data structures.&lt;/p&gt;
&lt;p&gt;When fetching a document from Firestore, your app will receive a dictionary of key/value pairs (or an array of dictionaries, if you use one of the operations returning multiple documents).&lt;/p&gt;
&lt;p&gt;Now, you can certainly use dictionaries in Swift, and they offer some great flexibility that might be exactly what your use case calls for. However, this approach isn’t type-safe and it’s easy to introduce hard-to-track-down bugs by misspelling attribute names, or forgetting to map that new attribute your team added when they shipped that exciting new feature last week.&lt;/p&gt;
&lt;p&gt;In the past, many developers have worked around these shortcomings by implementing a simple mapping layer that allowed them to map dictionaries to Swift types. But again, most of these implementations are based on manually specifying the mapping between Firestore documents and the corresponding types of your app’s data model.&lt;/p&gt;
&lt;p&gt;With Firestore’s support for Swift’s Codable API, this becomes a lot easier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You will no longer have to manually implement any mapping code&lt;/li&gt;
&lt;li&gt;It’s easy to define how to map attributes with different names&lt;/li&gt;
&lt;li&gt;It has built-in support for many of Swift’s types&lt;/li&gt;
&lt;li&gt;And it’s easy to add support for mapping custom types&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And - best of all: for simple data models, you won’t have to write any mapping code at all.&lt;/p&gt;
&lt;h2&gt;Mapping Data&lt;/h2&gt;
&lt;p&gt;Cloud Firestore stores data in documents which map keys to values. To fetch data from an individual document, we can call &lt;code&gt;DocumentSnapshot.data()&lt;/code&gt;, which returns a dictionary mapping the field names to an &lt;code&gt;Any: func data() -&amp;gt; [String : Any]?.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This means we can use Swift’s subscript syntax to access each individual field, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#warning(&quot;DO NOT MAP YOUR DOCUMENTS MANUALLY. USE CODABLE INSTEAD.&quot;)
func fetchBook(documentId: String) {
  let docRef = db.collection(&quot;books&quot;).document(documentId)

  docRef.getDocument { document, error in
    if let error = error as NSError? {
    self.errorMessage = &quot;Error getting document: \(error.localizedDescription)&quot;
    }
    else {
      if let document = document {
        let id = document.documentID
        let data = document.data()
        let title = data?[&quot;title&quot;] as? String ?? &quot;&quot;
        let numberOfPages = data?[&quot;numberOfPages&quot;] as? Int ?? 0
        let author = data?[&quot;author&quot;] as? String ?? &quot;&quot;
        self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it might seem straightforward and easy to implement, this code is fragile, hard to maintain, and error-prone. As you can see, we’re making assumptions about the data types of the document fields . These might or might not be correct (remember, as there is no schema, you can easily add a new document to the collection and choose a different type for a field - you might accidentally choose &lt;code&gt;string&lt;/code&gt; for the &lt;code&gt;numberOfPages&lt;/code&gt; field, which would result in a difficult-to-find mapping issue). Also, you’ll have to update your mapping code whenever a new field is added, which is rather cumbersome. And let&apos;s not forget that we&apos;re not taking advantage of Swift&apos;s strong type system, which knows exactly what&apos;s the correct type for each of the properties of &lt;code&gt;Book&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;So, please don’t map your documents manually - use Codable instead!&lt;/p&gt;
&lt;h2&gt;What is Codable, anyway?&lt;/h2&gt;
&lt;p&gt;According to Apple’s &lt;a href=&quot;https://developer.apple.com/documentation/swift/codable&quot;&gt;documentation&lt;/a&gt;, Codable is “&lt;em&gt;a type that can convert itself into and out of an external representation.&lt;/em&gt;” In fact, &lt;code&gt;Codable&lt;/code&gt; is a type alias for the &lt;code&gt;Encodable&lt;/code&gt; and &lt;code&gt;Decodable&lt;/code&gt; protocols. By conforming a Swift type to this protocol, the compiler will synthesise the code needed to encode/decode an instance of this type from a serialised format, such as JSON.&lt;/p&gt;
&lt;p&gt;A simple type for storing data about a book might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Book: Codable {
  var title: String
  var numberOfPages: Int
  var author: String
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, conforming the type to &lt;code&gt;Codable&lt;/code&gt; is minimally invasive - we only had to add the conformance to the protocol, no other changes are required.&lt;/p&gt;
&lt;p&gt;With this in place, we can now easily encode a book to a JSON object:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;do {
  let book = Book(title: &quot;The Hitchhiker&apos;s Guide to the Galaxy&quot;,
                  numberOfPages: 816,
                  author: &quot;Douglas Adams&quot;)
  let encoder = JSONEncoder()
  let data = try encoder.encode(user)
}
catch {
  print(&quot;Error when trying to encode book: \(error)&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Decoding a JSON object to a &lt;code&gt;Book&lt;/code&gt; instance works as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let decoder = JSONDecoder()
let decodedBook = try decoder.decode(Book.self, from: data)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more details about &lt;code&gt;Codable&lt;/code&gt;, I recommend the following resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;John Sundell has a nice article about the &lt;a href=&quot;https://www.swiftbysundell.com/basics/codable/&quot;&gt;Basics of Codable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If books are more your thing, check out Mattt&apos;s &lt;a href=&quot;https://flight.school/books/codable&quot;&gt;Flight School Guide to Swift Codable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;And finally, Donny Wals has an entire &lt;a href=&quot;https://www.donnywals.com/category/codable/&quot;&gt;series about Codable&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mapping simple types using Codable&lt;/h2&gt;
&lt;p&gt;Firestore supports a broad &lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/data-types&quot;&gt;set of data types&lt;/a&gt;, ranging from simple strings to nested maps. Most of these correspond directly to Swift&apos;s built-in types. Let&apos;s take a look at mapping some simple data types first before we dive into the more complex ones.&lt;/p&gt;
&lt;p&gt;To map Firestore documents to Swift types, follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;FirebaseFirestoreSwift&lt;/code&gt; to your project. You can use either CocoaPods or the &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk/blob/master/SwiftPackageManager.md&quot;&gt;Swift Package Manager&lt;/a&gt; to do so.&lt;/li&gt;
&lt;li&gt;Import &lt;code&gt;FirebaseFirestoreSwift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Conform your type to &lt;code&gt;Codable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add an &lt;code&gt;id&lt;/code&gt; property to your type, and use &lt;code&gt;@DocumentID&lt;/code&gt; to tell Firestore to map this to the document ID&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;documentReference.data(as: )&lt;/code&gt; to map a document reference to a Swift type&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;documentReference.setData(from: )&lt;/code&gt; to map data from Swift types to a Firestore document&lt;/li&gt;
&lt;li&gt;(optional, but highly recommended) Implement proper error handling&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s update our &lt;code&gt;Book&lt;/code&gt; type accordingly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Book: Codable {
  @DocumentID var id: String?
  var title: String
  var numberOfPages: Int
  var author: String
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this type was already codable, we only had to add the &lt;code&gt;id&lt;/code&gt; property and annotate it with the &lt;code&gt;@DocumentID&lt;/code&gt; property wrapper.&lt;/p&gt;
&lt;p&gt;Taking the previous code snippet for fetching and mapping a document, we can replace all the manual mapping code with a single line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func fetchBook(documentId: String) {
  let docRef = db.collection(&quot;books&quot;).document(documentId)

  docRef.getDocument { document, error in
    if let error = error as NSError? {
      self.errorMessage = &quot;Error getting document: \(error.localizedDescription)&quot;
    }
    else {
      if let document = document {
        do {
          self.book = try document.data(as: Book.self)
        }
        catch {
          print(error)
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can even write this more concisely by specifying the type of the document when calling &lt;code&gt;getDocument(as:)&lt;/code&gt;. This will perform the mapping for you, and return a &lt;code&gt;Result&lt;/code&gt; type containing the mapped document or an error in case decoding failed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private func fetchBook(documentId: String) {
  let docRef = db.collection(&quot;books&quot;).document(documentId)

||&amp;gt;  docRef.getDocument(as: Book.self) { result in&amp;lt;||
    switch result {
    case .success(let book):
      // A Book value was successfully initialized from the DocumentSnapshot.
      self.book = book
      self.errorMessage = nil
    case .failure(let error):
      // A Book value could not be initialized from the DocumentSnapshot.
      self.errorMessage = &quot;Error decoding document: \(error.localizedDescription)&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Updating an existing document is as simple as calling &lt;code&gt;documentReference.setData(from: )&lt;/code&gt;. Including some basic error handling, here is the code to save a &lt;code&gt;Book&lt;/code&gt; instance:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func updateBook(book: Book) {
  if let id = book.id {
    let docRef = db.collection(&quot;books&quot;).document(id)
    do {
||&amp;gt;      try docRef.setData(from: book)&amp;lt;||
    }
    catch {
      print(error)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When adding a new document, Firestore will automatically take care of assigning a new document ID to the document. This also works when the app is currently offline.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func addBook(book: Book) {
  let collectionRef = db.collection(&quot;books&quot;)
  do {
||&amp;gt;    let newDocReference = try collectionRef.addDocument(from: self.book)&amp;lt;||
    print(&quot;Book stored with new document reference: \(newDocReference)&quot;)
  }
  catch {
    print(error)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to mapping simple data types, Firestore supports a number of other datatypes, some of which are structured types that you can use to create compound types inside a document.&lt;/p&gt;
&lt;h2&gt;Mapping nested custom types&lt;/h2&gt;
&lt;p&gt;Most attributes we want to map in our documents are simple values, such as the book’s title or the author’s name. But what about those cases when we need to store a more complex object? For example, we might want to store the URLs to the book’s cover in different resolutions.&lt;/p&gt;
&lt;p&gt;The easiest way to do this in Firestore is to use a &lt;em&gt;map&lt;/em&gt;:

When writing the corresponding Swift &lt;code&gt;struct&lt;/code&gt;, we can make use of the fact that Firestore supports URLs - when storing a field that contains a URL, it will be converted to a &lt;code&gt;string&lt;/code&gt; and vice versa (see &lt;a href=&quot;https://cs.opensource.google/firebase-sdk/firebase-ios-sdk/+/master:Firestore/third_party/FirestoreEncoder/FirestoreEncoder.swift;l=459?q=Encoder&amp;amp;ss=firebase-sdk%2Ffirebase-ios-sdk&quot;&gt;FirestoreEncoder.swift&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct CoverImages: Codable {
  var small: URL
  var medium: URL
  var large: URL
}

struct BookWithCoverImages: Codable {
  @DocumentID var id: String?
  var title: String
  var numberOfPages: Int
  var author: String
  var cover: CoverImages?
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how we defined a struct, &lt;code&gt;CoverImages&lt;/code&gt; , for the &lt;code&gt;cover&lt;/code&gt; map in the Firestore document. By marking the &lt;code&gt;cover&lt;/code&gt; property on &lt;code&gt;BookWithCoverImages&lt;/code&gt; as optional, we’re able to handle the fact that some documents might not contain a &lt;code&gt;cover&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;If you’re curious why there is no code snippet for fetching or updating data, you will be pleased to hear that there is no need to adjust the code for reading or writing from/to Firestore: all of this works with the code we’ve written in the initial section.&lt;/p&gt;
&lt;h2&gt;Mapping arrays&lt;/h2&gt;
&lt;p&gt;Sometimes, we want to store a collection of values in a document. The &lt;a href=&quot;https://book-genres.com/book-genres-a-z/&quot;&gt;genres of a book&lt;/a&gt; are a good example: a book like &lt;em&gt;The Hitchhiker’s Guide to the Galaxy&lt;/em&gt; might fall into several categories - in this case &lt;em&gt;Sci-Fi&lt;/em&gt; and &lt;em&gt;Comedy:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In Firestore, we can model this using an &lt;code&gt;array&lt;/code&gt; of values. This is supported for any codable types (such as &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Int&lt;/code&gt;, etc.). The following shows how to add an array of genres to our &lt;code&gt;Book&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public struct BookWithGenre: Codable {
  @DocumentID var id: String?
  var title: String
  var numberOfPages: Int
  var author: String
||&amp;gt;  var genres: [String]&amp;lt;||
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this works for any codable type, we can use custom types as well. Imagine we’d want to store a list of tags for each book. Along with the name of the tag, we’d like to store the color of the tag as well, like this:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To store tags in this way, all we need to do is implement a &lt;code&gt;Tag&lt;/code&gt; struct to represent a tag and make it codable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Tag: Codable, Hashable {
  var title: String
  var color: String
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just like that, we can store an array of &lt;code&gt;Tag&lt;/code&gt;s in our &lt;code&gt;Book&lt;/code&gt; documents!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BookWithTags: Codable {
  @DocumentID var id: String?
  var title: String
  var numberOfPages: Int
  var author: String
||&amp;gt;  var tags: [Tag]&amp;lt;||
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A quick word about mapping document IDs&lt;/h2&gt;
&lt;p&gt;Before we move on to more mapping more types, let’s talk about mapping document IDs for a moment.&lt;/p&gt;
&lt;p&gt;We’ve used the &lt;code&gt;@DocumentID&lt;/code&gt; property wrapper in some of the previous examples to map the document ID of our Firestore documents to the &lt;code&gt;id&lt;/code&gt; property of our Swift types. This is important for a number of reasons: one, it helps us to know which document to update in case the user makes local changes. And two, SwiftUI&apos;s &lt;code&gt;List&lt;/code&gt; requires its elements to be &lt;code&gt;Identifiable&lt;/code&gt; in order to prevent elements from jumping around when they get inserted.&lt;/p&gt;
&lt;p&gt;It’s worth pointing out that an attribute marked as &lt;code&gt;@DocumentID&lt;/code&gt; will &lt;em&gt;not&lt;/em&gt; be encoded by Firestore&apos;s encoder when writing the document back. This is because the document ID is not an attribute of the document itself - so writing it to the document would be a mistake.&lt;/p&gt;
&lt;p&gt;When working with nested types (such as the array of tags on the &lt;code&gt;Book&lt;/code&gt; in an earlier example in this article), it is not required to add an &lt;code&gt;@DocumentID&lt;/code&gt; property: nested properties are a part of the Firestore document, and do not constitute a separate document. Hence, they do not need a document ID.&lt;/p&gt;
&lt;h2&gt;Mapping dates and times&lt;/h2&gt;
&lt;p&gt;Firestore has a built-in data type for handling dates and times, and - thanks to Firestore’s support for Codable, it’s straightforward to use them.&lt;/p&gt;
&lt;p&gt;Let’s take a look at this document which represents the mother of all programming languages, Ada - invented in 1843:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;A Swift type for mapping this document might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ProgrammingLanguage: Codable {
  @DocumentID var id: String?
  var name: String
  var year: Date
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We cannot leave this section about dates and times without having a conversation about &lt;code&gt;@ServerTimestamp&lt;/code&gt;. This property wrapper is a powerhouse when it comes to dealing with timestamps in your app.&lt;/p&gt;
&lt;p&gt;In any distributed system, chances are that the clocks on the individual systems are not completely in sync all of the time. You might think this is not a big deal, but imagine the implications of a clock running slightly out of sync for a stock trade system: even a millisecond deviation might result in a difference of millions of dollars when executing a trade.&lt;/p&gt;
&lt;p&gt;Firestore handles attributes marked with &lt;code&gt;@ServerTimestamp&lt;/code&gt; as follows: if the attribute is &lt;code&gt;nil&lt;/code&gt; when you store it (using &lt;code&gt;addDocument&lt;/code&gt;, for example), Firestore will populate the field with the current server timestamp at the time of writing it into the database. If the field is not &lt;code&gt;nil&lt;/code&gt; when you call &lt;code&gt;addDocument()&lt;/code&gt; or &lt;code&gt;updateData()&lt;/code&gt;, Firestore will leave the attribute value untouched. This way, it is easy to implement fields like &lt;code&gt;createdAt&lt;/code&gt; and &lt;code&gt;lastUpdatedAt&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Mapping GeoPoints&lt;/h2&gt;
&lt;p&gt;One of the most magical experiences when I got my first smartphone was to be able to pinpoint my exact location on a map and find the nearest café. Many exciting features become possible by storing geolocations. For example, it might be useful to store a location for a task so your app can remind you about a task when you reach a destination.&lt;/p&gt;
&lt;p&gt;Firestore has a built-in data type &lt;em&gt;GeoPoint&lt;/em&gt; that can store the longitude and latitude of any location. To map locations from / to a Firestore document, we can use the &lt;code&gt;GeoPoint&lt;/code&gt; type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Office: Codable {
  @DocumentID var id: String?
  var name: String
  var location: GeoPoint
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Converting a &lt;code&gt;GeoPoint&lt;/code&gt; to a &lt;code&gt;CLLocationCoordinate2D&lt;/code&gt; is a straight-forward operation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CLLocationCoordinate2D(latitude: office.location.latitude,
                      longitude: office.location.longitude)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more about querying documents by physical location, check out &lt;a href=&quot;https://firebase.google.com/docs/firestore/solutions/geoqueries#solution_geohashes&quot;&gt;this solution guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Mapping Colors&lt;/h2&gt;
&lt;p&gt;“How can I map colors” is one of the most frequently asked questions, not only for Firestore, but for mapping between Swift and JSON as well. There are plenty of solutions out there, but most of them focus on JSON, and almost all of them map colors as a nested dictionary composed of its RGB components.&lt;/p&gt;
&lt;p&gt;I’ve been mildly dissatisfied with all of these solutions and always felt there should be a better, simpler solution. Why don’t we use web colors (or, to be more specific, &lt;a href=&quot;https://drafts.csswg.org/css-color/#hex-color&quot;&gt;CSS hex color notation&lt;/a&gt;) - they’re easy to use (essentially just a string), and they even support &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/css-hex-code-colors-alpha-values&quot;&gt;transparency&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;To be able to map a Swift &lt;code&gt;Color&lt;/code&gt; to its hex value, we need to create a Swift extension that adds &lt;code&gt;Codable&lt;/code&gt; to &lt;code&gt;Color.&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Color: Codable {
  init(hex: String) {
    let rgba = hex.toRGBA()

    self.init(.sRGB,
              red: Double(rgba.r),
              green: Double(rgba.g),
              blue: Double(rgba.b),
              opacity: Double(rgba.alpha))
    }

  public init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let hex = try container.decode(String.self)

    self.init(hex: hex)
  }

  public func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    try container.encode(toHex)
  }

  //... (code for translating between hex and RGBA ommitted for brevity)

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using &lt;code&gt;decoder.singleValueContainer()&lt;/code&gt;, we can decode a &lt;code&gt;String&lt;/code&gt; to its &lt;code&gt;Color&lt;/code&gt; equivalent, without having to nest the RGBA components. Plus, you can use these values in the web UI of your app, without having to convert them first!&lt;/p&gt;
&lt;p&gt;With this, we can update code for mapping tags, making it easier to handle the tag colors directly instead of having to map them manually in our app’s UI code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Tag: Codable, Hashable {
  var title: String
  var color: Color
}

struct BookWithTags: Codable {
  @DocumentID var id: String?
  var title: String
  var numberOfPages: Int
  var author: String
  var tags: [Tag]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Mapping Enums&lt;/h2&gt;
&lt;p&gt;Enums are probably one of the most underrated language features in Swift - there’s much more to them than meets the eye. A common use case for enums is to model the discrete states of something. For example, we might be writing an app for managing articles. To track the status of an article, we might want to use an enum &lt;code&gt;Status&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum Status: String, Codable {
  case draft
  case inReview
  case approved
  case published
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Firestore doesn’t support enums natively (i.e. it cannot enforce the set of values), but we can still make use of the fact that enums can be typed, and choose a codable type. In this example, we’ve chosen &lt;code&gt;String&lt;/code&gt; , which means all enum values will be mapped to/from &lt;code&gt;string&lt;/code&gt; when stored in a Firestore document.&lt;/p&gt;
&lt;p&gt;And, since Swift supports custom raw values, we can even customise which values refer to which enum case. So for example, if we decided to store the &lt;code&gt;Status.inReview&lt;/code&gt; case as &lt;code&gt;in review&lt;/code&gt;, we could just update the above enum as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum Status: String, Codable {
  case draft
  case inReview = &quot;in review&quot;
  case approved
  case published
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Customising the mapping&lt;/h2&gt;
&lt;p&gt;Sometimes, the attribute names of the Firestore documents we want to map don’t match up with the names of the properties of our data model in Swift. For example, one of our coworkers might be a Python developer, and decided to choose &lt;code&gt;snake_case&lt;/code&gt; for all their attribute names 🐍.&lt;/p&gt;
&lt;p&gt;Not to worry - Codable has us covered!&lt;/p&gt;
&lt;p&gt;For cases like these, we can make use of &lt;code&gt;CodingKeys&lt;/code&gt; . This is an &lt;code&gt;enum&lt;/code&gt; we can add to a codable struct to specify how certain attributes will be mapped.&lt;/p&gt;
&lt;p&gt;Consider this document:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To map this document to a &lt;code&gt;struct&lt;/code&gt; that has a &lt;code&gt;name&lt;/code&gt; property of type &lt;code&gt;String&lt;/code&gt;, we need to add a &lt;code&gt;CodingKeys&lt;/code&gt; enum to the &lt;code&gt;ProgrammingLanguage&lt;/code&gt; struct, and specify the name of the attribute in the document:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ProgrammingLanguage: Codable {
  @DocumentID var id: String?
  var name: String
  var year: Date

  enum CodingKeys: String, CodingKey {
    case id
    case name = &quot;language_name&quot;
    case year
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
By default, the Codable API will use the property names of our Swift types to determine the
attribute names on the Firestore documents we’re trying to map. So as long as the attribute names
match, there is no need to add &lt;code&gt;CodingKeys&lt;/code&gt; to our codable types. However, once we use
&lt;code&gt;CodingKeys&lt;/code&gt; for a specific type, we need to add &lt;em&gt;all&lt;/em&gt; property names we want to map. Any property
that is not listed as a case on the respective &lt;code&gt;CodingKeys&lt;/code&gt; enum will be ignore during the mapping
process.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;This can actually be convenient if we specifically want to exclude some of the properties from being mapped.&lt;/p&gt;
&lt;p&gt;So for example, if we want to exclude the &lt;code&gt;reasonWhyILoveThis&lt;/code&gt; property from being mapped, all we need to do is to remove it from the &lt;code&gt;CodingKeys&lt;/code&gt; enum:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ProgrammingLanguage: Identifiable, Codable {
  @DocumentID var id: String?
  var name: String
  var year: Date
  var reasonWhyILoveThis: String = &quot;&quot;

  enum CodingKeys: String, CodingKey {
    case id
    case name = &quot;language_name&quot;
    case year
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Occasionally we might want to write an empty attribute back into the Firestore document. Swift has the notion of optionals to denote the absence of a value, and Firestore supports &lt;code&gt;null&lt;/code&gt; values as well. However, the default behaviour for encoding optionals that have a &lt;code&gt;nil&lt;/code&gt; value is to just omit them. &lt;code&gt;@ExplicitNull&lt;/code&gt; gives us some control over how Swift optionals are handled when encoding them: by flagging an optional property as &lt;code&gt;@ExplicitNull&lt;/code&gt;, we can tell Firestore to write this property to the document with a &lt;code&gt;null&lt;/code&gt; value if it contains a value of &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Error Handling&lt;/h2&gt;
&lt;p&gt;In the above code snippets I intentionally kept error handling at a minimum, but in any production app, you’ll want to make sure to gracefully handle any errors.&lt;/p&gt;
&lt;p&gt;Here is a code snippet that show how to use handle any error situations you might run into:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MappingSimpleTypesViewModel: ObservableObject {
  @Published var book: Book = .empty
  @Published var errorMessage: String?

  private var db = Firestore.firestore()

  func fetchAndMap() {
    fetchBook(documentId: &quot;hitchhiker&quot;)
  }

  func fetchAndMapNonExisting() {
    fetchBook(documentId: &quot;does-not-exist&quot;)
  }

  func fetchAndTryMappingInvalidData() {
    fetchBook(documentId: &quot;invalid-data&quot;)
  }

  private func fetchBook(documentId: String) {
    let docRef = db.collection(&quot;books&quot;).document(documentId)

    docRef.getDocument(as: Book.self) { result in
      switch result {
      case .success(let book):
        // A Book value was successfully initialized from the DocumentSnapshot.
        self.book = book
        self.errorMessage = nil
      case .failure(let error):
        // A Book value could not be initialized from the DocumentSnapshot.
        switch error {
        case DecodingError.typeMismatch(_, let context):
          self.errorMessage = &quot;\(error.localizedDescription): \(context.debugDescription)&quot;
        case DecodingError.valueNotFound(_, let context):
          self.errorMessage = &quot;\(error.localizedDescription): \(context.debugDescription)&quot;
        case DecodingError.keyNotFound(_, let context):
          self.errorMessage = &quot;\(error.localizedDescription): \(context.debugDescription)&quot;
        case DecodingError.dataCorrupted(let key):
          self.errorMessage = &quot;\(error.localizedDescription): \(key)&quot;
        default:
          self.errorMessage = &quot;Error decoding document: \(error.localizedDescription)&quot;
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The previous code snippet demonstrates how to handle errors when fetching a single document. In addition to fetching data once, Firestore also supports delivering updates to your app as they happen, using so-called &lt;em&gt;snapshot listeners&lt;/em&gt;: we can register a snapshot listener on a collection (or query), and Firestore will call our listener whenever there is an update.&lt;/p&gt;
&lt;p&gt;Here is a code snippet that show how to register a snapshot listener, map data using Codable, and handle any errors that might occur. It also shows how to add a new document to the collection. As you will see, there is no need to update the local array holding the mapped documents ourselves, as this is taken care of by the code in the snapshot listener.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MappingColorsViewModel: ObservableObject {
  @Published var colorEntries = [ColorEntry]()
  @Published var newColor = ColorEntry.empty
  @Published var errorMessage: String?

  private var db = Firestore.firestore()
  private var listenerRegistration: ListenerRegistration?

  public func unsubscribe() {
    if listenerRegistration != nil {
      listenerRegistration?.remove()
      listenerRegistration = nil
    }
  }

  func subscribe() {
    if listenerRegistration == nil {
      listenerRegistration = db.collection(&quot;colors&quot;)
        .addSnapshotListener { [weak self] (querySnapshot, error) in
          guard let documents = querySnapshot?.documents else {
            self?.errorMessage = &quot;No documents in &apos;colors&apos; collection&quot;
            return
          }

          self?.colorEntries = documents.compactMap { queryDocumentSnapshot in
            let result = Result { try queryDocumentSnapshot.data(as: ColorEntry.self) }

            switch result {
            case .success(let colorEntry):
              if let colorEntry = colorEntry {
                // A ColorEntry value was successfully initialized from the DocumentSnapshot.
                self?.errorMessage = nil
                return colorEntry
              }
              else {
                // A nil value was successfully initialized from the DocumentSnapshot,
                // or the DocumentSnapshot was nil.
                self?.errorMessage = &quot;Document doesn&apos;t exist.&quot;
                return nil
              }
            case .failure(let error):
              // A Book value could not be initialized from the DocumentSnapshot.
              switch error {
              case DecodingError.typeMismatch(_, let context):
                self?.errorMessage = &quot;\(error.localizedDescription): \(context.debugDescription)&quot;
              case DecodingError.valueNotFound(_, let context):
                self?.errorMessage = &quot;\(error.localizedDescription): \(context.debugDescription)&quot;
              case DecodingError.keyNotFound(_, let context):
                self?.errorMessage = &quot;\(error.localizedDescription): \(context.debugDescription)&quot;
              case DecodingError.dataCorrupted(let key):
                self?.errorMessage = &quot;\(error.localizedDescription): \(key)&quot;
              default:
                self?.errorMessage = &quot;Error decoding document: \(error.localizedDescription)&quot;
              }
              return nil
            }
          }
        }
    }
  }

  func addColorEntry() {
    let collectionRef = db.collection(&quot;colors&quot;)
    do {
      let newDocReference = try collectionRef.addDocument(from: newColor)
      print(&quot;ColorEntry stored with new document reference: \(newDocReference)&quot;)
    }
    catch {
      print(error)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All code snippets used in this post are part of a sample application that you can download from &lt;a href=&quot;https://github.com/peterfriese/Swift-Firestore-Guide&quot;&gt;this GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Go forth and use Codable!&lt;/h2&gt;
&lt;p&gt;Swift’s Codable API provides a powerful and flexible way to map data from serialised formats to and from your applications data model. In this article, you saw how easy it is to use in apps that use Firestore as their data store.&lt;/p&gt;
&lt;p&gt;Starting from a basic example with simple data types, we progressively increased the complexity of the data model, all the while being able to rely on Codable and Firebase’s implementation to perform the mapping for us.&lt;/p&gt;
&lt;p&gt;There really is no reason for not using Firestore’s Codable support.&lt;/p&gt;
&lt;p&gt;For additional information, &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;follow me&lt;/a&gt; on Twitter and subscribe to &lt;a href=&quot;https://www.youtube.com/c/PeterFriese&quot;&gt;my YouTube channel&lt;/a&gt;, where I regularly post content about Swift and Firebase.&lt;/p&gt;
&lt;p&gt;Thanks for reading! 🔥&lt;/p&gt;
</content:encoded></item><item><title>Using async/await in SwiftUI</title><link>https://peterfriese.dev/blog/2021/async-await-in-swiftui/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2021/async-await-in-swiftui/</guid><description>How to write asynchronous code without deeply nested callbacks</description><pubDate>Tue, 09 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox  from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Note&lt;/strong&gt; !&lt;/p&gt;
&lt;p&gt;As of WWDC 2021, async/await and many other features of Swift Concurrency are now available in Swift 5.5, as part of Xcode 13b1. Some of the information presented in this article is now out-of date, but the general priciples still apply.
I&apos;ve published an up-to-date introduction to using Swift Concurrency and async/await in SwiftUI here: &lt;a href=&quot;../swiftui-concurrency-essentials-part1/&quot;&gt;Getting Started with async/await in SwiftUI&lt;/a&gt;
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;A lot of the code we write has to deal with asynchronous behaviour. Fetching data from the disk, sending a request to a remote API, or downloading an image - all these operations take time, even on your super-fast, low-latency working-from-home network.&lt;/p&gt;
&lt;p&gt;A simple way of dealing with this is to just wait until a call has finished and the data we requested has arrived. The problem with this approach is that your app’s UI will freeze while it’s waiting. We’ve all used apps that seem to completely freeze up for certain tasks - it’s a terrible user experience.&lt;/p&gt;
&lt;p&gt;So how can we do better?&lt;/p&gt;
&lt;p&gt;One way is to perform long-running tasks on a background thread and call back into the main application once the result has arrived. There are a number of ways to implement this. As iOS developers, we’re familiar with delegates: you make a call to a method, and a few moments later you will receive the result on one of the delegate methods. This works well, but the resulting code is spread out all over the place and rather hard to read.&lt;/p&gt;
&lt;p&gt;Closures provide a slightly more elegant way to write asynchronous code. Let’s look at some pseudocode for a real-world example: ordering a meal in a restaurant.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chatWithFriends()
placeOrder(theOrder) { meal in
  eat(meal)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important thing here is that you can continue chatting with your friends while waiting for your order to arrive. The code in curly braces after the call to &lt;code&gt;placeOrder&lt;/code&gt; is called a trailing closure, and it is only called once the meal has been prepared and is delivered to your place.&lt;/p&gt;
&lt;p&gt;This is very different from synchronous code, where the computer executes one statement after the other in a sequential order. The following pseudocode is much more similar to how we &lt;em&gt;perceive&lt;/em&gt; this situation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;placeOrder(theOrder)
chatWithFriends()
meal = receiveMeal()
eat(meal)
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;We place the order&lt;/li&gt;
&lt;li&gt;Then, while we’re waiting for the meal to arrive, we chat with our friends&lt;/li&gt;
&lt;li&gt;After a little while the meal arrives, and we can enjoy it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Using closures and callbacks to deal with asynchronous code is common these days, and many iOS SDKs, such as Alamofire, AWS Amplify, Firebase, and even Apple’s very own &lt;code&gt;URLSession&lt;/code&gt; make use of it.&lt;/p&gt;
&lt;p&gt;However, once you try to coordinate several asynchronous calls, this kind of code can quickly become hard to read, and - what’s worse - difficult to get right. Wouldn’t it be nice if we were able to write asynchronous code in a way that looks similar to the second code snippet?&lt;/p&gt;
&lt;p&gt;The good news is, we can - the Swift team has proposed an addition to the Swift language that will allow us to do just that. The proposal is named &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md&quot;&gt;SE-0296&lt;/a&gt;, and is available in the development snapshots of the Swift compiler toolchain. It adds support for async/await to Swift, which will allow us to make asynchronous calls without using callbacks.&lt;/p&gt;
&lt;p&gt;In this article, I am going to show you how to install the compiler toolchain, activate this new feature, and rewrite a callback-based code snippet to make use of async/await.&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://youtu.be/K6UzzJOXYsA&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Installing the experimental compiler toolchain&lt;/h2&gt;
&lt;p&gt;To experiment with this feature, we first need to download the Swift compiler toolchain from the &lt;a href=&quot;https://swift.org/download/&quot;&gt;downloads page&lt;/a&gt; on the Swift website. Scroll down to the &lt;em&gt;Trunk Development (main)&lt;/em&gt; section and click on the &lt;em&gt;Xcode&lt;/em&gt; link. The link title is a bit misleading - the download doesn’t actually contain a copy of Xcode. It’s really just the compiler toolchain.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;After the download has finished, we can install it by double-clicking the package.&lt;/p&gt;
&lt;p&gt;To activate the compiler toolchain, we need to launch Xcode and select the toolchain. Go to &lt;em&gt;Preferences &amp;gt; Components &amp;gt; Toolchains&lt;/em&gt;, and select &lt;em&gt;Swift Development Snapshot&lt;/em&gt;. In this dialog, you’ll also see the official toolchain - it’s labelled &lt;em&gt;Xcode 12.5&lt;/em&gt;. Don’t forget to switch back to this original toolchain when you’re going back to working on your work projects.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Once the toolchain is activated, Xcode will display a blue chain icon in the status area:
&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
It’s worth keeping in mind that this is an experimental feature, and things might change between now and when it is launched. Most importantly, it is not possible to ship builds to the App Store using the experimental toolchain. You also can’t run on a real device, just on the Simulator. So - don’t use this in your production code.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h2&gt;Enabling experimental support for concurrency&lt;/h2&gt;
&lt;p&gt;Before we can make use of &lt;em&gt;async/await&lt;/em&gt;, we need to enable experimental concurrency in the build settings for our project.&lt;/p&gt;
&lt;p&gt;To do this, we need to open the build settings, find &lt;em&gt;Other Swift Flags&lt;/em&gt;, and add &lt;code&gt;-Xfrontend -enable-experimental-concurrency&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;a href=&quot;https://github.com/apple/swift/pull/35784&quot;&gt;PR 35784&lt;/a&gt; enables &lt;code&gt;async/await&lt;/code&gt; by default, so once this rolls out you can use this feature without having to enable experimental support - installing the toolchain will be enough.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;You might see an error message saying &lt;em&gt;“Could not launch (application name) - LLDB provided no error string”&lt;/em&gt; when trying to launch your app. If this happens, turn off debugging for your application target: Select your target, then choose &lt;em&gt;Edit Scheme… &amp;gt; Run&lt;/em&gt; and de-select the &lt;em&gt;Debug executable&lt;/em&gt; checkbox
&lt;/p&gt;
&lt;h2&gt;Check out the sample project&lt;/h2&gt;
&lt;p&gt;If you want to follow along, feel free to download the sample project for this article from &lt;a href=&quot;https://github.com/peterfriese/Swift-Async-Await-Experiments&quot;&gt;this GitHub repo&lt;/a&gt;, switch to the &lt;a href=&quot;https://github.com/peterfriese/Swift-Async-Await-Experiments/tree/blog/article_analyser/starter&quot;&gt;&lt;em&gt;starter&lt;/em&gt;&lt;/a&gt; branch and then open the &lt;em&gt;ArticleAnalyser&lt;/em&gt; project.&lt;/p&gt;
&lt;p&gt;The sample project contains some functionality I’ve taken from another project I am working on at the moment: Users can add links to articles they want to read later, and the app will analyse the web page and extract information like the title, hero image, and other meta information. In addition, it will use some Natural Language APIs to compute meaningful tags for the text. For example, if you paste a link to this article, it should suggest the tags &lt;em&gt;concurrency&lt;/em&gt; and &lt;em&gt;Swift&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Fetching the text of the web page is an asynchronous task, and performing the analyses might take a moment too - especially if we’re going to use any cloud-based APIs. At the moment, the code uses Apple’s &lt;code&gt;NLTagger&lt;/code&gt; APIs, but in the future we might decide to use &lt;a href=&quot;https://cloud.google.com/natural-language&quot;&gt;Google’s Cloud Natural Language APIs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As this is a multi-step process, I’ve defined an interface to help us keep things nice and organised:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protocol ArticleAnalyser {
  // fetch the article and return the entire HTML text
  func fetchArticle(from url: String, completion: @escaping (Result&amp;lt;String, AnalyserError&amp;gt;) -&amp;gt; Void)
  
  // extract just the body of the web page
  func extractText(from html: String, completion: (Result&amp;lt;String, AnalyserError&amp;gt;) -&amp;gt; Void)
  
  // extract the title
  func extractTitle(from html: String, completion: (Result&amp;lt;String, AnalyserError&amp;gt;) -&amp;gt; Void)
  
  // analyse the text and return the tags we inferred
  func inferTags(from text: String, completion: ([Tag]) -&amp;gt; Void)
  
  // try to extract image meta tag
  func extractImage(from url: String, completion: @escaping (Result&amp;lt;String, AnalyserError&amp;gt;) -&amp;gt; Void)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of the methods in this protocol make use of trailing closures, and some of them are marked as &lt;code&gt;@escaping&lt;/code&gt;, meaning there will be code that holds on to the closure after execution has returned from the method. This is a tell-tale sign for asynchronous code.&lt;/p&gt;
&lt;p&gt;So far, so good. Now let’s take a look at the code that controls the overall process. Remember, we first need to download the text that makes up the web page, then extract all the information, run our natural language processing, etc.&lt;/p&gt;
&lt;p&gt;Are you ready for this? It doesn’t look nice…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension ArticleAnalyser {
  func process(url: String, completion: @escaping (Article) -&amp;gt; Void) {
    self.fetchArticle(from: url) { result in
      switch result {
      case .failure(let error):
        print(error.localizedDescription)
      case .success(let html):
        self.extractTitle(from: html) { result in
          switch result {
          case .failure(let error):
            print(error.localizedDescription)
          case .success(let title):
            self.extractText(from: html) { result in
              switch result {
              case .failure(let error):
                print(error.localizedDescription)
              case .success(let text):
                self.extractImage(from: url) { result in
                  switch result {
                  case .failure(let error):
                    print(error.localizedDescription)
                  case .success(let imageUrl):
                    self.inferTags(from: text) { tags in
                      let article = Article(url: url, title: title, tags: tags, imageUrlString: imageUrl)
                      completion(article)
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think we can all agree that this is really hard to read - in fact, when I showed this to a colleague of mine, he said “I didn&apos;t know there were that many closing curly braces in the whole galaxy!”&lt;/p&gt;
&lt;p&gt;On top of that, the error handling isn’t even very good.&lt;/p&gt;
&lt;h2&gt;Async/await&lt;/h2&gt;
&lt;p&gt;Let’s see how we can improve this code by using async/await.&lt;/p&gt;
&lt;p&gt;The first step is to get rid of all the callbacks. Instead, we will tell the compiler that the result of these functions will be delivered asynchronously, and that they might potentially throw errors. And finally, we specify the return type.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protocol AsyncArticleAnalyser {
  // fetch the article and return the entire HTML text
  func fetchArticle(from url: String) async throws -&amp;gt; String
  
  // extract just the body of the web page
  func extractText(from html: String) async throws -&amp;gt; String
  
  // extract the title
  func extractTitle(from html: String) async throws -&amp;gt; String
  
  // analyse the text and return the tags we inferred
  func inferTags(from text: String) async -&amp;gt; [Tag]
  
  // try to extract image meta tag
  func extractImage(from url: String) async throws -&amp;gt; String
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This already looks a lot simpler. The contrast is even more striking when we do a before/after comparison of one of the methods:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// before
func fetchArticle(from url: String, completion: @escaping (Result&amp;lt;String, AnalyserError&amp;gt;) -&amp;gt; Void)

// after
func fetchArticle(from url: String) async throws -&amp;gt; String
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new method signature looks much cleaner, and it is easier to see what the input parameters are and what’s the return type.&lt;/p&gt;
&lt;p&gt;In the next step, let’s update the code that drives the whole process of downloading and analysing the web site.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension AsyncArticleAnalyser {
  func process(url: String) async throws -&amp;gt; Article {
    let htmlText = try await fetchArticle(from: url)
    let text = try await extractText(from: htmlText)
    let title = try await extractTitle(from: htmlText)
    let imageUrl = try await extractImage(from: url)
    let tags = await inferTags(from: text)
    
    return Article(url: url,
                   title: title,
                   tags: tags,
                   imageUrlString: imageUrl)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Getting rid of the callback handlers allows us to write this code in a much more concise way. First of all, we can mark the &lt;code&gt;process&lt;/code&gt; method as asynchronous. Then, we’ll tell the compiler that the method might throw an error, and that the return type is an &lt;code&gt;Article&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To call a method that is marked as &lt;code&gt;async&lt;/code&gt;, we need to prefix the call with the &lt;code&gt;await&lt;/code&gt; keyword. This tells the compiler that it needs to wait for the call to return. And the nice thing is - Xcode will tell us if the &lt;code&gt;await&lt;/code&gt; keyword is missing and will even offer to fix the code for us.&lt;/p&gt;
&lt;p&gt;And just like that, we’ve eliminated the deeply nested callback structure we had to use in the previous, callback-driven implementation. The code has become much cleaner and concise, and it reads like a linear program.&lt;/p&gt;
&lt;h2&gt;Refactoring the callback-based code&lt;/h2&gt;
&lt;p&gt;Of course, we need to convert the existing code for fetching the web page and extracting all the metadata as well.&lt;/p&gt;
&lt;p&gt;Let’s start by adopting the protocol we defined earlier.&lt;/p&gt;
&lt;p&gt;To get an impression of how little effort is required for converting existing call-back based code to async/wait, let’s paste the existing code, and then change it in place.&lt;/p&gt;
&lt;p&gt;Here is the code for fetching the web page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func fetchArticle(from url: String, completion: @escaping (Result&amp;lt;String, AnalyserError&amp;gt;) -&amp;gt; Void) {
  guard let url =  URL(string: url) else {
    completion(.failure(.badURL))
    return
  }
  
  URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in
    guard let localUrl = localUrl else {
      completion(.failure(.downloadFailed))
      return
    }
    if let htmlText = try? String(contentsOf: localUrl) {
      completion(.success(htmlText))
    }
  }.resume()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing you’ll notice when we paste this into our new method is that we don’t have a &lt;code&gt;completion&lt;/code&gt; handler any more that we can use to communicate with our caller. There are various ways to deal with this, and for the initial &lt;code&gt;guard&lt;/code&gt; statement, we can just throw an error if the URL is invalid:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func fetchArticle(from url: String) async throws -&amp;gt; String {
  guard let url =  URL(string: url) else {
    throw AnalyserError.badURL
  }

  // ... more code to come

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To wrap existing code, the async/await proposal provides us with a few helper functions, such as &lt;code&gt;withUnsafeThrowingContinuation&lt;/code&gt;. These helper functions take a closure with a &lt;code&gt;continuation&lt;/code&gt; parameter, which you can call when your code completes.&lt;/p&gt;
&lt;p&gt;Here is how it works:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func fetchArticle(from url: String) async throws -&amp;gt; String {
  guard let url =  URL(string: url) else {
    throw AnalyserError.badURL
  }
  
  return try await withUnsafeThrowingContinuation { continuation in
    URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in
      guard let localUrl = localUrl else {
        continuation.resume(throwing: AnalyserError.badURL)
        return
      }
      if let htmlText = try? String(contentsOf: localUrl) {
        continuation.resume(returning: htmlText)
      }
    }
    .resume()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;First, we wrap our existing code in a call to &lt;code&gt;withUnsafeThrowingContinuation&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then, whenever you want to communicate back to the caller, you call the &lt;code&gt;continuation&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnsafeThrowingContinuation&lt;/code&gt; has several overloaded &lt;code&gt;resume&lt;/code&gt; methods that allow you to return a &lt;code&gt;Result&lt;/code&gt; type, a normal return value, or to even throw an error.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This allows us to throw an error in case the download failed. And if the download succeeded, we can return the text of the web page.&lt;/p&gt;
&lt;p&gt;And these are the only changes you need to make to turn your code from using callbacks to using &lt;code&gt;async/await&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You now have all the techniques to convert the remaining functions. I’ll leave this as an exercise for you, but if you’re stuck, check out the &lt;a href=&quot;https://github.com/peterfriese/Swift-Async-Await-Experiments/tree/blog/article_analyser/final&quot;&gt;&lt;em&gt;final&lt;/em&gt;&lt;/a&gt; branch in the project’s repo.&lt;/p&gt;
&lt;h2&gt;Connecting the UI&lt;/h2&gt;
&lt;p&gt;Finally, let’s connect the UI to our new code. In this sample app, we use &lt;code&gt;ArticlesViewModel &lt;/code&gt; as our source of truth - it has a published property named &lt;code&gt;articles&lt;/code&gt; that contains a list of all the articles that the main list view will display.&lt;/p&gt;
&lt;p&gt;Currently, when the user adds a new URL to be analysed, the UI will call &lt;code&gt;addNewArticle&lt;/code&gt;, providing the &lt;code&gt;url&lt;/code&gt; as an input parameter. This method will then call &lt;code&gt;performAddNewArticle&lt;/code&gt;, which uses the callback-based &lt;code&gt;AnalyserService&lt;/code&gt; to download and analyse the article and add it to the &lt;code&gt;articles&lt;/code&gt; array.&lt;/p&gt;
&lt;p&gt;Instead, we want to use our new &lt;code&gt;AsyncArticleAnalyserService &lt;/code&gt;. We can start by making a copy of &lt;code&gt;performAddNewArticle&lt;/code&gt;, and make some adjustments so it works with our new asynchronous code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func performAddNewArticleAsync(from url: String) {
  DispatchQueue.main.async {
    self.isFetching = true
  }

  do {
    let article = try await asyncAnalyserService.process(url: url)
    DispatchQueue.main.async {
      self.articles.append(article)
    }
  }
  catch {
    print(error.localizedDescription)
  }
  
  DispatchQueue.main.async {
    self.isFetching = false
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We already know that we need to prefix calls to asynchronous functions with &lt;code&gt;async&lt;/code&gt;, and since the method can throw, we also need to wrap the call in a  &lt;code&gt;do / try / catch&lt;/code&gt; block. We can also get rid of the trailing closure and assign the result to a local variable, named &lt;code&gt;article&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But Xcode doesn’t seem to be happy and shows an error: &lt;code&gt;&apos;async&apos; in a function that does not support concurrency&lt;/code&gt;.  This is because we’re trying to call from a place that isn’t contained in an asynchronous context. Instead of establishing an asynchronous context ourselves, we can follow Xcode’s suggestion and mark &lt;code&gt;performAddNewArticleAsync&lt;/code&gt; using &lt;code&gt;@asyncHandler&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@asyncHandler func performAddNewArticleAsync(from url: String) {
  DispatchQueue.main.async {
    self.isFetching = true
  }

  do {
    let article = try await asyncAnalyserService.process(url: url)
    DispatchQueue.main.async {
      self.articles.append(article)
    }
  }
  catch {
    print(error.localizedDescription)
  }
  
  DispatchQueue.main.async {
    self.isFetching = false
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And with that, we can now run the app again and see our shiny new &lt;code&gt;async/await&lt;/code&gt; implementation in action!&lt;/p&gt;
&lt;h2&gt;Closure ;-)&lt;/h2&gt;
&lt;p&gt;I think  &lt;em&gt;async/await&lt;/em&gt; is a wonderful addition to the Swift language. Many other languages, such as C#, JavaScript, or TypeScript have similar language features, and adding &lt;em&gt;async/await&lt;/em&gt; to Swift will make it easier for people coming from these languages.&lt;/p&gt;
&lt;p&gt;Using async/await will make your code more readable, and refactoring existing code is relatively straightforward, as we’ve just seen.&lt;/p&gt;
&lt;p&gt;One of the strongest arguments for using async/await in your own code is that this allows you to explicitly express that certain parts of your code run asynchronously. This is a strong signal for your users and helps them better understand which parts of your API are asynchronous and might only return after a short pause. Even better - the Swift compiler will now issue warnings and errors if developers are trying to call your API in a non-asynchronous way. This is something that just wasn’t possible with callbacks and trailing closures.&lt;/p&gt;
&lt;p&gt;I also think this is great news for anyone who builds APIs, and will hopefully reduce the amount of time they spend on support. Fun fact: “The Firebase APIs are asynchronous…” is one my most frequently used sentences when answering questions on StackOverflow.&lt;/p&gt;
&lt;p&gt;I can’t wait for this feature to ship in one of the next versions of Swift. What about you? Reach out to me on Twitter or on the &lt;a href=&quot;https://github.com/peterfriese/Swift-Async-Await-Experiments/discussions&quot;&gt;discussions forum&lt;/a&gt; of the &lt;a href=&quot;https://github.com/peterfriese/Swift-Async-Await-Experiments&quot;&gt;repository&lt;/a&gt; for this article.&lt;/p&gt;
</content:encoded></item><item><title>Firebase Authentication: Migrating User Data</title><link>https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part4/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part4/</guid><description>Learn how to migrate data using Cloud Firestore</description><pubDate>Mon, 09 Nov 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;&lt;/p&gt;
&lt;p&gt;This article is part of a series of articles that explores building a real-world application using SwiftUI, Firebase, and a couple of other technologies.&lt;/p&gt;
&lt;p&gt;Here is an overview of the series and what we&apos;re building:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;&lt;a href=&quot;../replicating-reminder-swiftui-firebase-part1/&quot;&gt;part 1&lt;/a&gt;&lt;/strong&gt; of the series, we focussed on building the UI with SwiftUI, using a simple data model.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;&lt;a href=&quot;../replicating-reminder-swiftui-firebase-part2/&quot;&gt;part 2&lt;/a&gt;&lt;/strong&gt;, we connected the application to Firebase, and synchronized the user&apos;s tasks with Cloud Firestore.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;&lt;a href=&quot;../replicating-reminder-swiftui-firebase-part3/&quot;&gt;part 3&lt;/a&gt;&lt;/strong&gt; we implemented &lt;em&gt;Sign in with Apple&lt;/em&gt;, allowing users to sign in from multiple devices to access their data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the end of part 3, we briefly touched on an issue that users who sign in to the app on a secondary device might face:&lt;/p&gt;
&lt;p&gt;You might think that using an app on two different devices should be an entirely reasonable thing to do. Still, when trying to sign-in on the second device, Firebase will return an error message saying&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This credential is already associated with a different user account.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Does this mean you cannot sign in to more than one device when using Firebase?&lt;/p&gt;
&lt;p&gt;Read on to learn more about what this actually means and how we can gracefully recover the situation.&lt;/p&gt;
&lt;h2&gt;Why does this happen?&lt;/h2&gt;
&lt;p&gt;To better understand the issue, let’s first set the scene and look at how a user might end up in a situation in which Firebase refuses to perform account linking:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice launches our app, &lt;em&gt;MakeItSo&lt;/em&gt;, on her phone.&lt;/li&gt;
&lt;li&gt;She starts using the app without signing in and adds a couple of tasks.&lt;/li&gt;
&lt;li&gt;As she hasn’t signed in yet, all her data is connected to the anonymous user account the app created when Alice started using the app. Whenever she adds a new task, the app sets the &lt;code&gt;userId&lt;/code&gt; property of that task to the &lt;code&gt;uid&lt;/code&gt; of Alice’s anonymous user account.&lt;/li&gt;
&lt;li&gt;After using the app for a while, Alice is pleased with the app. She decides to sign up to take advantage of the extra features of the app, such as sharing data between multiple devices.&lt;/li&gt;
&lt;li&gt;She discovers the Sign in with Apple button and signs in using her Apple ID.&lt;/li&gt;
&lt;li&gt;The app receives a credential object from Sign in with Apple. The app then calls into Firebase Authentication to link Alice’s anonymous user with these credentials.&lt;/li&gt;
&lt;li&gt;Firebase Authentication upgrades the anonymous account into a permanent account, connecting it with Alice&apos;s Apple ID.&lt;/li&gt;
&lt;li&gt;A while later, Alice decides to use the app on her iPad as well.&lt;/li&gt;
&lt;li&gt;She installs the app on her iPad.&lt;/li&gt;
&lt;li&gt;Upon launching the app, she sees an empty screen.&lt;/li&gt;
&lt;li&gt;Assuming that it might take a while for the data to sync from her iPhone to her iPad, she starts adding new tasks on her iPad.&lt;/li&gt;
&lt;li&gt;What Alice doesn&apos;t know: as she is now using a different device, she is represented by a &lt;em&gt;new&lt;/em&gt; anonymous user ID on her iPad.&lt;/li&gt;
&lt;li&gt;As the new tasks she enters on her iPad don&apos;t show up on her iPhone, she gets suspicious and realises she needs to sign in to the app on her iPad as well.&lt;/li&gt;
&lt;li&gt;She finds the Sign in with Apple button on her iPad and signs in.&lt;/li&gt;
&lt;li&gt;The app receives Sign in with Apple credentials representing Alice.&lt;/li&gt;
&lt;li&gt;The app tries to link the anonymous account with these credentials.&lt;/li&gt;
&lt;li&gt;At this moment, Firebase Authentication returns an error, stating that the credential has already been linked to another Firebase user account (the one in step 7)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It helps to keep in mind that, at its core, a Firebase user is just a thin wrapper around an ID token. Any authentication provider you might use in your app essentially just helps you to exchange specific credentials for a Firebase-specific ID token.&lt;/p&gt;
&lt;p&gt;In their Firebase Summit 2020 session “&lt;a href=&quot;https://youtu.be/8JVmWtJLqNU&quot;&gt;Firebase Authentication: From fully managed to fully customizable&lt;/a&gt;”, my colleagues Sam and Malcolm dive into how the token exchange flow works in detail - I highly recommend watching this talk:&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://youtu.be/8JVmWtJLqNU&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;You can link a Firebase user to one or more authentication providers. Each of these providers can then provide a credential that &lt;em&gt;uniquely&lt;/em&gt; identifies the user in the context of this authentication provider. To uphold this identifying relationship, Firebase needs to ensure that the same credential cannot be used to identify a &lt;em&gt;different&lt;/em&gt; Firebase user.&lt;/p&gt;
&lt;p&gt;This is why you will receive an error message when you take a credential that has already been linked to a Firebase user and try to link it to another Firebase user.&lt;/p&gt;
&lt;h2&gt;How can we resolve this situation?&lt;/h2&gt;
&lt;p&gt;To implement a suitable solution, let’s consider the user’s situation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They have signed in on their primary device and started to add data to the app. The app has assigned the user’s ID to this data and has synced it with Cloud Firestore.&lt;/li&gt;
&lt;li&gt;When the user signed in, the app linked their user account with the user’s Apple ID credentials.&lt;/li&gt;
&lt;li&gt;The user then launched the app on a secondary device and might have added some data to the app on this device as well. They then realised they need to sign in on this secondary device to ensure the app can sync their data between all of their devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;We can assume that the user wants to merge all data they entered on the second device with any data they already entered on the first device.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For our implementation, this means the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We will first try to link the user’s Apple ID credentials to the current anonymous account.&lt;/li&gt;
&lt;li&gt;If this fails because the Apple ID credentials have already been linked to another Firebase user account, we will sign in to that user instead (keep in mind, this is the Firebase user we created on the user’s first device).&lt;/li&gt;
&lt;li&gt;We will then update any data the user has created on the second device, and make sure it gets assigned to the first user account.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To perform this data migration, we’ve got two main options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Perform the migration on-device.&lt;/strong&gt; This requires that the user is signed in to both the anonymous user account and the new, permanent account (by instantiating a separate &lt;a href=&quot;https://firebase.google.com/docs/reference/swift/firebasecore/api/reference/Classes/FirebaseApp&quot;&gt;&lt;code&gt;FirebaseApp&lt;/code&gt;&lt;/a&gt; instance for each of them). We can then iterate over all tasks that are owned by the anonymous user and set their &lt;code&gt;userId&lt;/code&gt; attribute to the ID of the permanent account. Firebase will then synchronise the updates to the backend, and all tasks will appear on all devices on which the user is signed in with the permanent account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Perform the migration in the backend / in the cloud.&lt;/strong&gt; This approach requires a Cloud Function that queries all tasks that belong to the anonymous user and updates their &lt;code&gt;userId&lt;/code&gt; attribute to the ID of the permanent account.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both options are viable, but for this article, I decided to implement the second option to demonstrate how to make use of Cloud Functions.&lt;/p&gt;
&lt;h2&gt;Migrating data in the cloud&lt;/h2&gt;
&lt;p&gt;To perform the data migration, our Cloud Function needs to update all documents that belong to the first (anonymous) user, and assign them to the second (permanent) user.&lt;/p&gt;
&lt;p&gt;Since it runs in a trusted environment, the function can make use of Firebase&apos;s Admin SDK and access all documents in our project&apos;s Cloud Firestore instance, independent of any &lt;a href=&quot;https://firebase.google.com/docs/firestore/security/get-started&quot;&gt;Security Rules&lt;/a&gt; we&apos;ve set up. If this sounds surprising to you, keep in mind that Security Rules are a mechanism that Firebase uses to protect data from being accessed by an untrusted client. Code running on Cloud Functions is considered secure; hence Security Rules don’t apply.&lt;/p&gt;
&lt;p&gt;The client app knows both the anonymous user and the permanent user account at the time of calling the Cloud Function. So can we just send the user IDs to the Cloud Function?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No&lt;/strong&gt; - that would be rather unsafe and open an attack vector: a malicious actor might be able to guess the user ID of a real user and call the Cloud Function with this user ID. This would put them in a position to either steal that user&apos;s data (by migrating it to an account the malicious actor has control over) or inject bogus data into the user&apos;s account.&lt;/p&gt;
&lt;p&gt;There are several measures we can put in place to eliminate this attack vector. The key facts we need to establish are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Both ID tokens are indeed valid, and represent user accounts of our application.&lt;/li&gt;
&lt;li&gt;The first account (the one we&apos;re migrating &lt;strong&gt;from&lt;/strong&gt;) is an anonymous user.&lt;/li&gt;
&lt;li&gt;The second account is &lt;strong&gt;not&lt;/strong&gt; an anonymous user account.&lt;/li&gt;
&lt;li&gt;In addition, we can put a check in place to ensure the second account was signed into recently (i.e. within the past few seconds).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&apos;s start by looking at how we can establish that the ID tokens the function receives are valid. Instead of just sending the plain user ID (which can easily be spoofed), it is much safer to use JWTs (JSON Web Tokens) as a tamper-proof way to communicate user IDs. JWTs are cryptographically signed, so it is easy to verify their integrity.&lt;/p&gt;
&lt;p&gt;Here is a JWT for an anonymous user in both encrypted and decoded form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxOTI5ZmY0NWM2MDllYzRjNDhlYmVmMGZiMTM5MmMzOTEzMmQ5YTEiLCJ0eXAiOiJKV1QifQ.eyJwcm92aWRlcl9pZCI6ImFub255bW91cyIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9wZXRlcmZyaWVzZS1tYWtlaXRzby1zYW5kYm94IiwiYXVkIjoicGV0ZXJmcmllc2UtbWFrZWl0c28tc2FuZGJveCIsImF1dGhfdGltZSI6MTYwNDMzNDgzNiwidXNlcl9pZCI6Impobk1wRWNsaExTSFNGdW9SUnN4WUtLa3AwdDIiLCJzdWIiOiJqaG5NcEVjbGhMU0hTRnVvUlJzeFlLS2twMHQyIiwiaWF0IjoxNjA0MzM0ODM2LCJleHAiOjE2MDQzMzg0MzYsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnt9LCJzaWduX2luX3Byb3ZpZGVyIjoiYW5vbnltb3VzIn19.iMSETKOeJO5vAui35fAz6h7izVJNNXLh3Q3lpx_ACw24OwFWFXsBcJAzhnjQB4D699_Nn6hoI1lupYERNzBL2VUzmdvNeqFEE16VRj8IFGih857nponVOWKSa4OpGwSnklDLHfzHhZ7wKuoozh5cAEp-oz10cHjztJiMuXMrqUPTTboGf7V7E6csAVgaaEoA990GNNBZuuRnihohKYu8-bV3Lt8DhtRaMhA4C-YXdImSha1WVtuZR9_quqAuULyFp4V8rWnJkUz9jOwv3jKVk3sf3Svv3jU5_RnLcILN12DqGHGKg1J5DxjrgWH3podZ2tOQb3j4cvzXAW9ruXQ3Jw
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;provider_id&quot;: &quot;anonymous&quot;,
  &quot;iss&quot;: &quot;https://securetoken.google.com/&amp;lt;your-project-id&amp;gt;&quot;,
  &quot;aud&quot;: &quot;&amp;lt;your-project-id&amp;gt;&quot;,
  &quot;auth_time&quot;: 1604334836,
  &quot;user_id&quot;: &quot;jhnMpEclhLSHSFuoRRsxYKKkp0t2&quot;,
  &quot;sub&quot;: &quot;jhnMpEclhLSHSFuoRRsxYKKkp0t2&quot;,
  &quot;iat&quot;: 1604334836,
  &quot;exp&quot;: 1604338436,
  &quot;firebase&quot;: {
    &quot;identities&quot;: {},
    &quot;sign_in_provider&quot;: &quot;anonymous&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, Firebase-issued ID tokens are specific to your Firebase project: the project ID is part of the issuer (in the &lt;code&gt;iss&lt;/code&gt; attribute), and the audience (in the &lt;code&gt;aud&lt;/code&gt; attribute).&lt;/p&gt;
&lt;p&gt;You can retrieve the ID token for a Firebase user by calling &lt;code&gt;user.getIDToken()&lt;/code&gt;. Just like most Firebase API calls, this is an asynchronous call which returns an optional result (the token string) and an optional error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;currentUser.getIDToken { (token, error) in
  if let idToken = token {
    // use the ID token
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ID token is signed with the service account of your Firebase project, so we can verify its integrity in our Cloud Function, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try {
  const decodedToken = await admin.auth().verifyIdToken(idToken);
} catch (error) {
  logger.error(`Error when trying to verify ID token. Error: ${error}`);
  return { error };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we&apos;ll retrieve the ID token of the anonymous user on the client, ready to be sent to the Cloud Function that will migrate this anonymous user&apos;s data.&lt;/p&gt;
&lt;p&gt;Now, we could technically do the same for the ID token of the Sign in with Apple account - however, there is no need to do so, as we&apos;re going to use an HTTPS Callable Cloud Function, which has the following advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can easily call the function from our client app.&lt;/li&gt;
&lt;li&gt;Requests to HTTPS Callable Cloud Functions automatically include Firebase Authentication tokens for the current user. These are verified for us by Firebase, so we don&apos;t have to perform any additional verification.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that we&apos;ve established that both the anonymous user&apos;s and the permanent user&apos;s ID token are valid, we should check that the first account is an anonymous account, and the second account is not an anonymous account.&lt;/p&gt;
&lt;p&gt;Firebase&apos;s client SDKs make it easy to detect whether a given user object represents an anonymous user: we can just call &lt;code&gt;user.isAnonymous()&lt;/code&gt;. The Firebase Admin SDK doesn&apos;t have such a convenience API. Instead, we need to inspect the ID token and check if the authentication provider is &lt;code&gt;anonymous&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function isAnonymous(idToken: admin.auth.DecodedIdToken) {
  return idToken.firebase.sign_in_provider === &apos;anonymous&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a final security measure, we&apos;ll also want to make sure the user has signed in to the permanent account very recently (let&apos;s say within the past minute or so). Fortunately, the ID token of a signed-in user contains an &lt;code&gt;auth_time&lt;/code&gt; field that tells us when the user signed in. Here is how we can check if this took place within the past five minutes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const gracePeriod = 5 * 60 * 1000;
const authTime = permanentAccountIdToken.auth_time * 1000;
const timeSinceSignIn = Date.now() - authTime;

if (timeSinceSignIn &amp;gt; gracePeriod) {
  throw new functions.https.HttpsError(
    &apos;invalid-argument&apos;,
    `Sign in must be within the past ${gracePeriod} miliseconds`,
    permanentAccountIdToken,
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all of those bits and pieces in place, we can now go ahead and implement the skeleton for the Cloud Function and call it from our client app:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as functions from &apos;firebase-functions&apos;;
import * as admin from &apos;firebase-admin&apos;;

let db: FirebaseFirestore.Firestore;
const logger = functions.logger;
const gracePeriod = 5 * 60 * 1000;
let initialized = false;

function initialize() {
  if (initialized === true) return;
  initialized = true;

  logger.log(`Starting up MakeItSo Cloud Functions`);
  admin.initializeApp();
  db = admin.firestore();
}

function isAnonymous(idToken: admin.auth.DecodedIdToken) {
  return idToken.firebase.sign_in_provider === &apos;anonymous&apos;;
}

async function verifyAnonymousUserIdToken(anonymousIdToken: string) {
  logger.log(`Verifying anonymous ID token ${anonymousIdToken}`);
  const verifiedAnonymousIdToken = await admin.auth().verifyIdToken(anonymousIdToken);

  if (!isAnonymous(verifiedAnonymousIdToken)) {
    throw new functions.https.HttpsError(
      &apos;invalid-argument&apos;,
      &apos;ID token must be anonymous&apos;,
      verifiedAnonymousIdToken,
    );
  }
  return verifiedAnonymousIdToken;
}

async function verifyPermanentUserIdToken(permanentAccountIdToken: admin.auth.DecodedIdToken) {
  logger.log(`Verifying permanent ID token ${permanentAccountIdToken}`);

  if (isAnonymous(permanentAccountIdToken)) {
    //(4)
    throw new functions.https.HttpsError(
      &apos;invalid-argument&apos;,
      &apos;ID token must be non-anonymous&apos;,
      permanentAccountIdToken,
    );
  }

  const authTime = permanentAccountIdToken.auth_time * 1000;
  const timeSinceSignIn = Date.now() - authTime;

  if (timeSinceSignIn &amp;gt; gracePeriod) {
    //(5)
    throw new functions.https.HttpsError(
      &apos;invalid-argument&apos;,
      `This operation requires a recent sign-in.`,
      permanentAccountIdToken,
    );
  }

  return permanentAccountIdToken;
}

export const migrateTasks = functions.https.onCall(async (data, context) =&amp;gt; {
  initialize();

  if (!context.auth) {
    //(1)
    throw new functions.https.HttpsError(
      &apos;failed-precondition&apos;,
      &apos;The function must be called while authenticated.&apos;,
    );
  } else {
    logger.log(&apos;Received data: %j&apos;, data);

    const verifiedAnonymousIdToken = await verifyAnonymousUserIdToken(data.idToken); //(2)
    const permanentAccountIdToken = await verifyPermanentUserIdToken(context.auth?.token); //(3)
    return performMigration(verifiedAnonymousIdToken, permanentAccountIdToken);
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, there is some setup and initialisation code. &lt;code&gt;migrateTasks&lt;/code&gt; is the function that will be exported and callable by our client. As you can see, we first verify that the call actually contains an auth token (1). If so, we extract the &lt;code&gt;idToken&lt;/code&gt; attribute from the data parameter (2) and verify the ID token it contains. We specifically check (in &lt;code&gt;verifyAnonymousUserIdToken&lt;/code&gt;) that the token is valid and that it represents an anonymous user. This is to prevent malicious actors from trying to steal data from non-anonymous accounts.&lt;/p&gt;
&lt;p&gt;In the next step, we verify the ID token of the permanent user as well: specifically, we check if the token represents a non-anonymous account (4), and whether the user signed in within the last minute (5).&lt;/p&gt;
&lt;p&gt;If all of this was successful, we call &lt;code&gt;performMigration&lt;/code&gt; to perform the actual data migration.&lt;/p&gt;
&lt;p&gt;Once this function is deployed to Cloud Functions, we can call it from the client app using the following snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func migrateTasks(from idToken: String) {
    let parameters = [&quot;idToken&quot;: idToken] //(1)
    functions.httpsCallable(&quot;migrateTasks&quot;).call(parameters) { (result, error) in //(2)
      if let error = error as NSError? {
        print(&quot;Error: \(error.localizedDescription)&quot;)
      }
      print(&quot;Function result: \(result?.data ?? &quot;(empty)&quot;)&quot;)
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To pass parameters, we need to construct a dictionary (1), and then invoke &lt;code&gt;httpsCallable&lt;/code&gt; with the name of the function. By now, you should be familiar with the fact that most Firebase API calls are asynchronous. HTTPS Callable Cloud Functions are no different: once the function completes, the trailing closure will be called (2), and we can check the result of the operation.&lt;/p&gt;
&lt;h2&gt;Updating multiple Firestore documents at once&lt;/h2&gt;
&lt;p&gt;The data migration itself is a two-step process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fetch all documents that are owned by the anonymous user&lt;/li&gt;
&lt;li&gt;Update their &lt;code&gt;userId&lt;/code&gt; attribute with the user ID of the permanent account&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Database operations like this should be executed atomically, to ensure that either all operations succeed, or none of them are applied. It would be a rather nasty surprise for the user to find out only some of their tasks were migrated!&lt;/p&gt;
&lt;p&gt;Cloud Firestore provides two mechanisms to achieve this: &lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/transactions&quot;&gt;transactions and batched writes&lt;/a&gt;. A transaction is a set of read and write operations on one or more documents. A batched write is a set of write operations on one or more documents.&lt;/p&gt;
&lt;p&gt;In our use case, we want to first read a bunch of documents (i.e. fetch all documents that are owned by the anonymous user), and then update all of them in one fell swoop, so a transaction is the right choice.&lt;/p&gt;
&lt;p&gt;When using transactions in Cloud Firestore, keep in mind that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All read operations must precede any write operations.&lt;/li&gt;
&lt;li&gt;A function calling a transaction might be run more than once in case a concurrent edit affects one or more documents that are part of the transaction.&lt;/li&gt;
&lt;li&gt;Transactions will fail if the client is offline (as we&apos;re calling from a Cloud Function, this doesn&apos;t apply).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the code that performs the data migration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function performMigration(
  anonymousIdToken: admin.auth.DecodedIdToken,
  permanentAccountIdToken: admin.auth.DecodedIdToken,
) {
  const anonymousUserId = anonymousIdToken.uid;
  const permamentUserId = permanentAccountIdToken.uid;

  logger.log(
    `Migrating tasks from previous userID [${anonymousUserId}] to new userID [${permamentUserId}].`,
  );

  return db.runTransaction(async (transaction) =&amp;gt; {
    //(1)
    const tasksToMigrateQuery = db.collection(&apos;tasks&apos;).where(&apos;userId&apos;, &apos;==&apos;, anonymousUserId);
    const tasksToMigrate = await transaction.get(tasksToMigrateQuery); //(2)

    if (tasksToMigrate.empty) {
      //(3)
      logger.log(`Previous user [${anonymousUserId}] didn\&apos;t have any documents, nothing to do.`);
    } else {
      logger.log(
        `Migrating ${tasksToMigrate.size} tasks from userID [${anonymousUserId}] to new userId [${permamentUserId}]`,
      );
      tasksToMigrate.forEach((snapshot) =&amp;gt; {
        //(4)
        transaction.update(snapshot.ref, { userId: permamentUserId }); //(5)
      });
    }
    return {
      //(6)
      updatedDocCount: tasksToMigrate.size,
      anonymousUserId: anonymousUserId,
      permamentUserId: permamentUserId,
    };
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As discussed, we use a transaction (1) to wrap all data access code. Inside the transaction, the first step is to fetch all documents that belong to the anonymous user (2). The result set might be empty, in which case we’ll just log a message.&lt;/p&gt;
&lt;p&gt;If the result set is not empty, we will iterate over all documents (4), and update their &lt;code&gt;userId&lt;/code&gt; attribute to the ID of the user’s permanent account (5).&lt;/p&gt;
&lt;p&gt;Finally, we return a dictionary with some details about the data migration. The client app can use this information to let the user know how many tasks were migrated.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Firebase Authentication &lt;em&gt;can&lt;/em&gt; be really simple for straightforward use cases, but that simplicity does not prevent you from building more complex solutions when needed. By introducing some server-side code in a Cloud Function, you can implement very flexible and powerful authentication systems.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The header image is based on &lt;a href=&quot;https://thenounproject.com/search/?creator=5235012&amp;amp;q=cloud&amp;amp;i=3182749&quot;&gt;Cloud&lt;/a&gt; by &lt;a href=&quot;https://thenounproject.com/madadesign/&quot;&gt;Gajah Mada Studio&lt;/a&gt; from the &lt;a href=&quot;https://thenounproject.com/&quot;&gt;Noun Project&lt;/a&gt; and &lt;a href=&quot;https://thenounproject.com/search/?q=forklift&amp;amp;i=3339592&quot;&gt;Forklift&lt;/a&gt; by &lt;a href=&quot;https://thenounproject.com/victorulerz/&quot;&gt;Victoruler&lt;/a&gt; from the &lt;a href=&quot;https://thenounproject.com/&quot;&gt;Noun Project&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Firebase and the new SwiftUI 2 Application Life Cycle</title><link>https://peterfriese.dev/blog/2020/swiftui-new-app-lifecycle-firebase/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/swiftui-new-app-lifecycle-firebase/</guid><description>Learn how to initialise Firebase in your SwiftUI 2 application</description><pubDate>Mon, 19 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the longest time, iOS developers have used &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiapplicationdelegate&quot;&gt;&lt;code&gt;UIApplicationDelegate&lt;/code&gt;&lt;/a&gt; to handle application startup and other lifecycle events in their apps. At WWDC 2020, Apple made some significant changes to how apps participate in the application lifecycle.&lt;/p&gt;
&lt;p&gt;In this article, we&apos;re going to take a look at what this means for your Firebase apps. For a more in-depth look at the new application lifecycle, check out &lt;a href=&quot;https://peterfriese.dev/ultimate-guide-to-swiftui2-application-lifecycle/&quot;&gt;this other article&lt;/a&gt; I wrote recently.&lt;/p&gt;
&lt;p&gt;When you create a new iOS app in Xcode, you will notice there are no &lt;code&gt;AppDelegate&lt;/code&gt; or &lt;code&gt;SceneDelegate&lt;/code&gt; classes any more. Instead, the main entry point to your app will look similar to this snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

@main
struct SwiftUIAppLifeCycleApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This new approach falls in line with Apple&apos;s move towards more declarative APIs. To learn more about its advantages and the impacts it has on your apps that use features like Siri, Handoff, Spotlight, or User Activity Continuation, check out &lt;a href=&quot;https://peterfriese.dev/ultimate-guide-to-swiftui2-application-lifecycle/&quot;&gt;The Ultimate Guide to the SwiftUI 2 Application Life Cycle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you use Firebase in your iOS apps, there are two main strategies you can follow.&lt;/p&gt;
&lt;h2&gt;Continue using the UIKit App Delegate Life Cycle&lt;/h2&gt;
&lt;p&gt;As of this writing, Apple hasn&apos;t indicated that the UIKit App Delegate Life Cycle is deprecated. This is pretty good news and means you will be able to continue using the &lt;code&gt;AppDelegate&lt;/code&gt;-based approach for initialising your app and handling life cycle events.&lt;/p&gt;
&lt;p&gt;Here is a snippet from one of Firebase&apos;s &lt;a href=&quot;https://github.com/firebase/quickstart-ios/blob/master/firestore/FirestoreExample/AppDelegate.swift&quot;&gt;sample apps&lt;/a&gt;, to give you an impression of how that would look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&amp;gt; Bool {
    FirebaseApp.configure()
    return true
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s also worth noting that Xcode 12 still allows you to create new apps using the UIKit App Delegate Life Cycle so that you can use this approach even for new apps, should you care to do so.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Start using the SwiftUI App Life Cycle&lt;/h2&gt;
&lt;p&gt;The new app life cycle is a step into the direction of using more and more domain-specific APIs. It comes with several benefits, such as more fine-grained control over where you&apos;d like to &lt;a href=&quot;https://peterfriese.dev/ultimate-guide-to-swiftui2-application-lifecycle/&quot;&gt;handle deep links and user activity continuation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Firebase, just like many other third-party SDKs and Frameworks, needs to be initialised early in the application life cycle. When using the new app life cycle, the earliest possible point to participate in the life cycle is by adding an initialiser to your app&apos;s main entry point:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI
import Firebase

@main
struct SofiaApp: App {
  init() {
    FirebaseApp.configure()
  }

  var body: some Scene {
    WindowGroup {
      MainScreen()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach works well for Firebase SDKs such as Cloud Firestore and Crashlytics.&lt;/p&gt;
&lt;p&gt;For other Firebase SDKs, such as Firebase Cloud Messaging, this is not sufficient, as they use method swizzling to hook into the application life cycle. This mechanism allows frameworks to intercept calls to specific methods and handle them before passing the call on to your application.&lt;/p&gt;
&lt;p&gt;In this case, you need to use the &lt;code&gt;@UIApplicationDelegateAdaptor&lt;/code&gt; property wrapper to connect your app to an instance of &lt;code&gt;AppDelegate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is a template to get you started:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -&amp;gt; Bool {
    print(&quot;Colors application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.&quot;)
    FirebaseApp.configure()
    return true
  }
}

@main
struct ColorsApp: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you the following benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You will be able to continue using frameworks that use method swizzling to participate in the application&apos;s startup&lt;/li&gt;
&lt;li&gt;There&apos;s a good chance you can just copy and paste your existing &lt;code&gt;AppDelegate&lt;/code&gt; code&lt;/li&gt;
&lt;li&gt;You will be able to gradually migrate to the new app life cycle model (e.g. by &lt;a href=&quot;../ultimate-guide-to-swiftui2-application-lifecycle/&quot;&gt;handling deep links&lt;/a&gt; using the &lt;code&gt;onOpenUrl&lt;/code&gt; modifier)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And that&apos;s really it!&lt;/p&gt;
&lt;p&gt;I hope this article helped you to start using Firebase and Apple&apos;s new app life cycle in your projects. Let me know which Firebase SDKs you use in your apps and if this helped you to migrate your app to SwiftUI 2. If you run into any issues, feel free to file an issue against our &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk/issues&quot;&gt;GitHub issue tracker&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The header image is based on &lt;a href=&quot;https://thenounproject.com/kavya261990/collection/space/?i=3437783&quot;&gt;Rocket&lt;/a&gt; by &lt;a href=&quot;https://thenounproject.com/kavya261990/&quot;&gt;Icongeek26&lt;/a&gt; on the &lt;a href=&quot;https://thenounproject.com/&quot;&gt;Noun Project&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>The Ultimate Guide to the SwiftUI 2 Application Life Cycle</title><link>https://peterfriese.dev/blog/2020/ultimate-guide-to-swiftui2-application-lifecycle/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/ultimate-guide-to-swiftui2-application-lifecycle/</guid><description>Everything you need to know about the new application life cycle in SwiftUI 2</description><pubDate>Fri, 09 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;import demo from &apos;./ultimate-guide-to-swiftui2-application-lifecycle/demo.mp4&apos;;&lt;/p&gt;
&lt;p&gt;For the longest time, iOS developers have used &lt;code&gt;AppDelegate&lt;/code&gt;s as the main entry point for their applications. With the launch of SwiftUI2 at WWDC 2020, Apple has introduced a new application life cycle that (almost) completely does away with &lt;code&gt;AppDelegate&lt;/code&gt;, making way for a DSL-like approach.&lt;/p&gt;
&lt;p&gt;In this article, I will discuss why this change was introduced, and how you can make use of the new life cycle in new or existing apps.&lt;/p&gt;
&lt;h2&gt;Specifying the application entry point&lt;/h2&gt;
&lt;p&gt;One of the first questions that we need to answer is, how can we tell the compiler about the entry point to our application? &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0281-main-attribute.md&quot;&gt;SE-0281&lt;/a&gt; specifies how &lt;em&gt;Type-Based Program Entry Points&lt;/em&gt; work:&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
The Swift compiler will recognize a type annotated with the @main attribute as providing the entry
point for a program. Types marked with @main have a single implicit requirement: declaring a
static main() method.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;When creating a new SwiftUI app, the app&apos;s main class looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

@main
struct SwiftUIAppLifeCycleApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So where is the static &lt;code&gt;main()&lt;/code&gt; function that&apos;s mentioned in SE-0281?&lt;/p&gt;
&lt;p&gt;Well, it turns out that framework providers can (and should) provide a default implementation for their users&apos; convenience. Looking at the code snippet above, you will notice that &lt;code&gt;SwiftUIAppLifeCycleApp&lt;/code&gt; conforms to the &lt;code&gt;App&lt;/code&gt; protocol. Apple provides a protocol extension that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension App {

    /// Initializes and runs the app.
    ///
    /// If you precede your ``SwiftUI/App`` conformer&apos;s declaration with the
    /// [@main](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID626)
    /// attribute, the system calls the conformer&apos;s `main()` method to launch
    /// the app. SwiftUI provides a
    /// default implementation of the method that manages the launch process in
    /// a platform-appropriate way.
    public static func main()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there we have it - this protocol extension provides a default implementation that takes care of the application startup.&lt;/p&gt;
&lt;p&gt;Since the SwiftUI framework isn&apos;t open source, we can&apos;t see how Apple implemented this, but &lt;a href=&quot;https://github.com/apple/swift-argument-parser&quot;&gt;Swift Argument Parser&lt;/a&gt; is open source, and uses this approach as well. Check out the source code for &lt;code&gt;ParsableCommand&lt;/code&gt; to see how they use a protocol extension to provide a default implementation of the static &lt;code&gt;main&lt;/code&gt; function that serves as the program entry point:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension ParsableCommand {
...
  public static func main(_ arguments: [String]?) {
    do {
      var command = try parseAsRoot(arguments)
      try command.run()
    } catch {
      exit(withError: error)
    }
  }

  public static func main() {
    self.main(nil)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If all this sounds a bit complicated, the good news is you don&apos;t actually have to worry about it when creating a new SwiftUI application: just make sure to select &lt;em&gt;SwiftUI App&lt;/em&gt; in the &lt;em&gt;Life Cycle&lt;/em&gt; dropdown when creating your app, and you&apos;re done:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at some common scenarios.&lt;/p&gt;
&lt;h2&gt;Initialising resources / your favourite SDK or framework&lt;/h2&gt;
&lt;p&gt;Most applications need to perform a few steps at application startup: fetching some configuration values, connecting to a database, or initialising a framework or third-party SDK.&lt;/p&gt;
&lt;p&gt;Usually, you&apos;d do this in your &lt;code&gt;ApplicationDelegate&lt;/code&gt;s &lt;code&gt;application(_:didFinishLaunchingWithOptions:)&lt;/code&gt; method. As we no longer have an application delegate, we need to find other ways to initialise our application. Depending on your specific requirements, here are some strategies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement an initialiser on your main class (see the &lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID205&quot;&gt;docs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Set initial values for stored properties (see the &lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID206&quot;&gt;docs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Set default property values using a closure (see the &lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID232&quot;&gt;docs&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;@main
struct ColorsApp: App {
  init() {
    print(&quot;Colors application is starting up. App initialiser.&quot;)
  }

  var body: some Scene {
    WindowGroup {
      ContentView()
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If none of this meets your needs, you might need an AppDelegate after all. Read on until the end to learn how you can add one.&lt;/p&gt;
&lt;h2&gt;Handling your application&apos;s life cycle&lt;/h2&gt;
&lt;p&gt;It&apos;s sometimes useful to be able to know which state your application is in. For example, you might want to fetch new data as soon as your app becomes active, or flush any caches once your application becomes inactive and transitions into the background.&lt;/p&gt;
&lt;p&gt;Usually, you would implement &lt;code&gt;applicationDidBecomeActive&lt;/code&gt;, &lt;code&gt;applicationWillResignActive&lt;/code&gt;, or &lt;code&gt;applicationDidEnterBackground&lt;/code&gt; on your &lt;code&gt;ApplicationDelegate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Starting with iOS 14.0, Apple has provided a new API that allows for a more elegant and maintainable way of tracking an app&apos;s state: &amp;lt;code&amp;gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/scenephase&quot;&gt;ScenePhase&lt;/a&gt;&amp;lt;/code&amp;gt;. Your project can have multiple scenes, but chances are you&apos;ve got only one scene, represented by &amp;lt;code&amp;gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/windowgroup&quot;&gt;WindowGroup&lt;/a&gt;&amp;lt;/code&amp;gt;.&lt;/p&gt;
&lt;p&gt;SwiftUI tracks a scene&apos;s state in the environment, and you can make the current value accessible to your code by fetching it using the &lt;code&gt;@Environment&lt;/code&gt; property wrapper, and then using the &lt;code&gt;onChange(of:)&lt;/code&gt; modifier to listen to any changes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@main
struct SwiftUIAppLifeCycleApp: App {
  @Environment(\.scenePhase) var scenePhase

  var body: some Scene {
    WindowGroup {
      ContentView()
    }
    .onChange(of: scenePhase) { newScenePhase in
      switch newScenePhase {
      case .active:
        print(&quot;App is active&quot;)
      case .inactive:
        print(&quot;App is inactive&quot;)
      case .background:
        print(&quot;App is in background&quot;)
      @unknown default:
        print(&quot;Oh - interesting: I received an unexpected new value.&quot;)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is worth noting that you can read the phase from other locations in your app as well. When reading the phase at the top level of the app (like shown in the code snippet), you will get an aggregate of all the phases in your app. A value of &lt;code&gt;.inactive&lt;/code&gt; means that none of the scenes in your app is active. When reading the phase on a view, you will receive the value of the phase that contains the view. Keep in mind your app might contain other scenes that have other phase values at this time. For more details about scene phases, read Apple&apos;s &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/scenephase&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Handling deep links&lt;/h2&gt;
&lt;p&gt;Previously, when handling deep links, you&apos;d have to implement &lt;code&gt;application(_:open:options:)&lt;/code&gt;, and route the incoming URL to the most appropriate handler.&lt;/p&gt;
&lt;p&gt;This becomes a lot easier with the new app life cycle model. You can handle incoming URLs by attaching the &lt;code&gt;onOpenURL&lt;/code&gt; modifier to the top-most scene in your app:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@main
struct SwiftUIAppLifeCycleApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
        .onOpenURL { url in
          print(&quot;Received URL: \(url)&quot;)
        }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&apos;s really cool: you can install multiple URL handlers throughout your application - making deep linking a lot easier, as you can handle incoming links where it&apos;s most appropriate.&lt;/p&gt;
&lt;p&gt;If at all possible, you should use &lt;a href=&quot;https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content&quot;&gt;universal links&lt;/a&gt; (or &lt;a href=&quot;https://firebase.google.com/docs/dynamic-links&quot;&gt;Firebase Dynamic Links&lt;/a&gt;, which makes use of &lt;a href=&quot;https://firebase.google.com/docs/dynamic-links/operating-system-integrations&quot;&gt;universal links for iOS apps&lt;/a&gt;), as these use associated domains to create a connection between a website you own and your app - this will allow you to share data securely.&lt;/p&gt;
&lt;p&gt;However, you can still use &lt;a href=&quot;https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app&quot;&gt;custom URL schemes&lt;/a&gt; to link to content within your app.&lt;/p&gt;
&lt;p&gt;Either way, a simple way to trigger a deep link in your app is to use the following command on you development machine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;xcrun simctl openurl booted &amp;lt;your url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;Continuing user activities&lt;/h2&gt;
&lt;p&gt;If your app uses &lt;code&gt;NSUserActivity&lt;/code&gt; to &lt;a href=&quot;https://developer.apple.com/documentation/foundation/nsuseractivity&quot;&gt;integrate with&lt;/a&gt; Siri, Handoff, or Spotlight, you need to handle user activity continuation.&lt;/p&gt;
&lt;p&gt;Again, the new application life cycle model makes this easier by providing two modifiers that allow you to advertise an activity and later continue it.&lt;/p&gt;
&lt;p&gt;Here is a snippet that shows how to advertise an activity, for example, in a details view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ColorDetailsView: View {
  var color: String

  var body: some View {
    Image(color)
      // ...
      .userActivity(&quot;showColor&quot; ) { activity in
        activity.title = color
        activity.isEligibleForSearch = true
        activity.isEligibleForPrediction = true
        // ...
      }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To allow continuation of this activity, you can register a &lt;code&gt;onContinueUserActivity&lt;/code&gt; closure in your top-level navigation view, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct ContentView: View {
  var colors = [&quot;Red&quot;, &quot;Green&quot;, &quot;Yellow&quot;, &quot;Blue&quot;, &quot;Pink&quot;, &quot;Purple&quot;]

  @State var selectedColor: String? = nil

  var body: some View {
    NavigationView {
      ScrollView {
        LazyVGrid(columns: columns) {
          ForEach(colors, id: \.self) { color in
            NavigationLink(destination: ColorDetailsView(color: color),
                           tag: color,
                           selection: $selectedColor) {
              Image(color)
            }
          }
        }
        .onContinueUserActivity(&quot;showColor&quot;) { userActivity in
          if let color = userActivity.userInfo?[&quot;colorName&quot;] as? String {
            selectedColor = color
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Help - none of the above works for me!&lt;/h2&gt;
&lt;p&gt;Not all of &lt;code&gt;AppDelegate&lt;/code&gt;&apos;s callbacks are supported by the new application life cycle (yet). If none of the above meets your needs, you might require an &lt;code&gt;AppDelegate&lt;/code&gt; after all.&lt;/p&gt;
&lt;p&gt;Another reason you might require an AppDelegate is if you use any third-party SDKs that make use of &lt;a href=&quot;https://pspdfkit.com/blog/2019/swizzling-in-swift/&quot;&gt;method swizzling&lt;/a&gt; to inject themselves into the application life cycle. &lt;a href=&quot;https://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt; is a &lt;a href=&quot;https://stackoverflow.com/a/62633158/281221&quot;&gt;well-known case&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To help you out, Swift provides a way to connect a conformer of &lt;code&gt;AppDelegate&lt;/code&gt; with your &lt;code&gt;App&lt;/code&gt; implementation: &lt;code&gt;@UIApplicationDelegateAdaptor&lt;/code&gt;. Here is how to use it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -&amp;gt; Bool {
    print(&quot;Colors application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.&quot;)
    return true
  }
}

@main
struct ColorsApp: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don&apos;t forget to remove the &lt;code&gt;@main&lt;/code&gt; attribute if you copy an existing &lt;code&gt;AppDelegate&lt;/code&gt; implementation - otherwise, the compiler will complain about multiple application entry points.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With all this, let&apos;s discuss why Apple made this change. I think there are a couple of reasons:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0281-main-attribute.md#motivation&quot;&gt;SE-0281&lt;/a&gt; explicitly states that one of the design goals was &lt;em&gt;&quot;to offer a more general purpose and lightweight mechanism for delegating a program’s entry point to a designated type.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The DSL-based approach Apple chose for handling the application life cycle aligns nicely with the declarative approach for building UIs in SwiftUI. Using the same concepts makes things easier to understand and will help onboarding new developers.&lt;/p&gt;
&lt;p&gt;The key benefit of any declarative approach is: instead of putting the burden of implementing a specific functionality on developers, the framework / platform provider takes care of this. Should any changes become necessary, it will be a lot easier to ship these without breaking many developers&apos; apps - ideally, developers won&apos;t have to change their implementation, as the framework will take care of everything for you.&lt;/p&gt;
&lt;p&gt;Overall, the new application life cycle model makes implementing your application start-up easier and less convoluted. Your code will be cleaner and easier to maintain - and that&apos;s always a good thing, if you ask me.&lt;/p&gt;
&lt;p&gt;I hope this article helped you understand the ins and outs of the new application life cycle. If you&apos;ve got any questions or remarks regarding this article, feel free to &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;follow me on Twitter&lt;/a&gt; and send me a message, or file an issue on the &lt;a href=&quot;https://github.com/peterfriese/Colors&quot;&gt;sample project on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;h2&gt;Further reading&lt;/h2&gt;
&lt;p&gt;If you want to learn more, check out these resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0281-main-attribute.md&quot;&gt;Swift Evolution SE-0281 - @main: Type-Based Program Entry Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/app&quot;&gt;The App Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content&quot;&gt;Allowing Apps and Websites to Link to Your Content&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;The header image is based on &lt;a href=&quot;https://thenounproject.com/kavya261990/collection/space/?i=3437783&quot;&gt;Rocket&lt;/a&gt; by &lt;a href=&quot;https://thenounproject.com/kavya261990/&quot;&gt;Icongeek26&lt;/a&gt; on the &lt;a href=&quot;https://thenounproject.com/&quot;&gt;Noun Project&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Updating Data in Firestore from a SwiftUI app</title><link>https://peterfriese.dev/blog/2020/swiftui-firebase-update-data/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/swiftui-firebase-update-data/</guid><description>Learn how to update data in Firestore from a SwiftUI Application</description><pubDate>Mon, 28 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import demo from &apos;./swiftui-firebase-update-data/demo.mp4&apos;;&lt;/p&gt;
&lt;p&gt;In this series of articles about SwiftUI and Firebase, we’re building a simple CRUD (Create, Read, Update, Delete) application that allows users to manage their book library.&lt;/p&gt;
&lt;p&gt;In previous episodes,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I showed you &lt;a href=&quot;../swiftui-firebase-fetch-data/&quot;&gt;how to fetch data from Cloud Firestore&lt;/a&gt; in realtime&lt;/li&gt;
&lt;li&gt;we looked at making our apps safer by &lt;a href=&quot;../swiftui-firebase-codable/&quot;&gt;mapping Firestore documents using Codable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;we discovered how easy it is to &lt;a href=&quot;../swiftui-firebase-add-data/&quot;&gt;add data to Cloud Firestore from a SwiftUI app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following screen flow diagram gives you an impression of what we&apos;ve achieved so far (in the blue frame):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can use our sample app to add new books and display them in a list view. What&apos;s missing is updating existing books or seeing their details.&lt;/p&gt;
&lt;p&gt;In this episode, we’re going to implement the missing screens (in the pink frame) for viewing and editing a book’s details. The result will be a basic CRUD application that you can easily adapt to other data models.&lt;/p&gt;
&lt;p&gt;Along the way, you will learn a few things about refactoring SwiftUI code and which strategies can help us re-use code in our views.&lt;/p&gt;
&lt;p&gt;Without further ado, let’s get started!&lt;/p&gt;
&lt;h2&gt;Architectural Overview&lt;/h2&gt;
&lt;p&gt;The architecture for the sample app follows the MVVM (Model View ViewModel) paradigm. In MVVM apps, the state of a view is determined by the state of the view model(s) it is connected to. Any changes in the view model are reflected in the user interface. The same way, any actions the user takes in the UI will be applied to the view model. The view model, in turn, communicates with any underlying services and your app’s persistence layer.&lt;/p&gt;
&lt;p&gt;SwiftUI’s declarative approach and the way it handles state fits well with the ideas of MVVM, and this is one of the reasons why I like to use MVVM when building SwiftUI apps.&lt;/p&gt;
&lt;h2&gt;Take a look inside - the Book Details screen&lt;/h2&gt;
&lt;p&gt;This screen presents more details about a book than we might be able to display in the main list view: information like the author, the number of pages, and the book cover. I know - the current version of our app doesn’t support book covers yet - this is something we’re going to implement in a future episode.&lt;/p&gt;
&lt;p&gt;The user can navigate to the Book Details screen by tapping on the book row in the main list view. This drill-down navigation is a classic navigation pattern in iOS, and you might be familiar with it from the official Contacts app.&lt;/p&gt;
&lt;p&gt;To implement the UI, we’ll make use of the &lt;code&gt;Form&lt;/code&gt; view we’ve used in the &lt;em&gt;Add Book&lt;/em&gt; screen in one of the previous episodes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct BookDetailsView: View {
  // MARK: - State

  @Environment(\.presentationMode) var presentationMode

  // MARK: - State (via initialiser)

  var book: Book //(1)

  // MARK: - UI Components

  var body: some View {
    Form {
      Section(header: Text(&quot;Book&quot;)) {
        Text(book.title)
        Text(&quot;\(book.numberOfPages) pages&quot;)
      }

      Section(header: Text(&quot;Author&quot;)) {
        Text(book.author)
      }
    }
    .navigationBarTitle(book.title)
  }

}

struct BookDetailsView_Previews: PreviewProvider {
  static var previews: some View {
    let book = Book(title: &quot;Changer&quot;, author: &quot;Matt Gemmell&quot;, numberOfPages: 474)
    return
      NavigationView { //(2)
        BookDetailsView(book: book)
      }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing exciting to write home about - you should be pretty familiar with the UI elements we&apos;re using in this screen.&lt;/p&gt;
&lt;p&gt;The only thing I&apos;d like to call out is how we&apos;ve set up the preview: instead of returning &lt;code&gt;BookDetailsView&lt;/code&gt; directly, we&apos;ve wrapped it in a &lt;code&gt;NavigationView&lt;/code&gt; (2) - this allows us to see how the &lt;em&gt;Book Details&lt;/em&gt; view will look like once it is embedded in the overall navigation structure.&lt;/p&gt;
&lt;p&gt;To allow users to navigate to this screen by tapping on a book in the main list view, we need to wrap all &lt;code&gt;BookRowView&lt;/code&gt;s in the &lt;code&gt;BooksListView &lt;/code&gt;list in a &lt;code&gt;NavigationLink&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      List {
        ForEach (viewModel.books) { book in
          NavigationLink(destination: BookDetailsView(book: book)) { //(1)
            BookRowView(book: book)
          }
        }
      }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run the app now, you will notice that everything seems to work fine: the list of books appears, you can add new books, and you can view a book&apos;s details by navigating to the &lt;em&gt;Book Details&lt;/em&gt; screen.&lt;/p&gt;
&lt;p&gt;However, if you change the details of a book in the Firebase Console, the updates don&apos;t show up in the &lt;em&gt;Book Details&lt;/em&gt; screen. They do show up in the list of books on the main screen, though - so what&apos;s wrong?&lt;/p&gt;
&lt;p&gt;If you recall how we set up the real-time synchronisation with Firestore in the &lt;a href=&quot;https://peterfriese.dev/swiftui-firebase-fetch-data/&quot;&gt;first episode&lt;/a&gt;, you&apos;ll remember that we subscribed to the &lt;code&gt;books&lt;/code&gt; collection in Firestore by adding a snapshot listener. We activate this snapshot listener in the &lt;code&gt;.onAppear&lt;/code&gt; callback (1) in the main view of the application, &lt;code&gt;BooksListView&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ...
var body: some View {
    NavigationView {
      List {
        ForEach (viewModel.books) { book in
          BookRowView(book: book)
        }
      }
      // ...
      .onAppear() { //(1)
        print(&quot;BooksListView appears. Subscribing to data updates.&quot;)
        self.viewModel.subscribe()
      }
      // by unsubscribing from the view model, we prevent updates coming in from Firestore to be reflected in the UI
      .onDisappear() { //(2)
        print(&quot;BooksListView disappears. Unsubscribing from data updates.&quot;)
        self.viewModel.unsubscribe()
      }
     // ...
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just a couple of lines below, we call &lt;code&gt;unsubscribe&lt;/code&gt; to deactivate the snapshot listener in the &lt;code&gt;.onDisappear&lt;/code&gt; callback (2).&lt;/p&gt;
&lt;p&gt;Both &lt;code&gt;.onAppear&lt;/code&gt; and &lt;code&gt;.onDisappear&lt;/code&gt; are callback methods that allow us to participate in the life cycle of a view - iOS will call them whenever a view appears or disappears.&lt;/p&gt;
&lt;p&gt;The intention of unsubscribing from the snapshot listener was to turn off real-time sync whenever the application goes into the background. However, it turns out that &lt;code&gt;.onDisappear&lt;/code&gt; will &lt;em&gt;not&lt;/em&gt; be called when the application goes into the background. SwiftUI 2 provides a new way to signal when an application goes into the background, and we will take a closer look at this in a separate episode.&lt;/p&gt;
&lt;p&gt;Since iOS terminates socket connections (which Firestore uses for its real-time sync feature) when your app enters the background, you won&apos;t receive any real-time updates while the app is backgrounded. Real-time sync will resume automatically once the app returns to the foreground&lt;/p&gt;
&lt;p&gt;So to fix the problem that updates do not propagate to the &lt;em&gt;Book Details&lt;/em&gt; screen, all we have to do is remove the line which unsubscribes from the snapshot listener.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      // by unsubscribing from the view model, we prevent updates coming in from Firestore to be reflected in the UI
//      .onDisappear() {
//        print(&quot;BooksListView disappears. Unsubscribing from data updates.&quot;)
//        self.viewModel.unsubscribe()
//      }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Making changes - the &lt;em&gt;Edit Book&lt;/em&gt; screen&lt;/h2&gt;
&lt;p&gt;When we implemented the &lt;em&gt;Add Book&lt;/em&gt; screen in the previous episode, I sneakily named it &lt;code&gt;BookEditView&lt;/code&gt;, because I knew we&apos;d want to reuse it. As you can see on the screen flow diagram, the screens are reasonably similar, with only a few differences:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Add Book&lt;/th&gt;
&lt;th&gt;Edit Book&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Confirmation Button&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;td&gt;Save&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Title&lt;/td&gt;
&lt;td&gt;New book&lt;/td&gt;
&lt;td&gt;(the actual title of the book)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form action button&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Delete Book&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;SwiftUI comes with a couple of language features that we can apply to use the screen both for adding new books, as well as editing existing ones.&lt;/p&gt;
&lt;p&gt;To express the mode we wish to use the screen in, let&apos;s first introduce an enum in &lt;code&gt;BookEditView.swift&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum Mode {
  case new
  case edit
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By adding the following line to &lt;code&gt;BookEditView&lt;/code&gt;, we define a &lt;code&gt;mode&lt;/code&gt; property with a default value of &lt;code&gt;.new&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To indicate whether we want to add a new book or edit an existing one, let&apos;s add the following line to &lt;code&gt;BookEditView&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var mode: Mode = .new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swift will automatically add this to the memberwise initialiser, which will allow us to create an instance of this screen with any of the following calls:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BookEditView(mode: .edit)  // open to edit an existing book
BookEditView(mode: .new) // open to add a new book
BookEditView() // open to add a new book (thanks to default value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Providing a default value of &lt;code&gt;.new&lt;/code&gt; allows us to call the initialiser without a parameter, meaning we won&apos;t have to change our existing code.&lt;/p&gt;
&lt;p&gt;This preliminary work allows us to display the views on the screen according to the &lt;code&gt;mode&lt;/code&gt; the caller specifies.&lt;/p&gt;
&lt;h3&gt;Displaying the title according to the screen mode&lt;/h3&gt;
&lt;p&gt;According to the screen flow diagram and the property table, the screen title should read “New book” if it is in &lt;code&gt;.new&lt;/code&gt; mode. If it is in &lt;code&gt;.edit&lt;/code&gt; mode, it should display the title of the book we’re editing.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;ternary conditional operator&lt;/em&gt; (sometimes referred to as the &lt;code&gt;ternary if statement&lt;/code&gt;) provides a great way to implement this requirement.&lt;/p&gt;
&lt;p&gt;Replace the following line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  .navigationBarTitle(&quot;New book&quot;, displayMode: .inline)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with the following two lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  .navigationTitle(mode == .new ? &quot;New book&quot; : viewModel.book.title)
  .navigationBarTitleDisplayMode(mode == .new ? .inline : .large)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could have used a ternary conditional operator for &lt;em&gt;both&lt;/em&gt; parameters of the &lt;code&gt;navigationBarTitle(_, displayMode)&lt;/code&gt; view modifier, but this would have looked a bit messy. Pulling this into two separate statements makes the code easier to read.&lt;/p&gt;
&lt;h3&gt;Displaying a different title on the Save button&lt;/h3&gt;
&lt;p&gt;To adapt the title of the &lt;em&gt;Save&lt;/em&gt; button according to the screen &lt;code&gt;mode&lt;/code&gt;, we will use the same approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  Button(action: { self.handleDoneTapped() }) {
    Text(mode == .new ? &quot;Done&quot; : &quot;Save&quot;)
  }
  .disabled(!viewModel.modified)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Connect the &lt;em&gt;Edit Book&lt;/em&gt; screen to the &lt;em&gt;Book Details&lt;/em&gt; screen&lt;/h3&gt;
&lt;p&gt;The screen flow diagram indicates that users can navigate to the &lt;em&gt;Edit Book&lt;/em&gt; screen by tapping the &lt;em&gt;Edit&lt;/em&gt; button on the navigation bar of the &lt;em&gt;Book Details&lt;/em&gt; screen.&lt;/p&gt;
&lt;p&gt;So let&apos;s add this button now to &lt;code&gt;BookDetailsView&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BookDetailsView: View {
  // MARK: - State

  @Environment(\.presentationMode) var presentationMode
  @State var presentEditBookSheet = false  //(1)

  // MARK: - State (via initialiser)

  var book: Book

  // MARK: - UI Components

  var body: some View {

    // ...

    .navigationBarTitle(book.title)
    .navigationBarItems(trailing:
      Button(action: { self.presentEditBookSheet.toggle() }) { //(2)
        Text(&quot;Edit&quot;)
      }
    )
    .sheet(isPresented: self.$presentEditBookSheet) { //(3)
      BookEditView(viewModel: BookViewModel(book: book), mode: .edit)
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few words about this code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We want to present the &lt;em&gt;Edit Book&lt;/em&gt; screen in a modal sheet, so we need to track the presentation state (presented / not presented) of the sheet. The &lt;code&gt;@State&lt;/code&gt; property &lt;code&gt;presentEditBookSheet&lt;/code&gt; helps us do this (1).&lt;/li&gt;
&lt;li&gt;To toggle the state of the &lt;code&gt;presentEditBookSheet&lt;/code&gt; property, we&apos;ve added a &lt;code&gt;Button&lt;/code&gt; (titled &lt;code&gt;Edit&lt;/code&gt;) to the navigation bar (2).&lt;/li&gt;
&lt;li&gt;And finally, we instruct SwiftUI to show &lt;code&gt;BookEditView&lt;/code&gt; in a modal sheet on top of the current screen (3).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Updating a book in Firestore&lt;/h2&gt;
&lt;p&gt;If you run the application now, you will notice that editing a book and saving it will result in a duplicate.&lt;/p&gt;
&lt;p&gt;This happens because we call &lt;code&gt;.addDocument(from:)&lt;/code&gt; in our view model regardless of whether we&apos;re adding a new book or editing an existing one.&lt;/p&gt;
&lt;p&gt;To fix this, we need to introduce a new method on our view model for updating existing books. But how does our view model know whether the user is editing an existing book or adding a new one? There are a couple of options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move the &lt;code&gt;mode&lt;/code&gt; property from &lt;code&gt;BookViewModel&lt;/code&gt; to &lt;code&gt;BookEditView&lt;/code&gt;, allowing us to access the mode (&lt;code&gt;.add&lt;/code&gt; or &lt;code&gt;.new&lt;/code&gt;) from both within the view model and the view.&lt;/li&gt;
&lt;li&gt;Inspect the &lt;code&gt;book&lt;/code&gt; property of the view model. If its &lt;code&gt;id&lt;/code&gt; property equals &lt;code&gt;nil&lt;/code&gt;, we can safely assume this is a new book that hasn&apos;t been saved to Firestore yet. A non-nil &lt;code&gt;id&lt;/code&gt; is a strong indicator that we have fetched this &lt;code&gt;Book&lt;/code&gt; instance from Firestore before and should update the existing document rather than saving a new one.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;ve decided to go with option (2), but feel free to implement option (1) and let me know which one you like better.&lt;/p&gt;
&lt;p&gt;Here&apos;s the code for updating an existing book:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private func updateBook(_ book: Book) {
  if let documentId = book.id {
    do {
      try db.collection(&quot;books&quot;).document(documentId).setData(from: book)
    }
    catch {
      print(error)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice this function is &lt;code&gt;private&lt;/code&gt;, as we only want to call it from within the &lt;code&gt;BookViewModel&lt;/code&gt; class. Don&apos;t forget to mark &lt;code&gt;addBook(_ book:)&lt;/code&gt; as &lt;code&gt;private&lt;/code&gt; as well, to prevent anyone from calling it from the outside accidentally.&lt;/p&gt;
&lt;p&gt;Let&apos;s now introduce a method that decides whether we need to add a new document or update an existing one:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private func updateOrAddBook() { //(1)
    if let _ = book.id {
      self.updateBook(self.book) //(2)
    }
    else {
      addBook(book) //(3)
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned before, this method will check (1) if the &lt;code&gt;book&lt;/code&gt; property has a non-nil &lt;code&gt;id&lt;/code&gt;. If so, the book holds data from an existing document, and we can update the existing document using our &lt;code&gt;updateBook&lt;/code&gt; (2) function. Otherwise, we&apos;ll call &lt;code&gt;addBook&lt;/code&gt; (3) to create a new document for our book in Firestore.&lt;/p&gt;
&lt;p&gt;And finally, we need to update the &lt;code&gt;save()&lt;/code&gt; method like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  func save() {
    updateOrAddBook()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Deleting a book&lt;/h2&gt;
&lt;p&gt;If you&apos;ve made it this far - hang in there, we&apos;re almost done! To check off all letters of CRUD (&lt;strong&gt;C&lt;/strong&gt;reate, &lt;strong&gt;R&lt;/strong&gt;ead, &lt;strong&gt;U&lt;/strong&gt;pdate, &lt;strong&gt;D&lt;/strong&gt;elete), we just need to implement deleting a book.&lt;/p&gt;
&lt;p&gt;As you will see in a minute, allowing users to delete a book from the &lt;em&gt;Edit Book&lt;/em&gt; screen poses an unexpected challenge.&lt;/p&gt;
&lt;p&gt;But first, let&apos;s add a delete button to the screen.&lt;/p&gt;
&lt;p&gt;At the end of the form in &lt;code&gt;BookEditView&lt;/code&gt;, insert a new section that contains a single button:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;        if mode == .edit {
          Section {
            Button(&quot;Delete book&quot;) { self.presentActionSheet.toggle() }
              .foregroundColor(.red)
          }
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This section is only visible when editing an &lt;em&gt;existing&lt;/em&gt; book. After all, deleting a book that hasn’t even been added to Firestore yet doesn’t make any sense!&lt;/p&gt;
&lt;p&gt;It is good practice to ask the user for confirmation before performing a destructive operation like deleting a book. A common way to do this is by showing an action sheet. If the user confirms their intent, we can go ahead and delete the book, and then dismiss the screen. We&apos;ll have to update the &lt;code&gt;BookEditView&lt;/code&gt; in a couple of locations to achieve this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BookEditView: View {
  @Environment(\.presentationMode) private var presentationMode
  @State var presentActionSheet = false //(1)

  // ...

  var body: some View {
    NavigationView {
      Form {
        // ...
        if mode == .edit {
          Section {
            Button(&quot;Delete book&quot;) { self.presentActionSheet.toggle() }  //(2)
              .foregroundColor(.red)
          }
        }

      }
      // ...
      .actionSheet(isPresented: $presentActionSheet) { //(3)
        ActionSheet(title: Text(&quot;Are you sure?&quot;),
                    buttons: [
                      .destructive(Text(&quot;Delete book&quot;),
                                   action: { self.handleDeleteTapped() }), //(4)
                      .cancel()
                    ])
      }
    }
  }

// ...

  func handleDeleteTapped() { //(5)
    viewModel.handleDeleteTapped()
    self.dismiss()
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few notes about this code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Depending on the value of the &lt;code&gt;presentActionSheet&lt;/code&gt; property (1), the action sheet will be presented (3)&lt;/li&gt;
&lt;li&gt;The user can change the state of the &lt;code&gt;presentActionSheet&lt;/code&gt; by tapping the delete button (2), or by dismissing the action sheet.&lt;/li&gt;
&lt;li&gt;If the user taps on &quot;Delete book&quot;, we will call &lt;code&gt;handleDeleteTapped()&lt;/code&gt; (4, 5)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To perform the delete operation, we need to add the following code to the &lt;code&gt;BookViewModel&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private func removeBook() {
    if let documentId = book.id {
      db.collection(&quot;books&quot;).document(documentId).delete { error in
        if let error = error {
          print(error.localizedDescription)
        }
      }
    }
  }

  // MARK: - UI handlers

  func handleDeleteTapped() {
    self.removeBook()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run the application now, you will notice that the user will end up on the &lt;em&gt;Book Details&lt;/em&gt; screen for the very book they have just deleted! This behaviour is rather unexpected and confusing. The expected behaviour is to jump back to the main list view instead.&lt;/p&gt;
&lt;p&gt;The solution is to also dismiss the &lt;em&gt;Book Details&lt;/em&gt; screen when the user deletes a book. Unfortunately, SwiftUI doesn&apos;t offer an elegant mechanism to pop more than one screen at a time - just have a look at &lt;a href=&quot;https://stackoverflow.com/search?q=swiftui+pop+view&quot;&gt;how many times this has been asked on Stack Overflow&lt;/a&gt;! Someone even came up with their own &lt;a href=&quot;https://github.com/biobeats/swiftui-navigation-stack&quot;&gt;navigation stack component&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Instead of following this route, I&apos;d like to propose a different solution here: let&apos;s add a completion handler to the &lt;em&gt;Edit Book&lt;/em&gt; screen that we can call when the user deletes the book. The parent screen can then decide what to do. In our case, the parent screen is the &lt;em&gt;Book Details&lt;/em&gt; screen, so we&apos;ll want to dismiss that screen as soon as the book gets deleted.&lt;/p&gt;
&lt;p&gt;Here&apos;s how to implement this. First, we&apos;ll define an enum that lets us express why the completion handler was called. Then, we define a completion handler on the &lt;code&gt;BookEditView&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum Action {
  case delete
  case done
  case cancel
}

struct BookEditView: View {
  // ...
  var mode: Mode = .new
  var completionHandler: ((Result&amp;lt;Action, Error&amp;gt;) -&amp;gt; Void)?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use Swift&apos;s &lt;code&gt;Result&lt;/code&gt; type to communicate with the parent view, which lets us send one of the &lt;code&gt;Action&lt;/code&gt; cases, and allows for optional error handling.&lt;/p&gt;
&lt;p&gt;The updated version of &lt;code&gt;handleDeleteTapped&lt;/code&gt; makes use of the &lt;code&gt;completionHandler&lt;/code&gt;, signalling that the reason for completion was &lt;code&gt;.delete&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  func handleDeleteTapped() {
    viewModel.handleDeleteTapped()
    self.dismiss()
    self.completionHandler?(.success(.delete))
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the &lt;code&gt;completionhandler&lt;/code&gt; is optional, the parent view is free to provide a closure for the completion handler or omit it. Here is the updated call site for &lt;code&gt;BookDetailsView&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    .sheet(isPresented: self.$presentEditBookSheet) {
      BookEditView(viewModel: BookViewModel(book: book), mode: .edit) { result in //(1)
        if case .success(let action) = result, action == .delete { //(2)
          self.presentationMode.wrappedValue.dismiss() //(3)
        }
      }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we provide an anonymous closure to handle the callback. Inside the closure, we check whether &lt;code&gt;BookEditView&lt;/code&gt; completed successfully, and if so, unwrap the &lt;code&gt;action&lt;/code&gt; parameter. If the &lt;code&gt;action&lt;/code&gt; equals &lt;code&gt;.delete&lt;/code&gt; (2), we dismiss the &lt;code&gt;BookDetailsView&lt;/code&gt; (3).&lt;/p&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
&lt;p&gt;With everything in place, we can now run the application one more time to verify everything works as intended. Notice how all changes are synced between Firestore and the UI in realtime:&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;And with that, we’ve implemented a simple CRUD application using SwiftUI and Firebase!&lt;/p&gt;
&lt;p&gt;So far, you have learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to connect a SwiftUI app to Firestore and synchronise data in realtime&lt;/li&gt;
&lt;li&gt;How to implementing an MVVM architecture helped us to separate concerns and keep our code lean and easy to maintain&lt;/li&gt;
&lt;li&gt;How to &lt;strong&gt;C&lt;/strong&gt;reate, &lt;strong&gt;R&lt;/strong&gt;ead, &lt;strong&gt;U&lt;/strong&gt;pdate, and &lt;strong&gt;D&lt;/strong&gt;elete data in Firestore using the Firebase iOS SDK&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the source code for the sample application in &lt;a href=&quot;https://github.com/peterfriese/BookSpine&quot;&gt;this GitHub repository&lt;/a&gt;. If you&apos;ve got any questions, feel free to file an issue or ping me on Twitter.&lt;/p&gt;
&lt;p&gt;You will find the source code for the sample application on GitHub. If you’ve got any questions, feel free to ping me on Twitter at &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;@peterfriese&lt;/a&gt;, or &lt;a href=&quot;https://github.com/peterfriese/BookSpine/issues&quot;&gt;open an issue&lt;/a&gt; on the GitHub repo.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The header image is based on &lt;a href=&quot;https://thenounproject.com/search/?q=cloud+arrow&amp;amp;i=3182755&quot;&gt;Upload&lt;/a&gt; by &lt;a href=&quot;https://thenounproject.com/madadesign/&quot;&gt;Gajah Mada Studio&lt;/a&gt; from the &lt;a href=&quot;https://thenounproject.com/&quot;&gt;Noun Project&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Fetching API Keys from Property List Files</title><link>https://peterfriese.dev/blog/2020/reading-api-keys-from-plist-files/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/reading-api-keys-from-plist-files/</guid><description>How to keep your apps secure</description><pubDate>Wed, 12 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Many APIs require developers to provide an API key and/or API secret in order to be able to access the API.&lt;/p&gt;
&lt;p&gt;This is both to identify the app that is accessing the API, and to limit access to the API for apps that are known to the API.&lt;/p&gt;
&lt;p&gt;Both the API key and the secret (if you have one) should be treated as a ... secret: anyone who knows these can access and use the API, impersonating as your app. This results in all sorts of security concerns: depending on the type of API, an attacker might be able to access your application&apos;s data, compromise your users&apos; data, and access information that is protected by the terms of service established between you and the service provider. They might also thrash the API, causing a large bill for you at the end of the month.&lt;/p&gt;
&lt;p&gt;All of these are good reasons to make sure to keep your API keys and secrets safe and secure.&lt;/p&gt;
&lt;p&gt;In this article, we&apos;re going to look at how to make sure your API keys and secrets don&apos;t accidentally leak to your version control system. The easiest, but also most dangerous way to store your API key is to define a constant in your app&apos;s source code. You might have seen code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  let apiKey = &quot;s33cr3t!k3y&quot;

  func search(queryString: String) {
    TMDB.set(apiKey: apiKey)

    TMDB.Search.movies(query: queryString) { pagedListResult in
      // handle result
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When committing code like this to your version control system, anyone who has access to your repository can go ahead and use the API key in &lt;em&gt;their&lt;/em&gt; app to access the API. This might not be a big deal if your code lives in an in-house repository with tight access control, but it is a huge security risk for open source projects.&lt;/p&gt;
&lt;p&gt;The easiest way to work around this is to externalise your API key into a configuration file that you don&apos;t check in to your repository. You can then keep the API key in a secure location (such as a password manager), and hand it out to developers on a need-to-know basis. For example, you might want to use the API key for accessing the production endpoints only on your CI/CD server, and provide developers with API keys to the development endpoints (which might have stricter rate limiting and tighter cost caps).&lt;/p&gt;
&lt;p&gt;In iOS, we traditionally use Plist (short for property list) files to store and manage configuration data. Plist files essentially are XML files with benefits: for example, Xcode provides a graphical editor to make editing Plist files more pleasant, and there&apos;s an easy-to-use API for reading Plist files.&lt;/p&gt;
&lt;p&gt;Let&apos;s have a look at how this helps us to keep the API key in the above code snippet safe and make our code more secure.&lt;/p&gt;
&lt;h2&gt;Externalising the API Key&lt;/h2&gt;
&lt;p&gt;The steps to properly externalise an API key are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a Plist file to your project&lt;/li&gt;
&lt;li&gt;Define the keys/values in the Plist file&lt;/li&gt;
&lt;li&gt;Read the API key from the Plist file&lt;/li&gt;
&lt;li&gt;Use the API key&lt;/li&gt;
&lt;li&gt;Handle error scenarios&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To add a new Plist file to your project, make sure to select your project&apos;s root folder in the project navigator, and then choose &lt;em&gt;New File&lt;/em&gt; ... from the root folder&apos;s context menu. Type &lt;em&gt;property&lt;/em&gt; into the filter field, and then choose the &lt;em&gt;Property File&lt;/em&gt; type from the &lt;em&gt;Resources&lt;/em&gt; section.&lt;/p&gt;
&lt;p&gt;Choosing a good name for your property list file is essential, as we will later write a build script to automate handling of this file. I recommend the following naming scheme: &lt;code&gt;&amp;lt;name of the API&amp;gt;-Info.plist&lt;/code&gt;. In our example, we access TMDB (&lt;a href=&quot;https://www.themoviedb.org/&quot;&gt;The Movie Database&lt;/a&gt;), so we&apos;ll name the file &lt;code&gt;TMDB-Info.plist&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, go ahead and add a new key/value pair to the newly created file. I chose &lt;code&gt;API_KEY&lt;/code&gt; as the name for the key. Most API keys are strings, so choose &lt;code&gt;String&lt;/code&gt; as the data type, and then insert the key itself in the &lt;em&gt;value&lt;/em&gt; field. Here is how it should look like:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s now write some code to read the API key from the Plist file and use it when accessing the API.&lt;/p&gt;
&lt;p&gt;Reading a Plist file is just a one-liner: &lt;code&gt;let plist = NSDictionary(contentsOfFile: filePath)&lt;/code&gt; - this will give you a dictionary, which makes reading the API key as simple as &lt;code&gt;let value = plist?.object(forKey: &quot;API_KEY&quot;) as? String&lt;/code&gt;. To make reading the API key and using it in our code as easy as possible, we will wrap it in a computed property. This will also give us the opportunity to perform some error handling.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private var apiKey: String {
  get {
    // 1
    guard let filePath = Bundle.main.path(forResource: &quot;TMDB-Info&quot;, ofType: &quot;plist&quot;) else {
      fatalError(&quot;Couldn&apos;t find file &apos;TMDB-Info.plist&apos;.&quot;)
    }
    // 2
    let plist = NSDictionary(contentsOfFile: filePath)
    guard let value = plist?.object(forKey: &quot;API_KEY&quot;) as? String else {
      fatalError(&quot;Couldn&apos;t find key &apos;API_KEY&apos; in &apos;TMDB-Info.plist&apos;.&quot;)
    }
    return value
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we use &lt;code&gt;Bundle.main.path(forResource:ofType)&lt;/code&gt; to obtain the path to our Plist file. In case the file doesn&apos;t exist, we crash fatally (not providing the file is a programming error after all, and there is no way the app can recover), issuing a helpful error message.&lt;/p&gt;
&lt;p&gt;Next, we try to load the Plist file into a dictionary (2), and then read the value for the API key from the dictionary. In case the value doesn&apos;t exist, or is of the wrong type, we issue another error message (and crash the app).&lt;/p&gt;
&lt;p&gt;And finally, we return the value. Since we named the computed property &lt;code&gt;apiKey&lt;/code&gt; - just like the constant - we can now delete the constant from our code, and our app will continue working as before.&lt;/p&gt;
&lt;p&gt;With that, we&apos;ve got the basic infrastructure in place. Before you can relax with a hot or cold beverage of your choice, we&apos;ll have to talk about source control.&lt;/p&gt;
&lt;h2&gt;Source Control&lt;/h2&gt;
&lt;p&gt;If you&apos;ve accidentally checked in an API key to a public repository, there are two ways to fix the situation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rotating the API key (i.e. remove the old one from your code, get a new one, and then &lt;em&gt;make sure to not check the new one in&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Rewriting history (if you&apos;re using git). Before doing this, I urge you to watch Scott Hanselman&apos;s video &lt;a href=&quot;https://www.youtube.com/watch?v=dgOpnebZkRo&amp;amp;list=PL0M0zPgJ3HSesuPIObeUVQNbKqlw5U2Vr&amp;amp;index=1&quot;&gt;Git Push --Force will destroy the timeline and kill us all&lt;/a&gt; from his brilliant series &lt;a href=&quot;https://www.youtube.com/playlist?list=PL0M0zPgJ3HSesuPIObeUVQNbKqlw5U2Vr&quot;&gt;Computer Stuff They Didn&apos;t Teach You&lt;/a&gt;, and then choose option 1.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Even better than having to revoke an API key is to prevent accidentally checking it in. To this end, add any configuration files that contain API keys to your &lt;code&gt;.gitignore&lt;/code&gt; file. This also means each developer can have their own copy of this file, with their specific values. For example, you might be using an API key and URL for the production endpoint, whereas your colleague who is working on a new feature is using the development key and URL for the development endpoint.&lt;/p&gt;
&lt;p&gt;To make it easier for new team members (or when you need to check out your code on a different machine), provide a sample configuration file. I suggest naming it &lt;code&gt;&amp;lt;name of the Plist file&amp;gt;-Sample.plist&lt;/code&gt;. Then, add all required keys to this file and provide placeholder values. By prefixing the placeholder values with an underscore (or any other character that usually doesn&apos;t occur in your configuration), you can then extend your error handling code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private var apiKey: String {
  get {
    // 1
    guard let filePath = Bundle.main.path(forResource: &quot;TMDB-Info&quot;, ofType: &quot;plist&quot;) else {
      fatalError(&quot;Couldn&apos;t find file &apos;TMDB-Info.plist&apos;.&quot;)
    }
    // 2
    let plist = NSDictionary(contentsOfFile: filePath)
    guard let value = plist?.object(forKey: &quot;API_KEY&quot;) as? String else {
      fatalError(&quot;Couldn&apos;t find key &apos;API_KEY&apos; in &apos;TMDB-Info.plist&apos;.&quot;)
    }
    // 3
    if (value.starts(with: &quot;_&quot;)) {
      fatalError(&quot;Register for a TMDB developer account and get an API key at https://developers.themoviedb.org/3/getting-started/introduction.&quot;)
    }
    return value
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code in (3) checks if the value read from the Plist file starts with an underscore (which denotes a placeholder), and issues an error message that tells the developer how to obtain an API key.&lt;/p&gt;
&lt;p&gt;When adding this sample Plist file to your project, make sure &lt;em&gt;you do not include it in any build target&lt;/em&gt; - we don&apos;t want to include this file in our application binary, it&apos;s only relevant when checking out the project from your repository.&lt;/p&gt;
&lt;h2&gt;Bonus: Provisioning the Plist File After Checking Out&lt;/h2&gt;
&lt;p&gt;Speaking of your repository - wouldn&apos;t it be nice if the config file (in our case, &lt;code&gt;TMDB-Info.plist&lt;/code&gt;) was created automatically after checking out the project from source control?&lt;/p&gt;
&lt;p&gt;We can use Xcode build phases to achieve this. Here is how:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Select your target in the Xcode project editor&lt;/li&gt;
&lt;li&gt;Navigate to &lt;em&gt;Build Phases&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Add a new &lt;em&gt;Run Script&lt;/em&gt; build phase, making sure it is the first build phase in your target. Name this new phase &lt;em&gt;Copy sample API key&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Paste the following code&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;CONFIG_FILE_BASE_NAME=&quot;TMDB-Info&quot;

CONFIG_FILE_NAME=${CONFIG_FILE_BASE_NAME}.plist
SAMPLE_CONFIG_FILE_NAME=${CONFIG_FILE_BASE_NAME}-Sample.plist

CONFIG_FILE_PATH=$SRCROOT/$PRODUCT_NAME/$CONFIG_FILE_NAME
SAMPLE_CONFIG_FILE_PATH=$SRCROOT/$PRODUCT_NAME/$SAMPLE_CONFIG_FILE_NAME

if [ -f &quot;$CONFIG_FILE_PATH&quot; ]; then
  echo &quot;$CONFIG_FILE_PATH exists.&quot;
else
  echo &quot;$CONFIG_FILE_PATH does not exist, copying sample&quot;
  cp -v &quot;${SAMPLE_CONFIG_FILE_PATH}&quot; &quot;${CONFIG_FILE_PATH}&quot;
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, change the variable &lt;code&gt;CONFIG_FILE_BASE_NAME&lt;/code&gt; to match your config file name. The code in this build phase will create a new API key config file based on the sample file you&apos;ve checked in to the repository.&lt;/p&gt;
&lt;p&gt;This means, whenever you (or one of your team members) checks out your project and builds it, the API key config file will be created. When you run the app, the error handling code in the computed property will detect that this is a pristine copy and tell you to replace the placeholder value with a proper API key.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Keeping your API keys and secrets secure is just one piece of the puzzle of implementing and operating your app safely and securely, but it is an important one. I hope this article helped you to take this important step on the path to a more secure app, while making this a pleasant experience for you and your team.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The header image is based on &lt;a href=&quot;https://thenounproject.com/search/?q=key&amp;amp;i=3476538&quot;&gt;Security&lt;/a&gt; by &lt;a href=&quot;https://thenounproject.com/itim2101/&quot;&gt;Komkrit Noenpoempisut&lt;/a&gt; from the &lt;a href=&quot;https://thenounproject.com/&quot;&gt;Noun Project&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Adding Data to Firestore from a SwiftUI app</title><link>https://peterfriese.dev/blog/2020/swiftui-firebase-add-data/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/swiftui-firebase-add-data/</guid><description>Learn how to write data to Firestore from a SwiftUI Application</description><pubDate>Wed, 01 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import demo from &apos;./swiftui-firebase-add-data/demo.mp4&apos;;&lt;/p&gt;
&lt;p&gt;Previously in this series of articles about SwiftUI and Firebase, we talked about &lt;a href=&quot;../swiftui-firebase-fetch-data/&quot;&gt;fetching data from Cloud Firestore&lt;/a&gt; and how to &lt;a href=&quot;../swiftui-firebase-codable/&quot;&gt;map from Firestore documents to Swift structs&lt;/a&gt; using Swift&apos;s Codable API.&lt;/p&gt;
&lt;p&gt;You might recall that we also introduced a way to add new books to our collection of books in Firestore. To do so, we added a method &lt;code&gt;addBook(book:)&lt;/code&gt; to our view model. However, we didn&apos;t actually use it, as we didn&apos;t have a UI in place for entering the details about the new book.&lt;/p&gt;
&lt;p&gt;So today, let&apos;s look at how to build UI for adding a new book and how to update our view models to support this use case.&lt;/p&gt;
&lt;h2&gt;Don&apos;t judge a book by its cover&lt;/h2&gt;
&lt;p&gt;Before we jump into the implementation details, let&apos;s consider for a moment how the UI for our application will look like. You will notice that the overall structure is pretty similar to the iOS Contacts app.&lt;/p&gt;
&lt;p&gt;Take a look at the following diagram which shows all the screens of our application.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(The greyed-out parts of the diagram will be covered in the following episodes of this series, when we work on completing the app by implementing all the other CRUD (Create, Read, Update, Delete) features).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The books list screen is the centerpiece of the application - it shows a list of all the user&apos;s books. Tapping on a list item will take the user to a screen that will display the details of the respective book. We will implement the book details screen in one of the next episodes, re-using some of the code we&apos;re going to build today.&lt;/p&gt;
&lt;p&gt;By tapping on the &lt;code&gt;+&lt;/code&gt; button in the navigation bar, the user can add a new book. This will take them to a screen that will allow them to enter the details of a new book, such as the title or the number of pages. Tapping &lt;em&gt;Done&lt;/em&gt; will add the book to the collection of books and dismiss the screen. If the user decides they don&apos;t want to add a new book after all, they can tap the &lt;em&gt;Cancel&lt;/em&gt; button which will just dismiss the dialog. We will use SwiftUI&apos;s Forms API along with a number of &lt;code&gt;TextField&lt;/code&gt;s to implement this screen.&lt;/p&gt;
&lt;p&gt;To implement the &lt;em&gt;Add a new book&lt;/em&gt; use case, we will need the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The UI for the screen. This will be a SwiftUI view.&lt;/li&gt;
&lt;li&gt;A view model for handling the new screen&apos;s state.&lt;/li&gt;
&lt;li&gt;A button in the navigation bar of the book list screen to allow the user to open the &lt;em&gt;New Book&lt;/em&gt; screen&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Building the UI&lt;/h2&gt;
&lt;p&gt;Let&apos;s start by sketching out the UI. Here is the bare bones version of our &lt;em&gt;Add a new book&lt;/em&gt; screen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI

struct BookEditView: View {
  @Environment(\.presentationMode) private var presentationMode //(1)
  @StateObject var viewModel = BookViewModel() //(2)

  var body: some View {
    NavigationView {
      Form {
        Section(header: Text(&quot;Book&quot;)) { //(3)
          TextField(&quot;Title&quot;, text: $viewModel.book.title) //(4)
          TextField(&quot;Number of pages&quot;, value: $viewModel.book.numberOfPages, formatter: NumberFormatter()) //(5)
        }

        Section(header: Text(&quot;Author&quot;)) { //(6)
          TextField(&quot;Author&quot;, text: $viewModel.book.author) //(7)
        }
      }
      .navigationBarTitle(&quot;New book&quot;, displayMode: .inline)
      .navigationBarItems(
        leading:
          Button(action: { self.handleCancelTapped() }) {
            Text(&quot;Cancel&quot;)
          },
        trailing:
          Button(action: { self.handleDoneTapped() }) {
            Text(&quot;Done&quot;)
          }
          .disabled(!viewModel.modified) //(8)
        )
    }
  }

  func handleCancelTapped() { //(9)
    dismiss()
  }

  func handleDoneTapped() { //(10)
    self.viewModel.handleDoneTapped()
    dismiss()
  }

  func dismiss() { //(11)
    self.presentationMode.wrappedValue.dismiss()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few notes on how this code works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As the screen is going to be displayed modally, we&apos;ll want to be able to dismiss it when the user taps on the &lt;em&gt;Done&lt;/em&gt; or &lt;em&gt;Cancel&lt;/em&gt; button. The presentation state is managed via the &lt;code&gt;presentationMode&lt;/code&gt; environment variable, which we bind in (1). We introduce a helper method &lt;code&gt;dismiss()&lt;/code&gt; (11) to make it a little bit easier to dismiss the screen from the action handler methods &lt;code&gt;handleCancelTapped()&lt;/code&gt; (9) and &lt;code&gt;handleDoneTapped()&lt;/code&gt; (10).&lt;/li&gt;
&lt;li&gt;We instantiate the view model (2) so we can bind the individual UI elements to it. Note that we&apos;re using &lt;code&gt;@StateObject&lt;/code&gt; property wrapper here, which is new in iOS 14 / Xcode 12b1.&lt;/li&gt;
&lt;li&gt;The main part of the screen is taken up by a form with two sections: one for the book details (3), the other for the name of the author (6).&lt;/li&gt;
&lt;li&gt;The individual text fields are bound to the view model (4, 5, 7)&lt;/li&gt;
&lt;li&gt;You will notice that the text field for the number of pages looks slightly different: as the underlying data type is a number, we&apos;re using a &lt;code&gt;NumberFormatter&lt;/code&gt; to convert the user input (which is a string) into a number, and vice versa. Invalid input (anything other than a number) will be rejected. Please note that, as of now, you need to press [Enter] to commit any input in a &lt;code&gt;TextField&lt;/code&gt; that uses a &lt;code&gt;Formatter&lt;/code&gt; subclass. This likely is Apple&apos;s attempt to minimize the number of times the formatter needs to run.&lt;/li&gt;
&lt;li&gt;Since it only makes sense to save the book in Firestore once the user has entered some text, we only enable the &lt;em&gt;Done&lt;/em&gt; button if the view model is modified (8)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The View Model&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;BookViewModel&lt;/code&gt; manages the state of our &lt;em&gt;Add a new book&lt;/em&gt; screen. Its responsibilities are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Providing access to the book the user is editing&lt;/li&gt;
&lt;li&gt;Keeping book of the book&apos;s state, so we can enable / disable the &lt;em&gt;Done&lt;/em&gt; button on the UI&lt;/li&gt;
&lt;li&gt;Saving the new book to Firestore&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&apos;s take a closer look at the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Foundation
import Combine
import FirebaseFirestore

class BookViewModel: ObservableObject {
  // MARK: - Public properties

  @Published var book: Book //(1)
  @Published var modified = false

  // MARK: - Internal properties

  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  // MARK: - Constructors

  init(book: Book = Book(title: &quot;&quot;, author: &quot;&quot;, numberOfPages: 0)) { //(2)
    self.book = book

    self.$book //(3)
      .dropFirst() //(5)
      .sink { [weak self] book in
        self?.modified = true
      }
      .store(in: &amp;amp;self.cancellables)
  }

  // MARK: - Firestore

  private var db = Firestore.firestore()

  func addBook(_ book: Book) {
    do {
      let _ = try db.collection(&quot;books&quot;).addDocument(from: book)
    }
    catch {
      print(error)
    }
  }

  // MARK: - Model management

  func save() {
    addBook(self.book)
  }

  // MARK: - UI handlers

  func handleDoneTapped() {
    self.save()
  }

}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The view model has two published properties, which we subscribed to in the UI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;book&lt;/code&gt; (1) is the book that the user is currently editing / adding. If you look back at the code for the UI, you will notice that the individual &lt;code&gt;TextView&lt;/code&gt;s bind to the respective attributes of the &lt;code&gt;Book&lt;/code&gt; instance. Any change in the UI will be reflected in the &lt;code&gt;book&lt;/code&gt; instance, and vice versa.&lt;/li&gt;
&lt;li&gt;We provide an empty &lt;code&gt;Book&lt;/code&gt; instance as the default parameter for the constructor (2) to make creating the view model a little bit less verbose.&lt;/li&gt;
&lt;li&gt;To keep track of the modification state of the &lt;code&gt;book&lt;/code&gt; attribute, we set up a simple Combine pipeline (3) that will set &lt;code&gt;modified&lt;/code&gt; to true (4) as soon as the &lt;code&gt;book&lt;/code&gt; model is modified. To prevent the view model from being marked as modified when initially setting the &lt;code&gt;book&lt;/code&gt; instance, we drop the first event (5). Side note: this code doesn&apos;t mark the view model as unmodified if the user undoes all modifications, which is an obvious shortcoming. I&apos;ll leave the implementation of a more robust change detection to the keen reader :-)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The view model not only holds the state for our UI, but also provides action handlers that can be called by the UI. The only action handler in this case is &lt;code&gt;handleDoneTapped()&lt;/code&gt;, which will store the &lt;code&gt;book&lt;/code&gt; instance into Firestore.&lt;/p&gt;
&lt;p&gt;And finally, &lt;code&gt;addBook&lt;/code&gt; will store the &lt;code&gt;book&lt;/code&gt; attribute into the &lt;code&gt;books&lt;/code&gt; collection in our Firestore instance. If you&apos;ve read the &lt;a href=&quot;..swiftui-firebase-codable/&quot;&gt;previous episode&lt;/a&gt;, you will recognise this method.&lt;/p&gt;
&lt;h2&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;Now that we&apos;ve got the UI for editing a new book and the view model for managing the view state, all that&apos;s missing is a button to open the &lt;em&gt;Add a new book&lt;/em&gt; screen.&lt;/p&gt;
&lt;p&gt;First, let&apos;s define the button itself:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct AddBookButton: View {
  var action: () -&amp;gt; Void
  var body: some View {
    Button(action: { self.action() }) {
      Image(systemName: &quot;plus&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then use it in the &lt;code&gt;BookListView&lt;/code&gt; we built in the previous episode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BooksListView: View {
  @StateObject var viewModel = BooksViewModel()
  @State var presentAddBookSheet = false //(1)

  var body: some View {
    NavigationView {
      List {
        ForEach (viewModel.books) { book in
          BookRowView(book: book)
        }
      }
      .navigationBarTitle(&quot;Books&quot;)
      .navigationBarItems(trailing: AddBookButton() { //(2)
        self.presentAddBookSheet.toggle() //(3)
      })
      .sheet(isPresented: self.$presentAddBookSheet) { //(4)
        BookEditView() //(5)
      }

    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&apos;s how it works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;Add new book&lt;/em&gt; screen will be displayed in a modal sheet (5) which will only be shown if the boolean &lt;code&gt;presentAddBookSheet&lt;/code&gt; is true (1, 4)&lt;/li&gt;
&lt;li&gt;The button we defined above is added to the navigation bar (2)&lt;/li&gt;
&lt;li&gt;When the user presses the button, the boolean &lt;code&gt;presentAddBookSheet&lt;/code&gt; is toggled (3), and the &lt;em&gt;Add a new book&lt;/em&gt; screen will be shown&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
&lt;p&gt;With all these pieces in place, we can now run the application. Any book we add using &lt;code&gt;BookEditView&lt;/code&gt; will be added to Firestore (use the Firebase console to watch this)), and will instantly show up in the list of books in &lt;code&gt;BooksListView&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this episode, we looked at how to add new data to Cloud Firestore, and how to integrate this in a SwiftUI app that follows the MVVM approach.&lt;/p&gt;
&lt;p&gt;You learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to map data from a Swift struct to a Cloud Firestore document by using Swift&apos;s Codable protocol&lt;/li&gt;
&lt;li&gt;How to use a View Model to encapsulate data access and make managing our view state easier, resulting in a cleaner and more maintainable code base&lt;/li&gt;
&lt;li&gt;How to tie this all together to make sure our application state is always in sync both with our backend and within the frontend&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the next episode, we will work on completing the main functionality of our application by implementing functionality to update and delete books. In addition to implementing a details screen and integrating it with our navigation structure, we will also look at the flow of data and talk about refactoring our code to keep all data access code in one place.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and make sure to check out the Firebase channel on YouTube, where you will find a video version of this article.&lt;/p&gt;
&lt;p&gt;If you&apos;ve got any questions or comments, feel free to post an issue on the &lt;a href=&quot;https://github.com/peterfriese/BookSpine&quot;&gt;GitHub repo for this project&lt;/a&gt;, or reach out to me on Twitter at &lt;a href=&quot;https://twitter.com/peterfriese&quot;&gt;@peterfriese&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>SwiftUI: Mapping Firestore Documents using Swift Codable</title><link>https://peterfriese.dev/blog/2020/swiftui-firebase-codable/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/swiftui-firebase-codable/</guid><description>Learn how to easily map Firestore data in Swift</description><pubDate>Tue, 05 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;../swiftui-firebase-fetch-data&quot;&gt;Last time&lt;/a&gt;, we looked at how to connect a SwiftUI app to a Firebase project and synchronise data in real time. If you take a look at the code we used for mapping from Firestore documents to our Swift model structs, you will notice that is has a number of issues:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;self.books = documents.map { queryDocumentSnapshot -&amp;gt; Book in
  let data = queryDocumentSnapshot.data()
  let title = data[&quot;title&quot;] as? String ?? &quot;&quot;
  let author = data[&quot;author&quot;] as? String ?? &quot;&quot;
  let numberOfPages = data[&quot;pages&quot;] as? Int ?? 0

  return Book(id: .init(), title: title, author: author, numberOfPages: numberOfPages)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;It is rather verbose - even for just the few attributes we&apos;ve got!&lt;/li&gt;
&lt;li&gt;The code makes some assumptions about the structure of the documents, such as the attribute types&lt;/li&gt;
&lt;li&gt;Things might start breaking if we change the structure of the documents or the struct holding our model&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So in this post, I&apos;m going to show you how to make your mapping code more concise, less error-prone, and more maintainable by using the &lt;code&gt;Codable&lt;/code&gt; protocol.&lt;/p&gt;
&lt;h2&gt;What is Codable?&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Codable&lt;/code&gt; protocol is Apple&apos;s contribution to a standardised approach to encoding and decoding data. If you&apos;ve been a Swift developer for a while, you might recall that, before Apple introduced &lt;code&gt;Codable&lt;/code&gt; in Swift 4, you had to perform data mapping from and to external representations either manually or by importing third-party libraries. None of this is required anymore, thanks to &lt;code&gt;Codable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a long time, Firestore lacked support for &lt;code&gt;Codable&lt;/code&gt;, and the GitHub issue asking for &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk/issues/627&quot;&gt;adding support for object serialization in Firestore (#627)&lt;/a&gt; might be one of the most popular / upvoted issues of Firebase iOS SDK so far.&lt;/p&gt;
&lt;p&gt;The good news is that, as of October 2019, Firestore provides support for &lt;code&gt;Codable&lt;/code&gt;, and it&apos;s every bit as easy to use as you might hope it is. It essentially boils down to three steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add Firestore &lt;code&gt;Codable&lt;/code&gt; to your project&lt;/li&gt;
&lt;li&gt;Make your models &lt;code&gt;Codable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use the new methods to retrieve / store data&lt;/li&gt;
&lt;li&gt;(Bonus!) delete all of your existing mapping code&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&apos;s look at these a little bit closer.&lt;/p&gt;
&lt;h2&gt;Prepare your project&lt;/h2&gt;
&lt;p&gt;As Firestore&apos;s Codable support is only available for Swift, it lives in a separate Pod, &lt;code&gt;FirebaseFirestoreSwift&lt;/code&gt; - add this to your &lt;code&gt;Podfile&lt;/code&gt; and run &lt;code&gt;pod install&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Make your models Codable&lt;/h2&gt;
&lt;p&gt;Import &lt;code&gt;FirebaseFirestoreCodable&lt;/code&gt; in the file(s) holding your model structs, and implement &lt;code&gt;Codable&lt;/code&gt;, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Book: Identifiable, Codable {
  @DocumentID var id: String?
  var title: String
  var author: String
  var numberOfPages: Int
  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we want to use the &lt;code&gt;Book&lt;/code&gt; models in a &lt;code&gt;ListView&lt;/code&gt;, they need to implement &lt;code&gt;Identifiable&lt;/code&gt;, i.e. they need to have an &lt;code&gt;id&lt;/code&gt; attribute. If you&apos;ve worked with Firestore before, you know that each Firestore document has a unique document ID, so we can use that and map it to the &lt;code&gt;id&lt;/code&gt; attribute. To make this easier, &lt;code&gt;FirebaseFirestoreSwift&lt;/code&gt; provides a property wrapper, &lt;code&gt;@DocumentID&lt;/code&gt;, which tells the Firestore SDK to perform this mapping for us.&lt;/p&gt;
&lt;p&gt;If the attribute names on your Firestore documents match with the property names of your model structs, you&apos;re done now.&lt;/p&gt;
&lt;p&gt;However, if the attribute names differ, as they do in our example, you need to provide instructions for the &lt;code&gt;Encoder&lt;/code&gt; / &lt;code&gt;Decoder&lt;/code&gt; to map them correctly. We can do so by providing a nested enumeration that conforms to the &lt;code&gt;CodingKey&lt;/code&gt; protocol.&lt;/p&gt;
&lt;p&gt;In our example, the name of the attribute that contains the number of pages of a book is called &lt;code&gt;pages&lt;/code&gt; in our Firestore documents, but &lt;code&gt;numberOfPages&lt;/code&gt; in our &lt;code&gt;Book&lt;/code&gt; struct. Let&apos;s use the &lt;code&gt;CodingKeys&lt;/code&gt; to map them to each other:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Foundation
import FirebaseFirestoreSwift

struct Book: Identifiable, Codable {
  @DocumentID var id: String?
  var title: String
  var author: String
  var numberOfPages: Int

  enum CodingKeys: String, CodingKey {
    case id
    case title
    case author
    case numberOfPages = &quot;pages&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is important to note that once you use &lt;code&gt;CodingKeys&lt;/code&gt; you&apos;ll have to explicitly provide the names of all attributes you want to map. So if you forget to map the &lt;code&gt;id&lt;/code&gt; attribute, the &lt;code&gt;id&lt;/code&gt;s of your model instances will be &lt;code&gt;nil&lt;/code&gt;. This will result in unexpected behaviour, for example when trying to display them in a ListView. Check out &lt;a href=&quot;https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types&quot;&gt;Apple&apos;s documentation&lt;/a&gt; for a more detailed discussion of &lt;code&gt;Codable&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Fetching data&lt;/h2&gt;
&lt;p&gt;Now that our model is prepared for mapping, we can update the existing mapping code on our view model. At the moment, it looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func fetchData() {
  db.collection(&quot;books&quot;).addSnapshotListener { (querySnapshot, error) in
    guard let documents = querySnapshot?.documents else {
    print(&quot;No documents&quot;)
    return
  }

  self.books = documents.map { queryDocumentSnapshot -&amp;gt; Book in
    let data = queryDocumentSnapshot.data()
    let title = data[&quot;title&quot;] as? String ?? &quot;&quot;
    let author = data[&quot;author&quot;] as? String ?? &quot;&quot;
    let numberOfPages = data[&quot;pages&quot;] as? Int ?? 0

    return Book(id: .init(), title: title, author: author, numberOfPages: numberOfPages)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Time to delete some code and simplify this!&lt;/p&gt;
&lt;p&gt;We can replace the mapping code in the &lt;code&gt;documents.map&lt;/code&gt; closure with a much simpler version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func fetchData() {
  db.collection(&quot;books&quot;).addSnapshotListener { (querySnapshot, error) in
    guard let documents = querySnapshot?.documents else {
      print(&quot;No documents&quot;)
      return
    }

    self.books = documents.compactMap { queryDocumentSnapshot -&amp;gt; Book? in
      return try? queryDocumentSnapshot.data(as: Book.self)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we got rid of the entire process of manually reading attributes from the &lt;code&gt;data&lt;/code&gt; dictionary, performing typecasts, and providing default values. Our code is a lot safer now that the SDK takes care of this for us.&lt;/p&gt;
&lt;p&gt;All of this is thanks to the &lt;code&gt;data(as:)&lt;/code&gt; method, which is provided by the &lt;code&gt;FirebaseFirestoreSwift&lt;/code&gt; module. Doesn&apos;t it feel great to remove all this code?&lt;/p&gt;
&lt;h2&gt;Writing data&lt;/h2&gt;
&lt;p&gt;So we&apos;ve covered mapping data when reading it from Firestore - what about the opposite direction?&lt;/p&gt;
&lt;p&gt;It turns out that this is almost as simple as reading data - let&apos;s take a quick look. To write data, we can use use &lt;code&gt;addDocument(from:)&lt;/code&gt; instead of &lt;code&gt;.addDocument(data:)&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func addBook(book: Book) {
  do {
    let _ = try db.collection(&quot;books&quot;).addDocument(from: book)
  }
  catch {
    print(error)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This saves us from writing the mapping code, which means a lot less typing, and - more importantly - a lot less opportunity to get things wrong and introduce bugs. Great!&lt;/p&gt;
&lt;h2&gt;Advanced Features&lt;/h2&gt;
&lt;p&gt;With the basics under our belt, let&apos;s take a look at a couple of more advanced features.&lt;/p&gt;
&lt;p&gt;We already used the &lt;code&gt;@DocumentID&lt;/code&gt; property wrapper to tell Firestore to map the document IDs to the &lt;code&gt;id&lt;/code&gt; attribute in our &lt;code&gt;Book&lt;/code&gt; struct. There are two other property wrappers you might find useful: &lt;code&gt;@ExplicitNull&lt;/code&gt; and &lt;code&gt;@ServerTimestamp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If an attribute is marked as &lt;code&gt;@ExplicitNull&lt;/code&gt;, Firestore will write the attribute into the target document with a &lt;code&gt;null&lt;/code&gt; value. If you save a document with an optional attribute that is &lt;code&gt;nil&lt;/code&gt; to Firestore and it is not marked as &lt;code&gt;@ExplicitNull&lt;/code&gt;, Firestore will just omit it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@ServerTimestamp&lt;/code&gt; is useful if you want need to handle timestamps in your app. In any distributed system, chances are that the clocks on the individual systems are not completely in sync all of the time. You might think this is not a big deal, but imagine the implications of a clock running slightly out of sync for a stock trade system: even a millisecond deviation might result in a difference of millions of dollars when executing a trade. Firestore handles attributes marked with &lt;code&gt;@ServerTiemstamp&lt;/code&gt; as follows: if the attribute is &lt;code&gt;nil&lt;/code&gt; when you store it (using &lt;code&gt;addDocument&lt;/code&gt;, for example), Firestore will populate the field with the current server timestamp at the time of writing it into the database. If the field is not &lt;code&gt;nil&lt;/code&gt; when you call &lt;code&gt;addDocument()&lt;/code&gt; or &lt;code&gt;updateData()&lt;/code&gt;, Firestore will leave the attribute value untouched. This way, it is easy to implement fields like &lt;code&gt;createdAt&lt;/code&gt; and &lt;code&gt;lastUpdatedAt&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Where to go from here&lt;/h2&gt;
&lt;p&gt;Today, you learned how to simplify your data mapping code by using the &lt;code&gt;Codable&lt;/code&gt; protocol with Firestore. Next time, we&apos;re going to take a closer look at saving, updating, and deleting data.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and make sure to check out the &lt;a href=&quot;https://www.youtube.com/user/Firebase/featured&quot;&gt;Firebase channel&lt;/a&gt; on YouTube.&lt;/p&gt;
</content:encoded></item><item><title>SwiftUI: Fetching Data from Firestore in Real Time</title><link>https://peterfriese.dev/blog/2020/swiftui-firebase-fetch-data/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/swiftui-firebase-fetch-data/</guid><description>Learn how to fetch data from Firestore in a SwiftUI Application</description><pubDate>Wed, 22 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;&lt;/p&gt;
&lt;p&gt;SwiftUI is an exciting new way to build UIs for iOS and Apple&apos;s other platforms. By using a declarative syntax, it allows developers to separate concerns and focus on what&apos;s relevant.&lt;/p&gt;
&lt;p&gt;As with everything new, it sometimes takes a while to get used to how things are done in the new world. I often see people try to cram too many things into their SwiftUI views, resulting in less readable code that doesn&apos;t work well.&lt;/p&gt;
&lt;p&gt;It&apos;s time to shake off the old habits of building MVCs (massive view controllers), folks!&lt;/p&gt;
&lt;p&gt;In this blog post I&apos;m going to show you a clean way to connect your SwiftUI app to Firebase and fetch data from Cloud Firestore.&lt;/p&gt;
&lt;h2&gt;The sample app&lt;/h2&gt;
&lt;p&gt;To get us started, consider the following app which displays a list of books:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Book: Identifiable {
  var id: String = UUID().uuidString
  var title: String
  var author: String
  var numberOfPages: Int
}


let testData = [
  Book(title: &quot;The Ultimate Hitchhiker&apos;s Guide to the Galaxy: Five Novels in One Outrageous Volume&quot;, author: &quot;Douglas Adams&quot;, numberOfPages: 815),
  Book(title: &quot;Changer&quot;, author: &quot;Matt Gemmell&quot;, numberOfPages: 474),
  Book(title: &quot;Toll&quot;, author: &quot;Matt Gemmell&quot;, numberOfPages: 474)
]

struct BooksListView: View {
  var books = testData

  var body: some View {
    NavigationView {
      List(books) { book in
        VStack(alignment: .leading) {
          Text(book.title)
            .font(.headline)
          Text(book.author)
            .font(.subheadline)
          Text(&quot;\(book.numberOfPages) pages&quot;)
            .font(.subheadline)
        }
      }
      .navigationBarTitle(&quot;Books&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hard to believe, but in less than 40 lines of code, we&apos;re able to not only define a simple data model for books, as well as some sample data, but also a screen that displays the books in a list - that&apos;s the power of SwiftUI!&lt;/p&gt;
&lt;h2&gt;Adding Firebase&lt;/h2&gt;
&lt;p&gt;To fetch data from Firestore, you&apos;ll first have to connect your app to Firebase. I&apos;m not going to go into great detail about this here - if you&apos;re new to Firebase (or your background is in Android or web development), check out this video which will walk you through the process (don&apos;t worry, it&apos;s not very complicated).&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://www.youtube.com/watch?v=F9Gs_pfT3hs&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;How to store our books in Firestore&lt;/h2&gt;
&lt;p&gt;Data in Firestore is stored in documents that contain a number of attributes. Documents are stored in collections. You cannot nest collections, but a document can contain collections, making it possible to create hierarchical data structures.&lt;/p&gt;
&lt;p&gt;For our application, we will just use a simple collection named &lt;code&gt;books&lt;/code&gt;, which contains a number of documents that will represent our books. Each document will be made up of the following attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt;: &lt;code&gt;string&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;author&lt;/code&gt;: &lt;code&gt;string&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pages&lt;/code&gt;: &lt;code&gt;number&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fetching data and subscribing to updates&lt;/h2&gt;
&lt;p&gt;Firestore does support one-time fetch queries, but it really shines when you use its realtime synchronisation to update data on any connected device. No longer will you have to implement pull-to-refresh - all data is kept in sync on all devices all of the time! Even better: the Firestore SDKs provide offline support out of the box, so all changes that the user made while their device was offline will automatically be synced back to Cloud Firestore once the device comes back online again.&lt;/p&gt;
&lt;p&gt;Implementing all of this isn&apos;t even particularly complicated - quite the opposite, as you will see. Offline support is enabled by default, and implementing real-time sync is a matter of registering a &lt;a href=&quot;https://firebase.google.com/docs/firestore/query-data/listen&quot;&gt;snapshot listener&lt;/a&gt; to a Firestore collection you&apos;re interested in.&lt;/p&gt;
&lt;p&gt;The boilerplate for registering a snapshot listener for the books collection in our sample app looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  Firestore.firestore().collection(&quot;books&quot;).addSnapshotListener { (querySnapshot, error) in
    guard let documents = querySnapshot?.documents else {
      print(&quot;No documents&quot;)
      return
    }

    documents.map { queryDocumentSnapshot -&amp;gt; Book in
      // map document to Book instance here
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But where&apos;s a good place to put the snapshot listener? After everything I told you about massive view controllers, you&apos;re probably guessing it won&apos;t be in the view itself - and you&apos;re absolutely right!&lt;/p&gt;
&lt;h2&gt;Implementing the view model&lt;/h2&gt;
&lt;p&gt;A good way to keep our views clean and lean is to use an MVVM (Model, View, View Model) architecture. We&apos;ve already got the view (&lt;code&gt;BooksListView&lt;/code&gt;) and model (&lt;code&gt;Book&lt;/code&gt;), so all that&apos;s missing is the view model.&lt;/p&gt;
&lt;p&gt;The primary responsibility of the view model is to provide access to the data we want to display in our UI. This is typically done in a view-specific way: we might want to display the list of books in two different ways: a list view and a cover flow. The list might display more details about each book (such as number of pages as well as the author&apos;s name), whereas the cover flow would display gorgeous book covers, with just the book title. Both views need different attributes from the model, so the view models would expose different attributes (and the view model for the cover flow might also include code to fetch large book cover art).&lt;/p&gt;
&lt;p&gt;A secondary responsibility of the view model is the provisioning of the data. In our simple application, we will handle data access directly in the view model, but in a more complex application I&apos;d definitely recommend extracting this into a store or repository to make data access reusable across multiple view models.&lt;/p&gt;
&lt;p&gt;To be able to use the view model in a SwiftUI view and subscribe to the data it manages, it needs to implement the &lt;code&gt;ObservableObject&lt;/code&gt; protocol, and all properties that we want our UI to be able to listen to need to be flagged using the &lt;code&gt;@Published&lt;/code&gt; property wrapper.&lt;/p&gt;
&lt;p&gt;Here is the boilerplate for our view model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Foundation
import FirebaseFirestore

class BooksViewModel: ObservableObject {
  @Published var books = [Book]()

  // code to fetch data
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, we can now add the code for registering the snapshot listener, and map the documents we receive into &lt;code&gt;Book&lt;/code&gt; instances:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Foundation
import FirebaseFirestore

class BooksViewModel: ObservableObject {
  @Published var books = [Book]()

  private var db = Firestore.firestore()

  func fetchData() {
    db.collection(&quot;books&quot;).addSnapshotListener { (querySnapshot, error) in
      guard let documents = querySnapshot?.documents else {
        print(&quot;No documents&quot;)
        return
      }

      self.books = documents.map { queryDocumentSnapshot -&amp;gt; Book in
        let data = queryDocumentSnapshot.data()
        let title = data[&quot;title&quot;] as? String ?? &quot;&quot;
        let author = data[&quot;author&quot;] as? String ?? &quot;&quot;
        let numberOfPages = data[&quot;pages&quot;] as? Int ?? 0

        return Book(id: .init(), title: title, author: author, numberOfPages: numberOfPages)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using the ViewModel in a SwiftUI view&lt;/h2&gt;
&lt;p&gt;Back in the BooksListView, we need to make two changes to use &lt;code&gt;BooksViewModel&lt;/code&gt; instead of the static list of books:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct BooksListView: View {
  @ObservedObject var viewModel = BooksViewModel() // (/1)

  var body: some View {
    NavigationView {
      List(viewModel.books) { book in // (2)
        VStack(alignment: .leading) {
          Text(book.title)
            .font(.headline)
          Text(book.author)
            .font(.subheadline)
          Text(&quot;\(book.numberOfPages) pages&quot;)
            .font(.subheadline)
        }
      }
      .navigationBarTitle(&quot;Books&quot;)
      .onAppear() { // (3)
        self.viewModel.fetchData()
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using the &lt;code&gt;@ObservedObject&lt;/code&gt; property wrapper (1), we tell SwiftUI to subscribe to the view model and invalidate (and re-render) the view whenever the observed object changes. And finally, we can connect the List view to the &lt;code&gt;books&lt;/code&gt; property on the view model (2), and get rid of the local &lt;code&gt;book&lt;/code&gt; array. Once the view appears, we can tell the view model to subscribe to the collection. Any changes that the user (and anyone else) makes to the &lt;code&gt;books&lt;/code&gt; collection in Firestore will now be reflected in the app&apos;s UI in realtime.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With just a few lines of code, we&apos;ve managed to connect an existing app to Firebase, subscribe to a collection of documents in Firestore and display any updates in real time.&lt;/p&gt;
&lt;p&gt;The key for a clean architecture is to extract the data access logic into a dedicated view model, and harness SwiftUI&apos;s and Combine&apos;s power to drive UI updates effortlessly.&lt;/p&gt;
&lt;h2&gt;Where to go from here&lt;/h2&gt;
&lt;p&gt;If you&apos;d like to learn more about Firestore, check out the &lt;a href=&quot;https://www.youtube.com/playlist?list=PLl-K7zZEsYLluG5MCVEzXAQ7ACZBCuZgZ&quot;&gt;Get to know Cloud Firestore series&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/toddkerpelman&quot;&gt;@ToddKerpelman&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Interested in seeing how to build a real world application using Firestore and SwiftUI? Check out my multi-part blog post series in which I rebuild the iOS Reminders app using SwiftUI and Firebase:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;../replicating-reminder-swiftui-firebase-part1/&quot;&gt;Replicating the iOS Reminders App using SwiftUI, Combine, and Firebase (Part 1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;../replicating-reminder-swiftui-firebase-part2/&quot;&gt;Replicating the iOS Reminders App, Part 2: Connecting SwiftUI and Cloud Firestore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;../replicating-reminder-swiftui-firebase-part3/&quot;&gt;Replicating the iOS Reminders App, Part 3: Sign in with Apple using SwiftUI and Firebase&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next week, we&apos;re going to look into how to make mapping &lt;a href=&quot;https://firebase.google.com/docs/reference/swift/firebasefirestore/api/reference/Classes/DocumentSnapshot&quot;&gt;DocumentSnapshot&lt;/a&gt;s easier and more type-safe using the &lt;a href=&quot;https://developer.apple.com/documentation/swift/codable&quot;&gt;Codable&lt;/a&gt; protocol - stay tuned!&lt;/p&gt;
</content:encoded></item><item><title>Sign in with Apple using SwiftUI and Firebase</title><link>https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part3/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part3/</guid><description>Learn how to add Sign in with Apple to a to-do list application built using SwiftUI and Firebase</description><pubDate>Tue, 24 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;This article is part of a series of articles that explores building a real-world application using SwiftUI, Firebase, and a couple of other technologies.&lt;/p&gt;
&lt;p&gt;Here is an overview of the series and what we&apos;re going to build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;&lt;a href=&quot;replicating-reminder-swiftui-firebase-part1&quot;&gt;part 1&lt;/a&gt;&lt;/strong&gt; of the series, we focussed on building the UI with SwiftUI, using a simple data model.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;&lt;a href=&quot;replicating-reminder-swiftui-firebase-part2/&quot;&gt;part 2&lt;/a&gt;&lt;/strong&gt;, we connected the application to Firebase, and synchronized the user&apos;s tasks with Cloud Firestore&lt;/li&gt;
&lt;li&gt;In part 3 (which you are reading right now), we&apos;re going to implement &lt;em&gt;Sign in with Apple&lt;/em&gt;, allowing users to sign in from multiple devices to access their data, and laying the groundwork for advanced features such as sharing tasks with your family and co-workers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, let&apos;s get started!&lt;/p&gt;
&lt;h2&gt;Why are we doing this?&lt;/h2&gt;
&lt;p&gt;Before we dive into implementing Sign in with Apple, let&apos;s take a step back and look at what we did in the past episode. So far, users of our application can enter new tasks, mark them as done, or modify them. All tasks the user enters are synchronised with Cloud Firestore.&lt;/p&gt;
&lt;p&gt;We&apos;re using Firebase Anonymous Authentication to get a unique ID for each user to make sure we can store data per user, and don&apos;t accidentally mix up the tasks of person A and B (I&apos;m pretty sure my kids would love to see their chores end up on my task list, but generally, this is not how task lists work - sorry to break the bad news to you...).&lt;/p&gt;
&lt;p&gt;Now you might be wondering how &lt;a href=&quot;https://firebase.google.com/docs/auth/ios/anonymous-auth&quot;&gt;Anonymous Auth&lt;/a&gt; determines this unique user ID, and some of you might suspect Firebase is using the device ID or some other user- or device-specific feature to compute the user ID. For a number of reasons, this is not what Firebase does. Instead, when you ask Firebase Auth to sign in a user anonymously, the SDK calls an &lt;a href=&quot;https://firebase.google.com/docs/reference/rest/auth#section-sign-in-anonymously&quot;&gt;API endpoint&lt;/a&gt; that returns a globally unique ID. This ID will then be stored in the local keychain, to make it easier to transparently sign in the user when they return to the app.&lt;/p&gt;
&lt;p&gt;This works great for users who only use one device, but it has one significant drawback: you will be assigned a new user ID every time you sign in to the app on a new device. Why is that?&lt;/p&gt;
&lt;p&gt;Essentially, what happens is this: when calling &lt;code&gt;Auth.auth().signInAnonymously()&lt;/code&gt;, the Firebase SDK will check the iOS keychain to see if the user has already signed in. If so, it will use this ID to sign in the user transparently. If not, it will call the API endpoint to generate a new user ID.&lt;/p&gt;
&lt;p&gt;This means: you will appear to be a new user whenever you launch the app on a different device. That&apos;s bad news for all of us who would like to use multiple devices to manage their tasks, e.g. their iPhone when they&apos;re on the go, or their iPad when chilling out at home (I hear WFH seems to be rather popular these days...).&lt;/p&gt;
&lt;p&gt;Thankfully, with Firebase it is easy to upgrade an anonymous user to a permanent user, making it possible to support use cases like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;accessing the same data from different devices (and, of course, operating systems)&lt;/li&gt;
&lt;li&gt;sharing tasks with other users (e.g. to set up a shared shopping list)&lt;/li&gt;
&lt;li&gt;sending users an email every morning with an overview of their due tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Firebase Authentication supports a wide range of sign-in providers, such as Email/Password, Phone Number, Google Sign-in, Facebook Login, Twitter Login, or Sign in with Apple. If none of the default providers match your needs, you can even implement your own provider and plug it into Firebase Auth using &lt;a href=&quot;https://firebase.google.com/docs/auth/ios/custom-auth&quot;&gt;Custom Auth&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What we&apos;re going to cover&lt;/h2&gt;
&lt;p&gt;In this article, we&apos;re going to specifically look into how to implement Sign in with Apple and how to connect it to Firebase Authentication. I will show how to integrate with other Sign-in providers in subsequent articles. A lot has been written about Sign in with Apple already, so we will gloss over some of the basic parts, but I will link to other articles in the resources section, so if you would like to read up on the basics, I&apos;d encourage you to do so.&lt;/p&gt;
&lt;p&gt;Integrating Sign in with Apple with SwiftUI (which is the UI technology I decided to use for our demo app) has a number of challenges which we will cover in this article.&lt;/p&gt;
&lt;p&gt;Specifically, we&apos;ll look at the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to implement the Sign in with Apple button in SwiftUI&lt;/li&gt;
&lt;li&gt;How to handle the authorization flow and how to implement &lt;code&gt;ASAuthorizationControllerDelegate&lt;/code&gt; in a SwiftUI application&lt;/li&gt;
&lt;li&gt;How to upgrade an anonymous user to a permanent user&lt;/li&gt;
&lt;li&gt;What happens with the user&apos;s data when upgrading an anonymous user&lt;/li&gt;
&lt;li&gt;What happens if you&apos;re trying to link to an account that has already been upgraded&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Basic setup&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_4/sign_in_with_apple/start&quot;&gt;&lt;code&gt;stage_4/sign_in_with_apple/start&lt;/code&gt;&lt;/a&gt; and open &lt;code&gt;MakeItSo.xcworkspace&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To use Sign in with Apple with Firebase Auth, there are a few preliminary steps you need to take:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First of all, add the Sign in with Apple capability to your Xcode project
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Firebase Console, enable Sign in with Apple in the Authentication section
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(In case you haven&apos;t already done this) make sure to add your iOS app to your Firebase project. We already did this in &lt;a href=&quot;../replicating-reminder-swiftui-firebase-part2/&quot;&gt;part 2&lt;/a&gt; of the series, which also contains a link to a video I made about this. Check it out &lt;a href=&quot;https://www.youtube.com/watch?v=mPBBNi2Ou6o&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that out of the way, let&apos;s write some code!&lt;/p&gt;
&lt;h2&gt;Implementing the Sign in with Apple button&lt;/h2&gt;
&lt;p&gt;Apple is very specific about how the Sign in with Apple button should look like (see the &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/&quot;&gt;HIG&lt;/a&gt;). To make things easier, they&apos;ve provided &lt;code&gt;ASAuthorizationAppleIDButton&lt;/code&gt;, which goes a long way: for example, the button uses Apple-approved fonts and icons, maintains ideal content dimensions, and (maybe most importantly) supports internationalisation right out of the box. So instead of creating a &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/&quot;&gt;custom button&lt;/a&gt; (which is explained in the same HIG document a bit further down), let&apos;s use the Apple-provided class.&lt;/p&gt;
&lt;p&gt;However, there is no SwiftUI equivalent of &lt;code&gt;ASAuthorizationAppleIDButton&lt;/code&gt;, so we&apos;ll have to use &lt;code&gt;UIViewRepresentable&lt;/code&gt; to wrap it. We&apos;ll also want to make sure that the button automatically adjusts to light/dark mode.&lt;/p&gt;
&lt;p&gt;Create a new file &lt;code&gt;SignInWithAppleButton.swift&lt;/code&gt; and paste the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI
import AuthenticationServices

// Implementation courtesy of https://stackoverflow.com/a/56852456/281221
struct SignInWithAppleButton: View {
  @Environment(\.colorScheme) var colorScheme: ColorScheme // (1)
  
  var body: some View {
    Group {
      if colorScheme == .light { // (2)
        SignInWithAppleButtonInternal(colorScheme: .light)
      }
      else {
        SignInWithAppleButtonInternal(colorScheme: .dark)
      }
    }
  }
}

fileprivate struct SignInWithAppleButtonInternal: UIViewRepresentable { // (3)
  var colorScheme: ColorScheme
  
  func makeUIView(context: Context) -&amp;gt; ASAuthorizationAppleIDButton {
    switch colorScheme {
    case .light:
      return ASAuthorizationAppleIDButton(type: .signIn, style: .black) // (4)
    case .dark:
      return ASAuthorizationAppleIDButton(type: .signIn, style: .white) // (5)
    @unknown default:
      return ASAuthorizationAppleIDButton(type: .signIn, style: .black) // (6)
    }
  }
  
  func updateUIView(_ uiView: ASAuthorizationAppleIDButton, context: Context) {
  }
}

struct SignInWithAppleButton_Previews: PreviewProvider {
  static var previews: some View {
    SignInWithAppleButton()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few quick remarks about this specific implementation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We subscribe to the current &lt;code&gt;colorScheme&lt;/code&gt; using an &lt;code&gt;Environment&lt;/code&gt; object (1)&lt;/li&gt;
&lt;li&gt;This allows us to force SwiftUI to re-create the button whenever the color scheme changes - see (2)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SignInWithAppleButtonInternal&lt;/code&gt; is the class that actually implements &lt;code&gt;UIViewRepresentable&lt;/code&gt; (3)&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;makeUIView&lt;/code&gt;, we create an instance of &lt;code&gt;ASAuthorizationAppleIDButton&lt;/code&gt; according to the current colorScheme (4, 5, 6)&lt;/li&gt;
&lt;li&gt;As we don&apos;t expose any properties that would require us to update the button whenever they change, there is no need to implement &lt;code&gt;updateUIView&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can now use the Sign in with Apple button like any other SwiftUI view. Let&apos;s create a simple sign-up screen that explains the benefits of signing up to the user, and add the Sign in with Apple button to the bottom of the screen. In a later episode in this series, we&apos;ll add more buttons to allow the user to sign in with other authentication providers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI
import Firebase
import AuthenticationServices
import CryptoKit

struct SignInView: View {
  @Environment(\.window) var window: UIWindow?
  @Environment(\.presentationMode) var presentationMode: Binding&amp;lt;PresentationMode&amp;gt;
  
  @State var signInHandler: SignInWithAppleCoordinator?
  
  var body: some View {
    VStack {
      Text(&quot;Create an account to save your tasks and access them anywhere.&quot;)
        .font(.headline)
        .fontWeight(.medium)
        .multilineTextAlignment(.center)
        .padding(.top, 20)

      Spacer()

      SignInWithAppleButton()
        .frame(width: 280, height: 45)
        .onTapGesture { // (1)
          self.signInWithAppleButtonTapped() // (2)
      }

      // other buttons will go here
    }
  }

  func signInWithAppleButtonTapped() {
    signInHandler = SignInWithAppleCoordinator(window: self.window)
    signInHandler?.link { (user) in
      print(&quot;User signed in \(user.uid)&quot;)
      self.presentationMode.wrappedValue.dismiss() // (3)
    }
  }
}

struct SignInView_Previews: PreviewProvider {
  static var previews: some View {
    SignInView()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the user taps (1) on the Sign in with Apple button, we will kick off (2) the authentication flow. Once the flow finishes, we will dismiss the sign-in view and return to the parent view (3).&lt;/p&gt;
&lt;p&gt;All this isn&apos;t terribly exciting yet, so let&apos;s take a closer look at the sign-in flow itself.&lt;/p&gt;
&lt;h2&gt;Handling the Sign in with Apple flow&lt;/h2&gt;
&lt;p&gt;Handling the Sign in with Apple flow is a three-step process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First, we need to create an &lt;code&gt;ASAuthorizationAppleIDRequest&lt;/code&gt; that contains the scopes we&apos;re interested in as well as some security-specific fields&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, we need to create an &lt;code&gt;ASAuthorizationController&lt;/code&gt;, pass in the request, and call &lt;code&gt;performRequests()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ASAuthorizationController&lt;/code&gt; will do all the heavy lifting of interacting with the user. Once it has navigated the user through the process of signing in, it will invoke one of the callbacks:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;didCompleteWithAuthorization&lt;/code&gt;: if things went well&lt;/li&gt;
&lt;li&gt;&lt;code&gt;didCompleteWithError&lt;/code&gt;: if there was an issue&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&apos;s dive in a little deeper and see what&apos;s going on in each of these steps!&lt;/p&gt;
&lt;h3&gt;Coordinating the Sign-in Flow&lt;/h3&gt;
&lt;p&gt;In a UIKit application, you might put most of the code for handling Sign in with Apple in a view controller. With SwiftUI, this isn&apos;t possible, as we do not have a view controller. This actually is a good thing (tm), as it will prevent us from ending up with a &lt;em&gt;Massive View Controller&lt;/em&gt; (MVC). Instead, let&apos;s create a class &lt;code&gt;SignInWithAppleCoordinator&lt;/code&gt; and a couple of extensions to organize the code in a more maintainable way.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum SignInState: String {
  case signIn
  case link
  case reauth
}

class SignInWithAppleCoordinator: NSObject {
  @LazyInjected private var taskRepository: TaskRepository
  @LazyInjected private var authenticationService: AuthenticationService // (1)
  
  private weak var window: UIWindow!
  private var onSignedIn: ((User) -&amp;gt; Void)? // (2)

  private var currentNonce: String? // (3)
  
  init(window: UIWindow?) {
    self.window = window
  }

  // more code to follow

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let me explain the code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AuthenticationService&lt;/code&gt; (1) is a class we implemented in &lt;a href=&quot;../replicating-reminder-swiftui-firebase-part2/&quot;&gt;part 2 of this series&lt;/a&gt; - it manages the sign-in flow and keeps track of the currently signed in user.&lt;/li&gt;
&lt;li&gt;We define a callback (2) that we store in &lt;code&gt;onSignedIn&lt;/code&gt; so that we can inform the caller when the user has signed in.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;currentNonce&lt;/code&gt; (3) holds a &lt;a href=&quot;https://en.wikipedia.org/wiki/Cryptographic_nonce&quot;&gt;cryptographic nonce&lt;/a&gt; - a value that can be used exactly once to prove that the request was actually sent by the current client. More on this in a minute.&lt;/li&gt;
&lt;li&gt;We hold on to the &lt;code&gt;window&lt;/code&gt; that owns the current view - we will need to return this in our implementation of &lt;code&gt;ASAuthorizationControllerPresentationContextProviding&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Creating the request&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ASAuthorizationController&lt;/code&gt; is the core class handling the authorization flow for Sign in with Apple. Since it is capable of handling different types of authorization flows (password, single sign-on, and signing in with an Apple ID), it needs to be configured using an &lt;code&gt;ASAuthorizationRequest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We do this in a helper method, &lt;code&gt;appleIDRequest&lt;/code&gt;, by creating &lt;code&gt;ASAuthorizationAppleIDRequest&lt;/code&gt; (which is a subclass of &lt;code&gt;ASAuthorizationRequest&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignInWithAppleCoordinator: NSObject {
  // ...
  private func appleIDRequest(withState: SignInState) -&amp;gt; ASAuthorizationAppleIDRequest {
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    let request = appleIDProvider.createRequest() // (1)
    request.requestedScopes = [.fullName, .email] // (2)
    request.state = withState.rawValue //(3)

    let nonce = randomNonceString() // (4)
    currentNonce = nonce
    request.nonce = sha256(nonce)

    return request
  }

  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After creating the request using &lt;code&gt;ASAuthorizationAppleIDProvider.createRequest()&lt;/code&gt; (1), we configure a couple of things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, we indicate that we&apos;re interested in the user&apos;s full name and their email address by providing the &lt;code&gt;.fullName&lt;/code&gt; and &lt;code&gt;.email&lt;/code&gt; scopes (2).&lt;/li&gt;
&lt;li&gt;We also pass in a &lt;code&gt;SignInState&lt;/code&gt; (3) - this is an enum that indicates which type of authorization flow we&apos;re using (signing up, logging in, re-authenticating).&lt;/li&gt;
&lt;li&gt;As briefly mentioned above, the &lt;code&gt;nonce&lt;/code&gt; (4) is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Cryptographic_nonce&quot;&gt;one-time key&lt;/a&gt;: we will compare the nonce in the private field &lt;code&gt;currentNonce&lt;/code&gt; to the nonce we receive in &lt;code&gt;didCompleteWithAuthorization&lt;/code&gt; to verify that it was actually us who sent the request.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the code to generate the nonce and compute it&apos;s secure hash code (using the &lt;a href=&quot;https://en.wikipedia.org/wiki/SHA-2&quot;&gt;SHA-256 algorithm&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
private func randomNonceString(length: Int = 32) -&amp;gt; String {
  precondition(length &amp;gt; 0)
  let charset: Array&amp;lt;Character&amp;gt; =
      Array(&quot;0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._&quot;)
  var result = &quot;&quot;
  var remainingLength = length

  while remainingLength &amp;gt; 0 {
    let randoms: [UInt8] = (0 ..&amp;lt; 16).map { _ in
      var random: UInt8 = 0
      let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &amp;amp;random)
      if errorCode != errSecSuccess {
        fatalError(&quot;Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)&quot;)
      }
      return random
    }

    randoms.forEach { random in
      if remainingLength == 0 {
        return
      }

      if random &amp;lt; charset.count {
        result.append(charset[Int(random)])
        remainingLength -= 1
      }
    }
  }

  return result
}

@available(iOS 13, *)
private func sha256(_ input: String) -&amp;gt; String {
  let inputData = Data(input.utf8)
  let hashedData = SHA256.hash(data: inputData)
  let hashString = hashedData.compactMap {
    return String(format: &quot;%02x&quot;, $0)
  }.joined()

  return hashString
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;To sign in or to link, that is the question&lt;/h3&gt;
&lt;p&gt;Usually, when you authenticate a user, you will use &lt;code&gt;Auth.auth().signIn()&lt;/code&gt; to sign them in to Firebase.&lt;/p&gt;
&lt;p&gt;In our case, however, we&apos;re in a different situation: the user already signed in - using Firebase Anonymous Authentication! This means we already have a &lt;code&gt;User&lt;/code&gt; instance, and the user potentially already has created a number of to-do items that are associated with the anonymous user&apos;s &lt;code&gt;userID&lt;/code&gt; in our Cloud Firestore instance. If we were to use  the &lt;code&gt;signIn()&lt;/code&gt; method to sign in to another account, we&apos;d end up with orphaned data, i.e. all those documents would still be stored in Firestore, but they&apos;d be assigned to an anonymous account that the user no longer can sign in to. Thus, the user would lose access to them.&lt;/p&gt;
&lt;p&gt;Instead, we will use a Firebase Authentication feature called &lt;a href=&quot;https://firebase.google.com/docs/auth/ios/account-linking&quot;&gt;Account Linking&lt;/a&gt; to upgrade the user&apos;s anonymous account into a permanent account based on their Apple ID.&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
Now I should give you a word of warning: Apple requires that you get the user&apos;s consent when you&apos;re linking their Apple ID with any directly identifiable personal information such as their email address or phone number. This applies when you&apos;re linking to an authentication provider such as Facebook Login, Google Sign-in, or Email/Password Auth. However, as an anonymous user is anonymous (duh) and doesn&apos;t contain any personal identifiable information, there&apos;s no need for us to get the user&apos;s consent.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To kick off the linking flow, we call the &lt;code&gt;link()&lt;/code&gt; method on our &lt;code&gt;SignInWithAppleCoordinator&lt;/code&gt; class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignInWithAppleCoordinator: NSObject {
  // ...
  func link(onSignedIn: @escaping (User) -&amp;gt; Void) {
    self.onSignedIn = onSignedIn // (1)
    
    let request = appleIDRequest(withState: .link)
    let authorizationController = ASAuthorizationController(authorizationRequests: [request])
    authorizationController.delegate = self // (2)
    authorizationController.presentationContextProvider = self // (3)
    authorizationController.performRequests()
  }
  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;re storing the callback (which is implemented as a trailing closure) in the &lt;code&gt;onSignedIn&lt;/code&gt; property (1) so we can inform the caller about the result of the authentication flow. Also note that both the &lt;code&gt;delegate&lt;/code&gt; (2) and the &lt;code&gt;presentationContextProvider&lt;/code&gt; (3) are set to &lt;code&gt;self&lt;/code&gt;, so we can handle all of &lt;code&gt;ASAuthorizationController&lt;/code&gt;&apos;s callbacks in one spot.&lt;/p&gt;
&lt;h3&gt;Handling the callbacks&lt;/h3&gt;
&lt;p&gt;Depending on the events that happen while the user interacts with the &lt;code&gt;ASAuthorizationController&lt;/code&gt;, we will receive some callbacks. Let&apos;s take a look at what might happen.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension SignInWithAppleCoordinator: ASAuthorizationControllerDelegate {
  func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
      guard let nonce = currentNonce else { // (1)
        fatalError(&quot;Invalid state: A login callback was received, but no login request was sent.&quot;)
      }
      guard let appleIDToken = appleIDCredential.identityToken else { // (2)
        print(&quot;Unable to fetch identity token&quot;)
        return
      }
      guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else { // (3)
        print(&quot;Unable to serialise token string from data: \(appleIDToken.debugDescription)&quot;)
        return
      }
      guard let stateRaw = appleIDCredential.state, let state = SignInState(rawValue: stateRaw) else {
        print(&quot;Invalid state: request must be started with one of the SignInStates&quot;)
        return
      }

      // ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First of all, we&apos;ll need to perform some sanity and security checks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the credential contain a nonce (1)?&lt;/li&gt;
&lt;li&gt;Does the authorization response contain an ID token (2)?&lt;/li&gt;
&lt;li&gt;Does the authorization response contain the state we sent (3)?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all of the above holds true, we can continue by requesting Firebase&apos;s &lt;code&gt;OAuthProvider&lt;/code&gt; to mint a credential based on the authentication provider (&lt;code&gt;apple.com&lt;/code&gt;), the ID token and the nonce:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      // ...
      let credential = OAuthProvider.credential(withProviderID: &quot;apple.com&quot;,
                                                idToken: idTokenString,
                                                rawNonce: nonce)
      // ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on the type of authorization flow we&apos;re interested in, we will then either perform a regular sign-in by calling &lt;code&gt;Auth.auth().signIn()&lt;/code&gt;, or link the anonymous user account with the freshly minted credential by calling &lt;code&gt;Auth.auth().currentUser.link()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s look at the code for the linking flow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      // ...
      currentUser.link(with: credential) { (result, error) in // (1)
        if let error = error, (error as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue { // (2)
          print(&quot;The user you&apos;re signing in with has already been linked, signing in to the new user and migrating the anonymous users [\(currentUser.uid)] tasks.&quot;)

          if let updatedCredential = (error as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as? OAuthCredential {
            print(&quot;Signing in using the updated credentials&quot;)
            Auth.auth().signIn(with: updatedCredential) { (result, error) in
              if let user = result?.user {
                // TODO: handle data migration

                self.doSignIn(appleIDCredential: appleIDCredential, user: user) // (3)
              }
            }
          }
        }
        else if let error = error {
          print(&quot;Error trying to link user: \(error.localizedDescription)&quot;)
        }
        else {
          if let user = result?.user {
            self.doSignIn(appleIDCredential: appleIDCredential, user: user) // (3)
          }
        }
      }
    }
    // ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s quite a bit, so let&apos;s take a closer look at what&apos;s happening.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As we decided to upgrade the existing anonymous user to a permanent user (based on their Apple ID credentials), we&apos;re calling &lt;code&gt;currentUser.link(with: credential)&lt;/code&gt; (1).&lt;/li&gt;
&lt;li&gt;The result of that call either is an upgraded user or an error (2).&lt;/li&gt;
&lt;li&gt;The most common cause for an error is if the user is trying to link with an Apple ID that they&apos;ve already linked to. As developers, we will run into this situation all of the time, as we&apos;re obviously linking to the Apple ID of our own account or a test account (should you have one). I&apos;ll show you how to unlink your Apple ID from your application below to help you test all scenarios, but we need to gracefully deal with this situation. We&apos;ve got a couple of options, such as telling the user they cannot do this (bad), or signing in to the already linked account (better), or signing in to the already linked account and migrating any data the user might have in the anonymous user they&apos;re currently using (best). Because this is a rather involved topic, we&apos;re going to cover this is a separate post.&lt;/li&gt;
&lt;li&gt;If all goes well (i.e. the user hadn&apos;t linked their Apple ID before), the result of &lt;code&gt;currentUser.link(with: credentials)&lt;/code&gt; is a signed-in &lt;code&gt;User&lt;/code&gt; instance, representing the user&apos;s Apple ID. In this case, we can go ahead and call the callback provided when the caller kicked off the sign-in process (3).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
&lt;strong&gt;Unlinking your Apple ID from an app that uses Sign in with Apple&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once you&apos;ve signed in to your app, you cannot repeat the sign-in flow, which makes testing a bit of a challenge. To get back to the initial state, you need to disconnect your Apple ID from your app. Here is how:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to https://appleid.apple.com and sign in using your Apple ID&lt;/li&gt;
&lt;li&gt;In the Security section, find Apps &amp;amp; Websites using Apple ID and click on Manage...&lt;/li&gt;
&lt;li&gt;You will see a pop-up dialog that shows you all apps that are connected to your Apple ID
&lt;/li&gt;
&lt;li&gt;Click on the name of the app you want to disconnect&lt;/li&gt;
&lt;li&gt;The following dialog will tell you when you first started using your Apple ID with this application
&lt;/li&gt;
&lt;li&gt;Click on Stop using Apple ID to disconnect this app from your Apple ID&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next time you open that app, you will need to sign in again.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h3&gt;Retrieving the user&apos;s display name&lt;/h3&gt;
&lt;p&gt;Many apps provide a more personalised experience for their signed in users, for example by displaying their name on the profile screen.&lt;/p&gt;
&lt;p&gt;Sign in with Apple will provide the user details you requested using the scopes only the first time a user signs in to your app with their Apple ID. This is a design decision Apple made to ensure developers take privacy concerns seriously.&lt;/p&gt;
&lt;p&gt;Firebase automatically stores the opaque user identifier Apple provides in the Firebase user that is created when you invoke &lt;code&gt;currentUser.link()&lt;/code&gt; or &lt;code&gt;Auth.auth().signIn()&lt;/code&gt;. The same applies to the user&apos;s email address. Keep in mind that if the user decides to use Apple&apos;s anonymous email relay, the user&apos;s email address will look like this: &lt;code&gt;&amp;lt;random-identifier&amp;gt;@privaterelay.appleid.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The user&apos;s full name is &lt;strong&gt;not&lt;/strong&gt; automatically stored in the user object that Firebase creates for a linked user. This is a &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk/issues/4393#issuecomment-567148801&quot;&gt;design decision that the Firebase team made&lt;/a&gt; to prevent developers from accidentally violating Apple&apos;s anonymised data requirements.&lt;/p&gt;
&lt;p&gt;As our to-do-list app does not support linking with any other identity provider other than Sign in with Apple, we won&apos;t be running into this situation. So let&apos;s store the user&apos;s full name into the Firebase user object ourselves:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension SignInWithAppleCoordinator: ASAuthorizationControllerDelegate {
  // ...
  private func doSignIn(appleIDCredential: ASAuthorizationAppleIDCredential, user: User) {
    if let fullName = appleIDCredential.fullName {
      if let givenName = fullName.givenName, let familyName = fullName.familyName {
        let displayName = &quot;\(givenName) \(familyName)&quot;
        self.authenticationService.updateDisplayName(displayName: displayName) { result in // (1)
          switch result {
          case .success(let user):
            print(&quot;Succcessfully update the user&apos;s display name: \(String(describing: user.displayName))&quot;)
          case .failure(let error):
            print(&quot;Error when trying to update the display name: \(error.localizedDescription)&quot;)
          }
          self.callSignInHandler(user: user)
        }
      }
      else {
        self.callSignInHandler(user: user)
      }
    }
  }
  
  private func callSignInHandler(user: User) {
    if let onSignedInHandler = self.onSignedInHandler {
      onSignedInHandler(user)
    }
  }

}

class AuthenticationService: ObservableObject {
  // ...
  func updateDisplayName(displayName: String) { // (2)
    if let user = Auth.auth().currentUser {
      let changeRequest = user.createProfileChangeRequest() // (3)
      changeRequest.displayName = displayName
      changeRequest.commitChanges { error in // (4)
        if error != nil {
          print(&quot;Successfully updated display name for user [\(user.uid)] to [\(displayName)]&quot;)
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first retrieve the user&apos;s full name from the &lt;code&gt;ASAuthorizationAppleIDCredential&lt;/code&gt; we received when the Sign in with Apple flow completed successfully (1). Then, in &lt;code&gt;AuthenticationService.updateDisplayName&lt;/code&gt; (2), we create a &lt;code&gt;UserProfileChangeRequest&lt;/code&gt; (3), set the user name, and commit the changes to Firebase Authentication (4).&lt;/p&gt;
&lt;h2&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;At this stage, we&apos;ve got an authentication solution for our app that will allow users to start using the app without having to sign in. Once they want to use their to-do list on a second device, they can sign in to the app on the first device using Sign in with Apple. At this point, their anonymous user will be converted into a permanent account that is connected to the user&apos;s Apple ID via Sign in with Apple.&lt;/p&gt;
&lt;p&gt;As their Firebase user ID hasn&apos;t changed by this upgrade, all data they have previously entered using their anonymous account is still stored in Cloud Firestore, but is now accessible to their new, permanent account.&lt;/p&gt;
&lt;p&gt;This means they can now sign in to their account on a different device and access the same data. Thanks to Cloud Firestore&apos;s realtime capabilities, any change the users makes on one device will be synchronised to any other device they are signed in to and will be visible almost instantaneously.&lt;/p&gt;
&lt;p&gt;Here is a screencast showing two simulators side by side showing how this looks like:&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://www.youtube.com/watch?v=iqsj9Qb8DXw&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
Get the completed source code for the app by checking out the tag &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_4/sign_in_with_apple/end&quot;&gt;&lt;code&gt;stage_4/sign_in_with_apple/end&lt;/code&gt;&lt;/a&gt; and opening &lt;code&gt;MakeItSo.xcworkspace&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;h2&gt;Recommendation for implementing authentication for your app&lt;/h2&gt;
&lt;p&gt;User on-boarding is a critical moment in an app&apos;s lifecycle, and it&apos;s easy to screw up and lose users instead of retaining them. One of the biggest mistakes is to ask users to register for using your app before you&apos;ve given them the opportunity to experience the app and properly understand its value proposition. Asking users to sign in before they can start using your app puts up a pretty serious speed bump, and your potential users will ask themselves if they want to make a commitment to your app. If you haven&apos;t given them an incentive, they are very likely going to uninstall your app.&lt;/p&gt;
&lt;p&gt;Implementing a local data storage solution that allows users to work locally first and only later synchronise their data with your backend (which requires asking them to sign in) is a serious piece of work, and it&apos;s easy to see why a lot of developers would rather try to avoid having to implement this.&lt;/p&gt;
&lt;p&gt;By using Firebase Anonymous Authentication to transparently create a Firebase user account, and storing the users&apos; data in Firebase Realtime Database or Cloud Firestore, it becomes a lot easier to implement such a solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Firebase Anonymous Auth to sign in the user transparently&lt;/li&gt;
&lt;li&gt;Use their anonymous user ID to store data in Firebase RTDB or Cloud Firestore&lt;/li&gt;
&lt;li&gt;Once the user has understood the value proposition of your application, suggest creating an account&lt;/li&gt;
&lt;li&gt;Use one of the identity providers that Firebase supports (such as Sign in with Apple, Google Sign-in, or Facebook Login), or Email/Password authentication to let the user sign in&lt;/li&gt;
&lt;li&gt;The anonymous user will be upgraded to a permanent user with the same user ID. This means all existing data is safe and can now be used by the user on any of the devices they sign in to using their preferred authentication mechanism.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Firebase supports a number of authentication mechanisms, and you can mix and match which one you&apos;d like to support in your app. Please keep in mind though, that you absolutely must support the same set of authentication mechanisms on all platforms your app is available on.&lt;/p&gt;
&lt;p&gt;Otherwise, users might sign in using Apple on iOS, and later be stuck when they try to sign in to your application on Android, but you only support Google Sign-in on Android. Or worse - they signed in using an identity provider on one platform, but you only support Email/Password sign-in on your other platforms. How should they sign in to their (existing) account using a password if they didn&apos;t even choose a password in the first place?&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, you saw how to&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement Sign in with Apple&lt;/li&gt;
&lt;li&gt;Upgrade anonymous users to permanent users by using a mechanism called account linking&lt;/li&gt;
&lt;li&gt;Learned about some guiding principles to ensure a great UX for your on-boarding and authorisation flow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Implementing a solid authentication solution can be a challenging task, but I hope you have seen how Firebase can make this a lot easier for you by providing a solid framework for implementing authorisation solutions.&lt;/p&gt;
&lt;p&gt;Over the course of the past three articles, we&apos;ve managed to implement a fully functional to-do list application, but there is still a lot to do. Here are a couple of ideas for things that we will look into in the next episodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Migrating an anonymous user&apos;s data in case they already signed in with Apple on another device&lt;/li&gt;
&lt;li&gt;Showing the user&apos;s due tasks in a Today Extension&lt;/li&gt;
&lt;li&gt;Allowing users to add new tasks via a Siri Extension&lt;/li&gt;
&lt;li&gt;Sharing lists with other users&lt;/li&gt;
&lt;li&gt;Uploading attachments to tasks&lt;/li&gt;
&lt;li&gt;Properly implementing due dates (and sending users a notification for any tasks due today)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&apos;ve got any features you&apos;d like to see me implement, please let me know by sending me a tweet or filing an issue on the project&apos;s repository on GitHub.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/&quot;&gt;Source code for the sample app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/sign-in-with-apple/get-started/&quot;&gt;Apple&apos;s Getting Started Guide for Sign in with Apple&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/introduction/&quot;&gt;Human Interface Guidelines for Sign in with Apple&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple&quot;&gt;App Store Review Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple&quot;&gt;What the Heck is Sign In with Apple?&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/aaronpk&quot;&gt;Aaron Parecki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Connecting SwiftUI and Cloud Firestore</title><link>https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part2/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part2/</guid><description>Learn how to use SwiftUI and Firebase to build a real-world app</description><pubDate>Wed, 05 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;This article is part of a series of articles that explores building a real-world application using SwiftUI, Firebase, and a couple of other technologies.&lt;/p&gt;
&lt;p&gt;Here is an overview of the series and what we&apos;re going to build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;&lt;a href=&quot;../replicating-reminder-swiftui-firebase-part1/&quot;&gt;part 1&lt;/a&gt;&lt;/strong&gt; of the series, we focussed on building the UI with SwiftUI, using a simple data model.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;part 2&lt;/strong&gt; (which you are reading right now), we&apos;re going to connect the application to Firebase, and will synchronize the user&apos;s tasks with Cloud Firestore&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;part 3&lt;/strong&gt;, we will implement Sign in with Apple to turn the application into a real multi-user application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the &lt;a href=&quot;../replicating-reminder-swiftui-firebase-part1&quot;&gt;previous article&lt;/a&gt; of this series, we saw how easy it was to replicate the UI of a well-known iOS app using SwiftUI: writing very little code, we implemented a fully functional copy of the iOS Reminders app. To keep things simple, we persisted data on the user&apos;s device using the &lt;a href=&quot;https://github.com/saoudrizwan/Disk&quot;&gt;Disk&lt;/a&gt; framework.&lt;/p&gt;
&lt;p&gt;Today, we&apos;re going to look into what&apos;s required to connect this app to Firebase, allowing users to store their data in the cloud.&lt;/p&gt;
&lt;p&gt;There are many reasons for storing data in the cloud: your users might want to access their data from multiple devices, such as their phone and their tablet. Or, they might want to share data with their coworkers or family and friends.&lt;/p&gt;
&lt;p&gt;Sounds complicated and like a lot of work? Well, fear not - all of this is possible with Firebase and Cloud Firestore, and as you will see in this article, it&apos;s not even very complicated.&lt;/p&gt;
&lt;p&gt;So, let&apos;s get started!&lt;/p&gt;
&lt;h2&gt;Setting up Firebase&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_3/implement_firestore_repository/start&quot;&gt;&lt;code&gt;stage_3/implement_firestore_repository/start&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcworkspace&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To use Firebase in your app, you&apos;ll have to set up a Firebase project and connect your app to it. It takes just a few steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up a new Firebase project with the &lt;a href=&quot;https://console.firebase.google.com/&quot;&gt;Firebase console&lt;/a&gt; on the web&lt;/li&gt;
&lt;li&gt;Add the Firebase SDK to your app (I recommend using CocoaPods, as we&apos;re already using this to integrate other libraries such as the Disk framework)&lt;/li&gt;
&lt;li&gt;Download the &lt;code&gt;GoogleService-Info.plist&lt;/code&gt; configuration file which tells the Firebase SDK which of your Firebase projects to connect to&lt;/li&gt;
&lt;li&gt;Import Firebase into your code and initialise it in your application delegate&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;target &apos;MakeItSo&apos; do
  use_frameworks!

  # Pods for MakeItSo
  pod &apos;Resolver&apos;
  pod &apos;Disk&apos;, &apos;~&amp;gt; 0.6.4&apos;

  pod &apos;Firebase/Analytics&apos;
  pod &apos;Firebase/Firestore&apos;
  pod &apos;FirebaseFirestoreSwift&apos;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember, we added &lt;code&gt;Resolver&lt;/code&gt; and &lt;code&gt;Disk&lt;/code&gt; in part of this series.&lt;/p&gt;
&lt;p&gt;Coincidentally, I just created a short video that explains how this works in more detail:&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://www.youtube.com/watch?v=F9Gs_pfT3hs&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;What is Cloud Firestore, anyway?&lt;/h2&gt;
&lt;p&gt;Let&apos;s quickly review what Cloud Firestore is to understand why it is a good fit for our project.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://firebase.google.com/products/firestore&quot;&gt;product website&lt;/a&gt; says that &quot;Cloud Firestore is a NoSQL document database that lets you easily store, sync, and query data for your mobile and web apps - at a global scale.&quot;&lt;/p&gt;
&lt;p&gt;That sounds great, so let&apos;s take a look at some of Cloud Firestore&apos;s properties to better understand what all of this means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is a &lt;strong&gt;NoSQL document database&lt;/strong&gt;, which means that your data doesn&apos;t have to follow a schema you might know from a traditional SQL database like MySQL. This makes it easier to upgrade your data model without having to migrate all your existing data to a new schema.&lt;/li&gt;
&lt;li&gt;You can structure your data in &lt;strong&gt;collections&lt;/strong&gt; and &lt;strong&gt;documents&lt;/strong&gt;, making it easy to organise your data hierarchically. For example, you can store all the user&apos;s tasks in one collection, making it convenient to retrieve all their tasks or just a few, based on some criteria you define.&lt;/li&gt;
&lt;li&gt;Your users can make changes to their data even when &lt;strong&gt;offline&lt;/strong&gt;, and all of their updates will get &lt;strong&gt;synchronised&lt;/strong&gt; across all of the user&apos;s devices &lt;strong&gt;automatically&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Cloud Firestore provides &lt;strong&gt;SDKs for popular programming languages&lt;/strong&gt; and environments such as iOS, Android, and the web.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details, check out this video:&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://www.youtube.com/watch?v=v_hR4K4auoQ&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Mapping our data model to Cloud Firestore&lt;/h2&gt;
&lt;p&gt;Mapping our data model to Cloud Firestore is pretty straightforward, and thanks to the recently added support for &lt;a href=&quot;https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types&quot;&gt;Codable&lt;/a&gt;, we won&apos;t even have to make many changes to our existing code base.&lt;/p&gt;
&lt;p&gt;In Cloud Firestore, you store data in &lt;strong&gt;documents&lt;/strong&gt;. You can think of a document as a lightweight record that contains fields which map to values. Each document is identified by a unique name. Each field has a type, such as &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, or more complex ones like &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;, and &lt;code&gt;timestamp&lt;/code&gt; - see the &lt;a href=&quot;https://firebase.google.com/docs/firestore/manage-data/data-types&quot;&gt;documentation&lt;/a&gt; for a discussion of their specifics, such as value ranges and sort order.&lt;/p&gt;
&lt;p&gt;Documents are stored in &lt;strong&gt;collections&lt;/strong&gt;. For example, you could have a &lt;code&gt;tasks&lt;/code&gt; collection to contain all of your tasks.&lt;/p&gt;
&lt;h3&gt;Hierarchical data model&lt;/h3&gt;
&lt;p&gt;Collections contain nothing but documents - you cannot store data directly into a collection. Similarly, documents cannot contain other documents, but they can point to subcollections, which in turn contain other documents. This allows you to build a hierarchical data model.&lt;/p&gt;
&lt;p&gt;For a task list application that wants to support multiple lists per user, the most straightforward approach might be to build a hierarchical data model that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;While this certainly works (and actually was the approach used in an earlier version of the app we&apos;re building), it will make things more challenging in the long run. Let&apos;s consider two use cases that we might want to implement in the future:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Supporting multiple lists per user&lt;/strong&gt;: At first sight, this looks very simple: create a collection for each list, and put all tasks on that list into the corresponding collection. However, this structure will make it more difficult to run a query that yields all the user&apos;s tasks across all lists that have been completed in the past week (something you might want to know when creating a report).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sharing lists with friends&lt;/strong&gt;: In our hypothetical hierarchical data model, a user&apos;s lists would be nested under the user&apos;s ID. This would make sharing lists with other users very complicated - you&apos;d have to come up with some sort of &quot;proxy&quot; lists that point at the original list. Reasoning about how to retrieve lists and tasks would become a lot more complicated than it needs to be.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Flat data model&lt;/h3&gt;
&lt;p&gt;Instead of using a hierarchical data model, let&apos;s use a flat data model and store all tasks in a single &lt;code&gt;tasks&lt;/code&gt; collection. To specify ownership, we will store the user ID of the owner in the task document. When retrieving a user&apos;s task, we can then simply query for all tasks that match the user&apos;s ID.&lt;/p&gt;
&lt;p&gt;Supporting advanced use cases becomes a lot easier now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To support &lt;strong&gt;multiple lists per user&lt;/strong&gt;, we will create another collection containing all lists (again using a &lt;code&gt;userId&lt;/code&gt; field to specify list ownership). To assign a task to a list, all we need to do is store the list ID in a field &lt;code&gt;listId&lt;/code&gt; in the task document, which makes it simple to search for all tasks in a particular list.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Likewise, &lt;strong&gt;sharing lists with friends&lt;/strong&gt; becomes rather simple as well: again , each list has a field userId to indicate the owner. To share a list with other users, we can add another field sharedWith that contains a list of user IDs this list is shared with.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Check out this video for a discussion of some of how to structure your data in Cloud Firestore:&lt;/p&gt;
&lt;p&gt;&amp;lt;YouTube id=&quot;https://youtu.be/haMOUb3KVSo?t=404&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;Here is how this would look like conceptually:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In the introduction to this series, we decided to deliberately simplify the application a bit - for example, we only support one list per user in the first iteration. The good news about our data model is that it is perfectly suitable for the simplified version of the app, while still being future proof: adding support for multiple lists per user is something that our data model can handle. We will update the UI to support multiple lists in a later part of the series.&lt;/p&gt;
&lt;p&gt;Now that we have a good understanding of how the overall structure of our data model looks like, let&apos;s take a look at the changes we have to make to our source code to implement this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Models/Task.swift
import Foundation
import FirebaseFirestore //(1)
import FirebaseFirestoreSwift

enum TaskPriority: Int, Codable {
  case high
  case medium
  case low
}

struct Task: Codable, Identifiable {
  @DocumentID var id: String? //(2)
  var title: String
  var priority: TaskPriority
  var completed: Bool
  @ServerTimestamp var createdTime: Timestamp? //(3)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As promised earlier, there are just a few changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We need to import &lt;code&gt;FirebaseFirestore&lt;/code&gt; (1) and &lt;code&gt;FirebaseFirestoreSwift&lt;/code&gt; (which contains support for Swift Codable)&lt;/li&gt;
&lt;li&gt;Every Firestore document (and every collection) has a unique identifier. We can make use of this fact and map this to our &lt;code&gt;id&lt;/code&gt; field (remember we need to help the &lt;code&gt;List&lt;/code&gt; view to track the individual rows, e.g. when inserting or deleting elements from the list). The &lt;code&gt;@DocumentID&lt;/code&gt; property wrapper (provided by &lt;code&gt;FirebaseFirestoreSwift&lt;/code&gt;) tells Firebase to map the document&apos;s ID (the last part of the document path) to this property when decoding the document.&lt;/li&gt;
&lt;li&gt;Likewise, &lt;code&gt;@ServerTimestamp&lt;/code&gt; tells Firestore that it should write the current server timestamp into this field when writing the document into the database. Using a server-side timestamp is important when working with data that originates from multiple clients, as the clocks on the clients are most likely not in sync with each other. We will later use this field to ensure that the data is displayed on the client in the order in which it was added to the list.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Implementing the Repository&lt;/h2&gt;
&lt;p&gt;In the first part of the series, we used the Disk framework to persist data to ... disk. We also used dependency injection to decouple the view models from the repository and thus make it easier to swap out the repository implementation. As you will see in a minute, this allows us to change the persistence technology without having to change any of the views or view models.&lt;/p&gt;
&lt;p&gt;Let&apos;s walk through the implementation of the new repository feature by feature.&lt;/p&gt;
&lt;h3&gt;Fetching Tasks from Firestore&lt;/h3&gt;
&lt;p&gt;One of the key features of Firebase is its near-real-time nature: clients can specify they&apos;d like to be notified for any changes to a document or multiple documents. To do so, you&apos;ll have to register a snapshot listener on the document or a query (to be notified when any of the documents in the result set of the query changes). All of this happens almost instantly (depending on the quality of your network connection).&lt;/p&gt;
&lt;p&gt;If you don&apos;t care for real-time updates, you can also perform a &lt;em&gt;one-time fetch&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For our application, we are interested in real-time updates, as this will allow us to let the user use multiple devices to manage their data without having to worry about synchronising data manually (or implementing any pull-to-refresh functionality).&lt;/p&gt;
&lt;p&gt;To receive updates for the user&apos;s tasks, we simply register a snapshot listener on the &lt;code&gt;tasks&lt;/code&gt; collection.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Repositories/TaskRepository.swift

class FirestoreTaskRepository: BaseTaskRepository, TaskRepository, ObservableObject {
  var db = Firestore.firestore() //(1)

  override init() {
    super.init()
    loadData()
  }

  private func loadData() {
    db.collection(&quot;tasks&quot;).order(by: &quot;createdTime&quot;).addSnapshotListener { (querySnapshot, error) in //(2)
      if let querySnapshot = querySnapshot {
        self.tasks = querySnapshot.documents.compactMap { document -&amp;gt; Task? in //(3)
          try? document.data(as: Task.self) //(4)
        }
      }
    }
  }

  // more code to follow
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few things are worth pointing out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We keep a reference to the global Firestore instance (1). For the Firestore client SDK to know which database to connect to, Firebase needs to be properly initialised. We did this when we first added Firebase to the project. If you haven&apos;t copied &lt;code&gt;GoogleService-Info.plist&lt;/code&gt; to the project and called &lt;code&gt;FirebaseApp.configure()&lt;/code&gt; in your application delegate, go back and watch that video I linked earlier in this article.&lt;/li&gt;
&lt;li&gt;The canonical way to register a snapshot listener is &lt;code&gt;db.collection(&quot;path&quot;).addSnapshotListener(callback)&lt;/code&gt;. In this snippet (2), I inserted &lt;code&gt;order(by: &quot;createdTime&quot;)&lt;/code&gt; - why? By default, a query retrieves all documents that satisfy the query in ascending order by document ID. This means they will appear in our &lt;code&gt;List&lt;/code&gt; view in exactly this order, which is not what we want: document IDs are globally unique random strings which will result in random order. As we want to make sure that the tasks the user enters in the UI appear in exactly the order they add them, we&apos;ll use a server-side timestamp to order them chronologically. You can experiment with different order clauses and filters in the Cloud Firestore UI - see the following image:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;querySnapshot&lt;/code&gt; we receive in the closure contains a collection of all the documents that are a result of the query (as we didn&apos;t specify any conditions, we will receive all documents in the tasks collection). Using &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;compactMap&lt;/code&gt; (3), we can transform the elements of this collection into &lt;code&gt;Tasks&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Right now, this query will try to grab all tasks from all users, no matter who wrote them! This is obviously a major problem, which we&apos;ll be fixing later in this post.&lt;/li&gt;
&lt;li&gt;Thanks to Firestore&apos;s support for &lt;code&gt;Codable&lt;/code&gt;, converting a Firestore &lt;code&gt;DocumentSnapshot&lt;/code&gt; into a &lt;code&gt;Task&lt;/code&gt; is a one-liner (4). As the result of this call is an optional, and might be &lt;code&gt;nil&lt;/code&gt; (e.g. when there was a problem performing the mapping due to non-matching data types), we need to use &lt;code&gt;compactMap&lt;/code&gt; when iterating over the collection. This will ensure we only return non-nil elements from the closure, thus yielding a result of &lt;code&gt;[Task]&lt;/code&gt; (as expected), rather than &lt;code&gt;[Task?]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Adding New Tasks to Firestore&lt;/h3&gt;
&lt;p&gt;To add a new document to a Firestore collection, it is sufficient to call &lt;code&gt;addDocument()&lt;/code&gt; on the collection. Firestore&apos;s support for &lt;code&gt;Codable&lt;/code&gt; makes this a delightfully simple call, as we can just pass in any struct or class that implements &lt;code&gt;Codable&lt;/code&gt;. In the past, you&apos;d have to convert your object into a dictionary first.&lt;/p&gt;
&lt;p&gt;Here is the complete code for adding a new task:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class FirestoreTaskRepository: BaseTaskRepository, TaskRepository, ObservableObject {
// ... more code omitted for clarity
func addTask(_ task: Task) {
    do {
      let _ = try db.collection(&quot;tasks&quot;).addDocument(from: task)
    }
    catch {
      print(&quot;There was an error while trying to save a task \(error.localizedDescription).&quot;)
    }
  }
// ... more code omitted for clarity
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might be wondering why we didn&apos;t add the new task to the local &lt;code&gt;tasks&lt;/code&gt; property. Firestore will call the snapshot listener we&apos;ve registered on the &lt;code&gt;tasks&lt;/code&gt; collection immediately after making any changes to the contained documents - even if the application is currently offline. This means that the closure in &lt;code&gt;loadData()&lt;/code&gt; will be called shortly after we&apos;ve added a new task, and thus update the &lt;code&gt;tasks&lt;/code&gt; property. This means we don&apos;t need to bother updating the property inside &lt;code&gt;addTask()&lt;/code&gt; or any of the other methods that operate on the user&apos;s tasks.&lt;/p&gt;
&lt;h3&gt;Updating an Existing Task in Firestore&lt;/h3&gt;
&lt;p&gt;Once the user updates a task by tapping on the task&apos;s checkbox or changing its title, we want to send those updates to Firestore as well.&lt;/p&gt;
&lt;p&gt;Updating a document in Firestore requires knowing its path and document ID. Since we asked Firestore to map the document ID to the &lt;code&gt;id&lt;/code&gt; field of our &lt;code&gt;Task&lt;/code&gt; struct, we already have the document ID:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class FirestoreTaskRepository: BaseTaskRepository, TaskRepository, ObservableObject {
// ... more code omitted for clarity
  func updateTask(_ task: Task) {
    if let taskID = task.id {
      do {
        try db.collection(&quot;tasks&quot;).document(taskID).setData(from: task) //(1)
      }
      catch {
        print(&quot;There was an error while trying to update a task \(error.localizedDescription).&quot;)
      }
    }
  }
// ... more code omitted for clarity
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, it&apos;s really easy to update the document, thanks to Firestore&apos;s &lt;code&gt;Codable&lt;/code&gt; support (1): just call &lt;code&gt;setData(from:)&lt;/code&gt; - it&apos;s that simple.&lt;/p&gt;
&lt;h3&gt;Deleting a Task from Firestore&lt;/h3&gt;
&lt;p&gt;Finally, let&apos;s look at how to delete tasks from Firestore:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class FirestoreTaskRepository: BaseTaskRepository, TaskRepository, ObservableObject {
// ... more code omitted for clarity
func removeTask(_ task: Task) {
    if let taskID = task.id {
      db.collection(&quot;tasks&quot;).document(taskID).delete { (error) in //(1)
        if let error = error {
          print(&quot;Error removing document: \(error.localizedDescription)&quot;)
        }
      }
    }
  }
// ... more code omitted for clarity
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first build a reference to the document, using the collection path (&lt;code&gt;tasks&lt;/code&gt;) and the document&apos;s ID. Deleting the document then is as easy as calling &lt;code&gt;delete()&lt;/code&gt; on the document reference.&lt;/p&gt;
&lt;h2&gt;Wiring up the Repository&lt;/h2&gt;
&lt;p&gt;To try out the new repository, we need to register it with our dependency injection framework, &lt;a href=&quot;https://github.com/hmlongco/Resolver&quot;&gt;Resolver&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: App/AppDelegate+Injection.swift
import Foundation
import Resolver

extension Resolver: ResolverRegistering {
  public static func registerAllServices() {
    register { FirestoreTaskRepository() as TaskRepository }.scope(application)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before you can run the application, we need to make provision a Firestore database in our Firebase project. To do so, go to the &lt;a href=&quot;https://console.firebase.google.com&quot;&gt;Firebase Console&lt;/a&gt; and navigate to the database section of your project and click on &quot;Create database&quot; to create a Firestore database for your project:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;When asked about security rules, choose &quot;Start in test mode&quot;. Later on, we will need to update the security rules to properly secure the database. For now, your database is open for everyone to read and write. To minimize the risk, this full access expires one month into the future (and you will receive some increasingly nagging emails shortly before this time runs out).&lt;/p&gt;
&lt;p&gt;Now, run the application on a Simulator or your phone. Initially, you should see an empty list. Go ahead and add a couple of tasks (keep in mind you need to tap the enter key to commit them), mark some of them as done, and update others.&lt;/p&gt;
&lt;p&gt;To better understand what&apos;s going on, open the &lt;a href=&quot;https://console.firebase.google.com/&quot;&gt;Firebase Console&lt;/a&gt; in your browser navigate into the database section of your app. You should see something similar to this:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You will notice how the data in the database browser gets updated almost instantaneously as you update the tasks in your app. Now go ahead and update a task by editing it in the database browser - as soon as you save the change, it will be reflected in the app! Take it a step further by starting the app on two devices (or a Simulator and a physical device) and try the same - the data stays in sync across all instances. Nice, huh?&lt;/p&gt;
&lt;h2&gt;But what about multiple users?&lt;/h2&gt;
&lt;p&gt;By now, you&apos;re probably wondering how we&apos;re going to support multiple users. After all, when we talked about the data model, we did mention multiple users, right? As it stands, the current implementation will use the same Firestore collection for all users - so all users will have to share their tasks. This is not what we want.&lt;/p&gt;
&lt;p&gt;Ultimately, we will need to implement a way for users to create an account and sign in to the app. Authentication systems usually have some form of user ID that can be used to organise the user&apos;s data. If you recall our earlier discussion of the data model for our application, you&apos;ll remember that we were going to add a field &lt;code&gt;userId&lt;/code&gt; to each task document to refer to the user who &quot;owns&quot; this piece of data.&lt;/p&gt;
&lt;p&gt;Asking users to create a user account does have advantages (for example, this is a prerequisite to sharing data with other users of your app), but it might also be a roadblock: if your users have to sign up for an account before being able to start using your app, they might decide to not give your app a try and uninstall it instead. The drop-off numbers can be significant. Thankfully, there is an easy way to avoid this (and pave the way for using a full-blown authentication system later on): Firebase Anonymous Authentication.&lt;/p&gt;
&lt;h2&gt;Signing in Anonymously&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Firebase Anonymous Authentication&lt;/em&gt; lets you sign in your application&apos;s users without asking them to provide any information about themselves - hence &lt;em&gt;anonymous&lt;/em&gt;. The whole process is completely transparent, which eliminates any sign-up speedbump that your users would face otherwise.&lt;/p&gt;
&lt;p&gt;You can later provide opportunities for them to create a full user account (e.g. by signing in with Google, Facebook, Twitter, or Sign in with Apple), enabling more advanced functionality such as sharing data with other users. Firebase Authentication makes upgrading anonymous users to a full user very easy - we will take a look at how this works in the next episode of this series.&lt;/p&gt;
&lt;p&gt;Like any Firebase user, anonymous users have a unique user ID, which allows us to uniquely identify them and store user-specific data, keeping it safe from other users&apos; eyes. Firebase Security Rules provide a powerful way to protect user data, making sure only the owner of the data can see it and perform operations on it.&lt;/p&gt;
&lt;p&gt;To support Firebase Anonymous Authentication in your application, you need to follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Turn on support for Anonymous Auth in the Firebase Console&lt;/li&gt;
&lt;li&gt;Add the Firebase Auth pod to your project&lt;/li&gt;
&lt;li&gt;Perform an anonymous sign in at application start-up&lt;/li&gt;
&lt;li&gt;Use the anonymous user&apos;s ID to store/retrieve data&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_3/implement_anonymous_auth/start&quot;&gt;&lt;code&gt;stage_3/implement_anonymous_auth/start&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcworkspace&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To enable support for Anonymous Authentication, navigate to the &lt;em&gt;Authentication&lt;/em&gt; section in the sidebar of your Firebase project, and open the &lt;em&gt;Sign-in method&lt;/em&gt; tab. Turn on &lt;em&gt;Anonymous&lt;/em&gt;, and click the &lt;em&gt;Save&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Before we can use Anonymous Auth in our app, we&apos;ll have to add Firebase/Auth to our CocoaPods file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;target &apos;MakeItSo&apos; do
  use_frameworks!

  # Pods for MakeItSo
  pod &apos;Resolver&apos;
  pod &apos;Disk&apos;, &apos;~&amp;gt; 0.6.4&apos;

  pod &apos;Firebase/Auth&apos;
  pod &apos;Firebase/Analytics&apos;
  pod &apos;Firebase/Firestore&apos;
  pod &apos;FirebaseFirestoreSwift&apos;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&apos;s build a simple authentication service to encapsulate handling signing in, signing out, and providing access to the currently signed-in user. By encapsulating all of this in a dedicated service, we&apos;ll later be able to add other authentication mechanisms (such as &lt;em&gt;Sign in with Apple&lt;/em&gt;) more easily.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Services/AuthenticationService
import Foundation
import Firebase

class AuthenticationService: ObservableObject {

  @Published var user: User? //(1)

  func signIn() {
    registerStateListener() //(2)
    Auth.auth().signInAnonymously() //(3)
  }

  private func registerStateListener() {
    Auth.auth().addStateDidChangeListener { (auth, user) in //(4)
      print(&quot;Sign in state has changed.&quot;)
      self.user = user

      if let user = user {
        let anonymous = user.isAnonymous ? &quot;anonymously &quot; : &quot;&quot;
        print(&quot;User signed in \(anonymous)with user ID \(user.uid).&quot;)
      }
      else {
        print(&quot;User signed out.&quot;)
      }
    }
  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The service&apos;s &lt;code&gt;user&lt;/code&gt; property (1) provides access to the currently signed in Firebase user. By annotating this as a &lt;code&gt;@Published&lt;/code&gt; property, we can later use Combine to react to any changes (such as when the user signs in or out) more easily.&lt;/p&gt;
&lt;p&gt;When the service&apos;s &lt;code&gt;signIn()&lt;/code&gt; method is called, we will register (2) a state listener (4), which is called whenever the user signs in or out. Finally, we ask Firebase Auth to sign in anonymously (3).&lt;/p&gt;
&lt;p&gt;As before, we need to register the service with our dependency injection framework in &lt;code&gt;AppDelegate+Injection.swift&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension Resolver: ResolverRegistering {
  public static func registerAllServices() {
    register { AuthenticationService() }.scope(application)
    register { FirestoreTaskRepository() as TaskRepository }.scope(application)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To initiate the sign-in process, we inject the service into our &lt;code&gt;AppDelegate&lt;/code&gt;, and call the &lt;code&gt;signIn&lt;/code&gt; method after Firebase has been initialised:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: App/AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {

  @Injected var authenticationService: AuthenticationService

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&amp;gt; Bool {

    FirebaseApp.configure()
    authenticationService.signIn()

    return true
  }
  // code omitted for clarity
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Making the Task Repository User-Aware&lt;/h2&gt;
&lt;p&gt;Now that we&apos;ve successfully and anonymously signed in our user, we can store the user&apos;s ID in the tasks they create to indicate their &quot;ownership&quot;.&lt;/p&gt;
&lt;p&gt;To this end, we first need to add a &lt;code&gt;userId&lt;/code&gt; field to the &lt;code&gt;Task&lt;/code&gt; struct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Task: Codable, Identifiable {
  @DocumentID var id: String?
  var title: String
  var priority: TaskPriority
  var completed: Bool
  @ServerTimestamp var createdTime: Timestamp?
  var userId: String?
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we&apos;ll have to make a couple of changes to the &lt;code&gt;FirestoreTaskRepository&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class FirestoreTaskRepository: BaseTaskRepository, TaskRepository, ObservableObject {
  var db = Firestore.firestore()

  @Injected var authenticationService: AuthenticationService //(1)
  var tasksPath: String = &quot;tasks&quot; //(2)
  var userId: String = &quot;unknown&quot;

  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  override init() {
    super.init()

    authenticationService.$user //(3)
      .compactMap { user in
        user?.uid //(4)
      }
      .assign(to: \.userId, on: self) //(5)
      .store(in: &amp;amp;cancellables)

    //(re)load data if user changes
    authenticationService.$user //(6)
      .receive(on: DispatchQueue.main) //(7)
      .sink { user in
        self.loadData() //(8)
      }
      .store(in: &amp;amp;cancellables)
  }

  private func loadData() {
    db.collection(tasksPath)
      .whereField(&quot;userId&quot;, isEqualTo: self.userId) //(9)
      .order(by: &quot;createdTime&quot;)
      .addSnapshotListener { (querySnapshot, error) in
        if let querySnapshot = querySnapshot {
          self.tasks = querySnapshot.documents.compactMap { document -&amp;gt; Task? in
            try? document.data(as: Task.self)
          }
        }
      }
  }

  func addTask(_ task: Task) {
    do {
      var userTask = task
      userTask.userId = self.userId //(10)
      let _ = try db.collection(tasksPath).addDocument(from: userTask)
    }
    catch {
      fatalError(&quot;Unable to encode task: \(error.localizedDescription).&quot;)
    }
  }

  func removeTask(_ task: Task) {
    if let taskID = task.id {
      db.collection(tasksPath).document(taskID).delete { (error) in
        if let error = error {
          print(&quot;Unable to remove document: \(error.localizedDescription)&quot;)
        }
      }
    }
  }

  func updateTask(_ task: Task) {
    if let taskID = task.id {
      do {
        try db.collection(tasksPath).document(taskID).setData(from: task)
      }
      catch {
        fatalError(&quot;Unable to encode task: \(error.localizedDescription).&quot;)
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s look at what this code does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We use Resolver to inject an instance of &lt;code&gt;AuthenticationService&lt;/code&gt; (1)&lt;/li&gt;
&lt;li&gt;All tasks are contained in the &lt;code&gt;tasks&lt;/code&gt; collection, so we define a constant (2) to keep things DRY.&lt;/li&gt;
&lt;li&gt;In the initialiser, we subscribe (3) and (6) to the &lt;code&gt;user&lt;/code&gt; publisher on the &lt;code&gt;AuthenticationService&lt;/code&gt; to be informed whenever the &lt;code&gt;user&lt;/code&gt; property changes.&lt;/li&gt;
&lt;li&gt;The first Combine pipeline (3) extracts the user&apos;s ID (4), and assigns it to the &lt;code&gt;userId&lt;/code&gt; property (5)&lt;/li&gt;
&lt;li&gt;The second pipeline (6) also kicks in whenever the &lt;code&gt;user&lt;/code&gt; property on the authentication service changes. It then invokes the &lt;code&gt;loadData()&lt;/code&gt; method (8) to (re)load the current user&apos;s tasks.&lt;/li&gt;
&lt;li&gt;It is essential to make sure any update to the UI is executed on the main thread. This can be achieved by using the &lt;code&gt;.receive(on:)&lt;/code&gt; operator (7) - this tells Combine to run the rest of the pipeline from here on on the specified thread/queue.&lt;/li&gt;
&lt;li&gt;To make sure we only fetch tasks that belong to the current user, we add a &lt;code&gt;whereField&lt;/code&gt; clause, specifying the current user id (9).&lt;/li&gt;
&lt;li&gt;When adding a new task, we need to make sure to provide the current user ID - otherwise, the task wouldn&apos;t be in the result set of the query above (9).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you run the app now, you will see that any tasks you add via the UI disappear from the list view immediately. You might also notice an error message in the Xcode console, indicating that the snapshot listener didn&apos;t yield any results and that you should create an index.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Firebase/Firestore][I-FST000001] Listen for query at tasks failed: The query requires an index. You can create it here: https://console.firebase.com/v1/r/project/&amp;lt;your-project-id&amp;gt;/firestore/indexes?create_composite=some-random-looking-id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is due to the fact that we&apos;re now using a &lt;em&gt;compound query&lt;/em&gt;: we ask Firestore to query the &lt;code&gt;userId&lt;/code&gt; field and sort by the &lt;code&gt;createdTime&lt;/code&gt; field. To fulfil the promise that &lt;em&gt;no query is a slow query&lt;/em&gt;, Firestore demands that you set up an index for this compound query. By following the link in the error message, you can create this index very easily. In fact, this is the recommended way to create indexes for Firestore!&lt;/p&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_3/finish_line&quot;&gt;&lt;code&gt;stage_3/finish_line&lt;/code&gt;&lt;/a&gt; and open
&lt;code&gt;MakeItSo.xcworkspace&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder]
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To verify your implementation, launch the application on a Simulator. When the UI comes up, the task list should be empty, and the Xcode console should indicate the user ID of the anonymous user you just signed in with.&lt;/p&gt;
&lt;p&gt;Open the Firebase console side-by-side with the Simulator, and navigate to the Firestore database browser so you can observe the database updating as you add new items to the task list (you might need to reload the browser window once): any new items will be inserted as child documents under the tasks collection of your user ID.&lt;/p&gt;
&lt;p&gt;If you start the application on another device (Simulator or physical phone), you will notice that this will result in a new user document being created under the &lt;code&gt;users&lt;/code&gt; collection. Both users can change their data independently of each other - just like we wanted.&lt;/p&gt;
&lt;p&gt;In the screenshot below, you can see that each of the simulators has been assigned a different user ID, and if you look closely, you will see that the user ID &lt;code&gt;58at9ENjbGWObumaKlnxGMqzyJ13&lt;/code&gt; (displayed in the Xcode console) makes an appearance in the Firestore data browser as well.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;We need to talk about security&lt;/h2&gt;
&lt;p&gt;No article about Cloud Firestore would be complete without mentioning &lt;a href=&quot;https://firebase.google.com/docs/firestore/security/get-started&quot;&gt;Security Rules&lt;/a&gt;! Earlier, when setting up the Firestore database for your project, I told you to choose the development settings. This made it easy to get started, because our clients could read and write data from and to the database. However, there is a huge issue: anyone can access your database - they just have to guess your project ID. To prevent malicious people on the internet from tampering with your users&apos; data, we need to set up some security rules. You can add security rules to your Cloud Firestore database by navigating to the Rules tab in the Cloud Firestore database browser in the Firebase Console.&lt;/p&gt;
&lt;p&gt;Without further ado, here are some initial security rules that might start to secure our database:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rules_version = &apos;2&apos;;
service cloud.firestore {
  match /databases/{database}/documents {
    match /tasks/{task} {
    	allow write: if request.auth.uid != null
                     &amp;amp;&amp;amp; request.resource.data.userId == request.auth.uid; //(1)
      allow read: if resource.data.userId == request.auth.uid; //(2)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To secure our database, we need to make sure that only registered users can create new tasks, and that users can only read the tasks they own (i.e. the ones they created).&lt;/p&gt;
&lt;p&gt;By demanding that a write request must contain a valid authentication object (1), we ensure only signed in users can create new tasks.&lt;/p&gt;
&lt;p&gt;The second requirement can be met by comparing the &lt;code&gt;userId&lt;/code&gt; field of the task that is being requested with the user ID of the incoming read request. Only if they match up, the read request is permitted.&lt;/p&gt;
&lt;p&gt;And with that, our database on its way to being more secure.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, you saw how easy it is to&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add Firebase to your existing iOS project&lt;/li&gt;
&lt;li&gt;Store user data in Cloud Firestore&lt;/li&gt;
&lt;li&gt;Use Firebase Anonymous Authentication to transparently sign in your users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to implementing MVVM and using a dependency injection framework like Resolver, we&apos;ve established a flexible architecture that has already made it easy to change some of the app&apos;s components without massive refactorings.&lt;/p&gt;
&lt;p&gt;In the next episode, we&apos;re going to look at &lt;em&gt;Sign in with Apple&lt;/em&gt;, how to add a sign-up flow to the app without disrupting the user experience, and how Firebase can make this easier for us.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/&quot;&gt;Source code for the sample app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hmlongco/Resolver&quot;&gt;Resolver on Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/saoudrizwan/Disk&quot;&gt;Disk on Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types&quot;&gt;Swift Codable documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>SwiftUI, Combine, and Firebase</title><link>https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part1/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2020/replicating-reminder-swiftui-firebase-part1/</guid><description>Learn how to use SwiftUI and Firebase to build a real-world app</description><pubDate>Sat, 18 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;import demo from &apos;./replicating-reminder-swiftui-firebase-part1/demo.mp4&apos;;&lt;/p&gt;
&lt;p&gt;At WWDC 2019, Apple announced their declarative UI framework, SwiftUI. By now, most of you probably have had time to take SwiftUI for a spin and get an understanding of how it works in general. In this series, I&apos;d like to change gears and see how far we can get in writing a real-world iOS application using SwiftUI and a few other technologies.&lt;/p&gt;
&lt;p&gt;To make it easier to see where SwiftUI excels (and where it falls short), let&apos;s replicate an application everybody knows: the iOS Reminders app. Now, if you&apos;re anything like me, you probably haven&apos;t been using the Reminders app much and instead use one of the popular to-do list apps available in the App Store. I was pleasantly surprised to discover that the iOS Reminders app has caught up with the competition and has become a much more feature-complete and powerful productivity app. For the sake of simplicity, we&apos;re going to focus on the core functionality, and will gradually add more features in future articles.&lt;/p&gt;
&lt;p&gt;As there&apos;s still a lot of ground to cover, I&apos;ve decided to break this project apart into a series of articles that build upon each other. Here is a quick overview of what we&apos;re going to build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;part 1&lt;/strong&gt; of the series (which you are reading right now), we will focus on building the UI with SwiftUI, using a simple data model.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;part 2&lt;/strong&gt;, we&apos;re going to connect the application to Firebase, and will synchronize the user&apos;s tasks with Cloud Firestore&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;part 3&lt;/strong&gt;, we will implement Sign in with Apple to turn the application into a real multi-user application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If that sounds good to you, let&apos;s get started!&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;The source code for the application is available in &lt;a href=&quot;https://github.com/peterfriese/MakeItSo&quot;&gt;this Github repository&lt;/a&gt;, with the various stages of the application being tagged accordingly. If you want to follow along, feel free to check out the repository - I will indicate which branch / tag we&apos;re at, so you can compare your implementation with mine.&lt;/p&gt;
&lt;p&gt;To get started, clone the &lt;a href=&quot;https://github.com/peterfriese/MakeItSo&quot;&gt;repository&lt;/a&gt;, and look around the checked out folder:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;starter&lt;/code&gt; folder contains a Single View application that has been cleaned up a bit, as well as a nice-looking application icon.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;final&lt;/code&gt; folder contains the finished version of the project, as well as all intermediate steps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can follow along by either writing your own implementation in the starter folder, or checking out the individual checkpoints in the final folder.&lt;/p&gt;
&lt;h2&gt;Data Model&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_1/data_model/start&quot;&gt;&lt;code&gt;stage_1/data-model/start&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcodeproj&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;As mentioned above, we will deliberately implement a simplified version of the iOS Reminders app to better be able to focus on the core concepts. For example, our implementation will only support one list of tasks (the iOS Reminders app supports multiple lists and navigating between them).&lt;/p&gt;
&lt;p&gt;Thus, our data model is very simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our application will manage a single task list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each task has&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a title (i.e. indicating what the user needs to do)&lt;/li&gt;
&lt;li&gt;a priority (high, medium, low)&lt;/li&gt;
&lt;li&gt;a flag indicating whether the task is completed or not&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the code for our data model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Models/Task.swift

enum TaskPriority {
  case high
  case medium
  case low
}

struct Task: Identifiable {
  var id: String = UUID().uuidString
  var title: String
  var priority: TaskPriority
  var completed: Bool
}

#if DEBUG
let testDataTasks = [
  Task(title: &quot;Implement UI&quot;, priority: .medium, completed: false),
  Task(title: &quot;Connect to Firebase&quot;, priority: .medium, completed: false),
  Task(title: &quot;????&quot;, priority: .high, completed: false),
  Task(title: &quot;PROFIT!!!&quot;, priority: .high, completed: false)
]
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A couple of things to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We made the &lt;code&gt;Task&lt;/code&gt; struct &lt;code&gt;Identifiable&lt;/code&gt;, and also added an &lt;code&gt;id&lt;/code&gt; attribute. This is necessary, as we will be displaying the tasks in a SwiftUI &lt;code&gt;List&lt;/code&gt;, which requires its items to be &lt;code&gt;Identifiable&lt;/code&gt;. Using &lt;code&gt;UUID&lt;/code&gt; makes sure each task will get a unique identifier.&lt;/li&gt;
&lt;li&gt;We defined a collection with test data, which we can use to drive development of the UI until we actually connect to some data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Building a Task List View&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_1/build_task_list/start&quot;&gt;&lt;code&gt;stage_1/build_task_list/start&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcodeproj&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;Before we set out to build the task list view ourselves, let&apos;s take a look at the iOS Reminders app. Looking at the main list view interface, we can see three main UI elements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The list title (&quot;Tasks&quot; in this example)&lt;/li&gt;
&lt;li&gt;The list view&lt;/li&gt;
&lt;li&gt;A button to add new tasks&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can also add new items by tapping in the blank area just below the last item, which will add a new, empty line to the list view, with the cursor waiting for you in the text field, ready to receive your new task. The same inline editing UX is used for changing items: tap on a task and start typing to make your changes. This UX pattern is really neat, as it allows the user to see the item they&apos;re currently editing in the context of the other items on the list:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Recreating this layout with SwiftUI is more or less straightforward - let&apos;s look at how it&apos;s done:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Views/TaskListView.swift

import SwiftUI

struct TaskListView: View {
  var tasks: [Task] = testDataTasks //(1)


  var body: some View {
    NavigationView { //(2)
      VStack(alignment: .leading) {
        List {
          ForEach (self.tasks) { task in //(3)
            TaskCell(task: task) //(6)
          }
          .onDelete { indexSet in //(4)
             // The rest of this function will be added later
          }
        }
        Button(action: {}) { //(7)
          HStack {
            Image(systemName: &quot;plus.circle.fill&quot;) //(8)
              .resizable()
              .frame(width: 20, height: 20) //(11)
            Text(&quot;New Task&quot;) //(9)
          }
        }
        .padding()
        .accentColor(Color(UIColor.systemRed)) //(13)
      }
      .navigationBarTitle(&quot;Tasks&quot;)
    }
  }
}

struct TaskListView_Previews: PreviewProvider {
  static var previews: some View {
    TaskListView()
  }
}

struct TaskCell: View { //(5)
  var task: Task

  var body: some View {
    HStack {
      Image(systemName: task.completed ? &quot;checkmark.circle.fill&quot; : &quot;circle&quot;)
        .resizable()
        .frame(width: 20, height: 20) //(12)
      Text(task.title)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will notice that we are using the test tasks (1) from our data model which - thanks to Xcode&apos;s Preview Canvas - gives us the nice benefit of being able to see some data in our UI while we&apos;re building it.&lt;/p&gt;
&lt;p&gt;Let&apos;s look at a couple of interesting aspects of the code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The entire view is wrapped in a &lt;code&gt;NavigationView&lt;/code&gt; (2), which lets us set the view title using &lt;code&gt;.navigationBarTitle()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Instead of iterating over our collection of tasks with &lt;code&gt;List(self.tasks)&lt;/code&gt;, we use a nested &lt;code&gt;ForEach&lt;/code&gt; (3), the reason being that &lt;code&gt;.onDelete()&lt;/code&gt; is only available on &lt;code&gt;ForEach&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Speaking of which, &lt;code&gt;onDelete()&lt;/code&gt; (4) enables &lt;em&gt;delete mode&lt;/em&gt; on the list, which lets users swipe left on a cell to reveal the delete action. Unfortunately, SwiftUI doesn&apos;t (yet) support any other contextual actions - my guess is that Apple wanted to use some extra time to hone out the DSL for adding contextual actions to lists.&lt;/li&gt;
&lt;li&gt;The cells for each of the tasks have already been extracted into a separate view, &lt;code&gt;TaskCell&lt;/code&gt; (5), which makes the call site (6) much cleaner&lt;/li&gt;
&lt;li&gt;The button (7) for adding a new task consists of two subviews: an image of a plus sign (8), and the text &quot;New Task&quot; (9). This is a great example of how SwiftUI promotes composable UIs.&lt;/li&gt;
&lt;li&gt;To make sure the checkboxes (which are SF Symbol icons) and the plus sign provide large enough touch targets, we make the icons resizable and change their frame to 20 x 20 pixels (11) and (12).&lt;/li&gt;
&lt;li&gt;Finally, we add some padding and use an accent color to tint the button red (13). Using a system color makes sure this looks great both in light and dark mode (read &lt;a href=&quot;https://developer.apple.com/documentation/xcode/supporting_dark_mode_in_your_interface&quot;&gt;this article&lt;/a&gt; for more background about how to implement dark mode in your apps). I wasn&apos;t able to figure out a way to also tint the navigation bar title - if you find out how to achieve this, please file a pull request on the repo!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And with that, we&apos;ve got the basic version of our UI in place. As always, you can run the app on the Simulator or on a physical device to see it in action, but thanks to SwiftUI&apos;s Preview, we don&apos;t need to do that! In fact, I was able to enjoy a preview of the UI while building it, thanks to our test data and Xcode&apos;s SwiftUI Preview Canvas (that&apos;s rather a mouthful):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
To quickly open the preview pane, press &lt;code&gt;⌥ + ⌘ + ↩&lt;/code&gt;. If the preview says &quot;Automatic preview
updating paused&quot;, press &lt;code&gt;⌥ + ⌘ + P&lt;/code&gt; to resume. For more Xcode keybindings, see &lt;a href=&quot;https://peterfriese.dev/xcode-shortcuts/&quot;&gt;Essential Xcode
Shortcuts for More Efficient Coding&lt;/a&gt;.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;When running the application (or using Live Preview), you will notice that it&apos;s not yet very functional - for example, nothing happens when you try to add a new task, and even tapping one of the checkboxes doesn&apos;t mark the task as completed.&lt;/p&gt;
&lt;p&gt;So let&apos;s change that now and implement the business logic of our application!&lt;/p&gt;
&lt;h2&gt;Application Architecture&lt;/h2&gt;
&lt;p&gt;Before we go any further, let&apos;s take a moment to think about the architecture for our application. While it&apos;s certainly possible to build SwiftUI views that also contain business logic, this approach easily results in code that is not only hard to read, but virtually untestable.&lt;/p&gt;
&lt;p&gt;Thankfully, SwiftUI&apos;s declarative nature lends itself to a functional reactive approach, which, backed by an MVVM (Model, View, ViewModel) architecture, will result in easy-to-read, well-testable code. For a good overview of different architecture patterns for SwiftUI, including an in-depth discussion of MVVM, check out &lt;a href=&quot;https://quickbirdstudios.com/blog/swiftui-architecture-redux-mvvm/&quot;&gt;SwiftUI Architectures: Model-View, Redux &amp;amp; MVVM&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition to the views and models or our application, we will need some view models and repositories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Views&lt;/strong&gt; are responsible for displaying data and handling user interaction&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ViewModels&lt;/strong&gt; are responsible for providing data to the views and turning user interactions into update requests for the data repositories&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Models&lt;/strong&gt; hold the data that the app operates on. They are transferred back and forth between the view models and the repositories.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repositories&lt;/strong&gt; provide an abstraction for the data layer, making it easy to swap out a local storage for a cloud-based storage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The relationships between all of the above can be seen in the following diagram:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Implementing View Models&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_2/implement_view_models/start&quot;&gt;&lt;code&gt;stage_2/implement_view_models/start&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcodeproj&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;MVVM calls for a 1:1 relationship between views and view models: each view has one view model that all the UI elements are bound to and which will handle any user interaction taking place on this particular screen.&lt;/p&gt;
&lt;p&gt;It might seem a bit surprising at first when I tell you that we will need to implement two different view models for our application: &lt;code&gt;TaskListViewModel&lt;/code&gt; and &lt;code&gt;TaskCellViewModel&lt;/code&gt;. This is due to the fact that each row in the list view also doubles as an editor view for the respective underlying model element. So - &lt;code&gt;TaskListViewModel&lt;/code&gt; is the view model for the list itself, whereas &lt;code&gt;TaskCellViewModel&lt;/code&gt; is the view model for the individual list view rows (or rather, cells).&lt;/p&gt;
&lt;p&gt;All talk is cheap, let&apos;s look at some code!&lt;/p&gt;
&lt;p&gt;Here is &lt;code&gt;TaskListViewModel&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: TaskListViewModel.swift

import Foundation
import Combine

class TaskListViewModel: ObservableObject { //(1)
  @Published var taskCellViewModels = [TaskCellViewModel]() //(3)

  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  init() {
    self.taskCellViewModels = testDataTasks.map { task in //(2)
      TaskCellViewModel(task: task)
    }
  }

  func removeTasks(atOffsets indexSet: IndexSet) { //(4)
    taskCellViewModels.remove(atOffsets: indexSet)
  }

  func addTask(task: Task) { //(5)
    taskCellViewModels.append(TaskCellViewModel(task: task))
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is &lt;code&gt;TaskCellViewModel&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: TaskCellViewModel.swift

import Foundation
import Combine

class TaskCellViewModel: ObservableObject, Identifiable  { //(6)
  @Published var task: Task

  var id: String = &quot;&quot;
  @Published var completionStateIconName = &quot;&quot;

  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  static func newTask() -&amp;gt; TaskCellViewModel {
    TaskCellViewModel(task: Task(title: &quot;&quot;, priority: .medium, completed: false))
  }

  init(task: Task) {
    self.task = task

    $task //(8)
      .map { $0.completed ? &quot;checkmark.circle.fill&quot; : &quot;circle&quot; }
      .assign(to: \.completionStateIconName, on: self)
      .store(in: &amp;amp;cancellables)

    $task //(7)
      .map { $0.id }
      .assign(to: \.id, on: self)
      .store(in: &amp;amp;cancellables)

  }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Both view models implement &lt;code&gt;ObservableObject&lt;/code&gt; (1), so we can bind them to SwiftUI elements and make sure the UI reacts to any changes to the view models. This is SwiftUI&apos;s superpower: no need to manually synchronise your UI with the underlying data model. I can&apos;t overstate the importance of this aspect.&lt;/li&gt;
&lt;li&gt;In a later step, we will connect to local storage (and then, to Firestore), but for the time being, we&apos;ll use the test data defined in &lt;code&gt;testDataTasks&lt;/code&gt; (2). Using the &lt;code&gt;map()&lt;/code&gt; method, we convert the &lt;code&gt;Task&lt;/code&gt; models in this collection into &lt;code&gt;TaskCellViewModel&lt;/code&gt;s. The array containing these converted view models, &lt;code&gt;taskCellViewModels&lt;/code&gt; (3), is annotated as &lt;code&gt;@Published&lt;/code&gt;, which allows us to bind the &lt;code&gt;List&lt;/code&gt; on the &lt;code&gt;TaskListView&lt;/code&gt; to it.&lt;/li&gt;
&lt;li&gt;To further help decouple the UI from the underlying models, we also add two methods for adding and removing tasks - &lt;code&gt;addTask&lt;/code&gt; (5) and &lt;code&gt;removeTask&lt;/code&gt; (4).&lt;/li&gt;
&lt;li&gt;The individual rows in the &lt;code&gt;TaskListView&lt;/code&gt; are backed by &lt;code&gt;TaskCellViewModels&lt;/code&gt;. As SwiftUI requires items in a &lt;code&gt;List&lt;/code&gt; view to be &lt;code&gt;Identifiable&lt;/code&gt;, we have to implement this protocol (6) and provide an &lt;code&gt;id&lt;/code&gt; attribute. The value of the &lt;code&gt;id&lt;/code&gt; attribute will be updated whenever the &lt;code&gt;task&lt;/code&gt; attribute is changed. To make this possible, we annotate the &lt;code&gt;task&lt;/code&gt; attribute as &lt;code&gt;@Published&lt;/code&gt;, and subscribe to it (7) in the constructor.&lt;/li&gt;
&lt;li&gt;Similarly, we update the name for the icon that represents the completion status of the task by subscribing to the &lt;code&gt;task&lt;/code&gt; property and mapping its &lt;code&gt;completed&lt;/code&gt; property to the respective image name (8).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Binding the View Models&lt;/h2&gt;
&lt;p&gt;We&apos;re now ready to bind the view models to our UI and hook up any UI actions, such as deleting or adding a new item, as well as updating the underlying tasks when editing a cell or tapping on a row&apos;s checkbox.&lt;/p&gt;
&lt;p&gt;Let&apos;s first look at the updated implementation of &lt;code&gt;TaskCell&lt;/code&gt; to understand what needs to change:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Views/TaskListView.swift

struct TaskCell: View {
  @ObservedObject var taskCellVM: TaskCellViewModel //(1)
  var onCommit: (Result&amp;lt;Task, InputError&amp;gt;) -&amp;gt; Void = { _ in } //(5)

  var body: some View {
    HStack {
      Image(systemName: taskCellVM.completionStateIconName) //(2)
        .resizable()
        .frame(width: 20, height: 20)
        .onTapGesture {
          self.taskCellVM.task.completed.toggle()
        }
      TextField(&quot;Enter task title&quot;, text: $taskCellVM.task.title, //(3)
                onCommit: { //(4)
                  if !self.taskCellVM.task.title.isEmpty {
                    self.onCommit(.success(self.taskCellVM.task))
                  }
                  else {
                    self.onCommit(.failure(.empty))
                  }
      }).id(taskCellVM.id)
    }
  }
}

enum InputError: Error {
  case empty
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are quite a few changes, so let&apos;s walk through them one at a time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It shouldn&apos;t come as a surprise that we&apos;ll bind all subviews of &lt;code&gt;TaskCell&lt;/code&gt; to a &lt;code&gt;TaskCellViewModel&lt;/code&gt;, hence we refactored the &lt;code&gt;task&lt;/code&gt; property to &lt;code&gt;taskCellVM&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Instead of polluting our views with business logic to compute view state, we bind the view properties to the respective view model properties. For example, we fetch the icon name for the completed status image from &lt;code&gt;taskCellVM.completionStateIconName&lt;/code&gt; (2).&lt;/li&gt;
&lt;li&gt;Maybe the biggest change is that we&apos;ve exchanged the &lt;code&gt;Text&lt;/code&gt; view we&apos;ve been using to display the task title for a &lt;code&gt;TextField&lt;/code&gt; (3). This allows the user to edit a task title by tapping a cell and starting to type, which is quite convenient.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In terms of handling user interactions, there are a couple of obvious changes, but also a few that require a bit more explanation. Let&apos;s start with a simple one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To allow the user to mark a task as complete, we need to handle taps on the image view (by adding an &lt;code&gt;onTapGesture&lt;/code&gt; callback). We can then simply toggle the completed state on the view model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As mentioned before, the user can edit the task title by tapping into the respective &lt;code&gt;TextField&lt;/code&gt; and starting to type. The changes will be reflected both on the local view model (&lt;code&gt;taskCellVM&lt;/code&gt;), and in the array containing it in the parent view&apos;s view model. So what&apos;s the reason for implementing &lt;code&gt;onCommit&lt;/code&gt; (4) on the TextField, and why do we forward this to our own &lt;code&gt;onCommit&lt;/code&gt; handler (5)?&lt;/p&gt;
&lt;p&gt;To answer this question, let&apos;s take a look at the updated implementation of &lt;code&gt;TaskListView&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Views/TaskListView.swift

struct TaskListView: View {
  @ObservedObject var taskListVM = TaskListViewModel() //(7)
  @State var presentAddNewItem = false

  var body: some View {
    NavigationView {
      VStack(alignment: .leading) {
        List {
          ForEach (taskListVM.taskCellViewModels) { taskCellVM in //(8)
            TaskCell(taskCellVM: taskCellVM) //(1)
          }
          .onDelete { indexSet in
            self.taskListVM.removeTasks(atOffsets: indexSet)
          }
          if presentAddNewItem { //(5)
            TaskCell(taskCellVM: TaskCellViewModel.newTask()) { result in //(2)
              if case .success(let task) = result {
                self.taskListVM.addTask(task: task) //(3)
              }
              self.presentAddNewItem.toggle() //(4)
            }
          }
        }
        Button(action: { self.presentAddNewItem.toggle() }) { //(6)
          HStack {
            Image(systemName: &quot;plus.circle.fill&quot;)
              .resizable()
              .frame(width: 20, height: 20)
            Text(&quot;New Task&quot;)
          }
        }
        .padding()
        .accentColor(Color(UIColor.systemRed))
      }
      .navigationBarTitle(&quot;Tasks&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let me draw your attention to two locations in this class: first, the place where we use &lt;code&gt;TaskCell&lt;/code&gt; to render as a normal cell within the &lt;code&gt;List&lt;/code&gt; view (1). Nothing special is going on here - we&apos;re just using a plain &lt;code&gt;TaskCell&lt;/code&gt; here.&lt;/p&gt;
&lt;p&gt;However, a little bit further down (2), a whole bunch of things are going on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Most prominently, the cell now has a trailing closure which receives a &lt;code&gt;Result&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the result is a &lt;code&gt;success&lt;/code&gt;, we extract a &lt;code&gt;Task&lt;/code&gt; from the result in order to add a new &lt;code&gt;TaskCellViewModel&lt;/code&gt; (3) to the view model holding all the task cell view models.&lt;/li&gt;
&lt;li&gt;Any other cases will be silently ignored. If you go back to the implementation of &lt;code&gt;TaskCell&lt;/code&gt;, you will see that the only other case is &lt;code&gt;empty&lt;/code&gt;, which we will send in case the user didn&apos;t enter any text.&lt;/li&gt;
&lt;li&gt;Finally, we toggle &lt;code&gt;presentAddNewItem&lt;/code&gt; (4) - this is a flag that guards (5) the visibility of the entire block and will be toggled (6) whenever the user taps the &quot;New Task&quot; button.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you look closely, you will notice that this entire block is wrapped inside the &lt;code&gt;List&lt;/code&gt;, meaning that whenever the user taps the &quot;New Task&quot; button, we will add a new, empty cell to the end of the list. This is the cell the user can use to add a new task with.&lt;/p&gt;
&lt;p&gt;Going back to the question of why we need the &lt;code&gt;onCommit&lt;/code&gt; callback: this is required because we only want to add a new &lt;code&gt;Task&lt;/code&gt; when the user taps the enter key on their keyboard.&lt;/p&gt;
&lt;p&gt;To round things off, you will see that we create a &lt;code&gt;TaskListViewModel&lt;/code&gt; (7), annotated as an &lt;code&gt;@ObservedObject&lt;/code&gt; which allows us to bind the &lt;code&gt;List&lt;/code&gt; view to its &lt;code&gt;taskCellViewModels&lt;/code&gt; property (8).&lt;/p&gt;
&lt;p&gt;If you run the app now, you will notice that the UI is mostly functional: you can add new tasks, modify existing tasks, and mark tasks as completed. However, your changes aren&apos;t persisted: every time you restart the app, you&apos;re back to the hardcoded demo data.&lt;/p&gt;
&lt;p&gt;To fix this, we need to implement a persistence layer (we&apos;re working our way down from the UI to the disk...).&lt;/p&gt;
&lt;h2&gt;Keeping Tasks in a Repository&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_2/implement_repository/start&quot;&gt;&lt;code&gt;stage_2/implement_repository/start&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcodeproj&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;To persist the user&apos;s tasks across app launches, we&apos;re going to implement a &lt;em&gt;repository&lt;/em&gt;. A repository serves as an abstraction for the persistence layer - this will make it easier for us to choose different technologies for storing our data. For example, we will first store the user&apos;s data on disk, and then implement a repository that connects to Firebase and lets us store data in Cloud Firestore. As an intermediate step, we&apos;re going to implement a &lt;code&gt;TestDataTaskRepository&lt;/code&gt;, to retrieve data from our array of test data (and also write back to it).&lt;/p&gt;
&lt;p&gt;Along the way, we will look at dependency injection and how it can help us to write more flexible and maintainable code.&lt;/p&gt;
&lt;p&gt;Without further ado, here is the code for &lt;code&gt;TestDataTaskRepository&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Repositories/TaskRepository.swift

import Foundation

class BaseTaskRepository {
  @Published var tasks = [Task]()
}

protocol TaskRepository: BaseTaskRepository {
  func addTask(_ task: Task)
  func removeTask(_ task: Task)
  func updateTask(_ task: Task)
}

class TestDataTaskRepository: BaseTaskRepository, TaskRepository, ObservableObject {
  override init() {
    super.init()
    self.tasks = testDataTasks
  }

  func addTask(_ task: Task) {
    tasks.append(task)
  }

  func removeTask(_ task: Task) {
    if let index = tasks.firstIndex(where: { $0.id == task.id }) {
      tasks.remove(at: index)
    }
  }

  func updateTask(_ task: Task) {
    if let index = self.tasks.firstIndex(where: { $0.id == task.id } ) {
      self.tasks[index] = task
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the &lt;code&gt;TaskRepository&lt;/code&gt; protocol defines a couple of methods to add, remove, and update tasks. The tasks themselves are held in an array of &lt;code&gt;Tasks&lt;/code&gt;, which is &lt;code&gt;@Published&lt;/code&gt;, so that our clients can easily subscribe to any updates. In &lt;code&gt;TestDataTaskRepository&lt;/code&gt;s initialiser, we fetch the actual test data. Obviously, any changes we make to the array of tasks are not going to be persisted anywhere - to change this, we&apos;re going to provide an implementation of &lt;code&gt;TaskRepository&lt;/code&gt; that is capable of reading and writing from / to disk.&lt;/p&gt;
&lt;p&gt;But before we can do that, we need to talk about dependency injection.&lt;/p&gt;
&lt;h2&gt;Dependency Injection&lt;/h2&gt;
&lt;p&gt;Comparing the architecture diagram with the code we&apos;ve got so far, it becomes obvious that we need to access the &lt;code&gt;TaskRepository&lt;/code&gt; from both of our view models. This is easier said than done, as the repository is stateful: it holds a collection of our tasks. If we&apos;d create an instance of the task repository in each of our views, we&apos;d quickly run out of sync.&lt;/p&gt;
&lt;p&gt;One way to resolve this situation is to make the repository a singleton. A lot has been said and written about singletons, and while there is nothing wrong with using singletons (Apple does it, too), I&apos;d like to take a different approach here and use dependency injection, because it will give us some nice benefits.&lt;/p&gt;
&lt;p&gt;One of the most elegant and lightweight dependency injection frameworks for Swift is &lt;a href=&quot;https://github.com/hmlongco/Resolver&quot;&gt;Resolver&lt;/a&gt; by &lt;a href=&quot;https://medium.com/@michaellong&quot;&gt;Michael Long&lt;/a&gt;, so let&apos;s add it to our project! You can either do this using Swift Package Manager, Carthage, or CocoaPods. As we&apos;ll need to add Firebase to our project as well at a later stage, let&apos;s choose CocoaPods (at the time of this writing, Firebase only supports CocoaPods and Carthage, with support for SPM &lt;a href=&quot;https://github.com/firebase/firebase-ios-sdk/issues/3136&quot;&gt;being worked&lt;/a&gt; on).&lt;/p&gt;
&lt;p&gt;Using Resolver is straightforward, and requires only three changes to our project:&lt;/p&gt;
&lt;p&gt;First, we&apos;ll have to add it to the project using CocoaPods:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;target &apos;MakeItSo&apos; do
  use_frameworks!

  pod &apos;Resolver&apos;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;re following along, don&apos;t forget to run &lt;code&gt;pod install&lt;/code&gt; in your project folder to install Resolver. After CocoaPods has finished installing Resolver and its dependencies, close the project in Xcode and open the workspace CocoaPods has created for you. Pro tip: run &lt;code&gt;xed .&lt;/code&gt; in the project folder - this will either open the project, or the workspace (depending on which one exists).&lt;/p&gt;
&lt;p&gt;Next, we need to register any classes we want to inject somewhere else. To do this, Resolver provides a convenient extension that we can hook into:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: AppDelegate+Resolving.swift

extension Resolver: ResolverRegistering {
  public static func registerAllServices() {
    register { TestDataTaskRepository() as TaskRepository }.scope(application)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code essentially says &quot;create an instance of &lt;code&gt;TastDataTaskRepository&lt;/code&gt; and inject it whereever a &lt;code&gt;Taskrepository&lt;/code&gt; instance is required&quot;.&lt;/p&gt;
&lt;p&gt;And finally, our view models need to be updated. Let&apos;s look at &lt;code&gt;TaskViewModel&lt;/code&gt; to understand the changes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: TaskListViewModel.swift

import Foundation
import Combine
import Resolver

class TaskListViewModel: ObservableObject {
  @Published var taskRepository: TaskRepository = Resolver.resolve()
  @Published var taskCellViewModels = [TaskCellViewModel]()

  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  init() {
    taskRepository.$tasks.map { tasks in
      tasks.map { task in
        TaskCellViewModel(task: task)
      }
    }
    .assign(to: \.taskCellViewModels, on: self)
    .store(in: &amp;amp;cancellables)
  }

  // ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we ask Resolver to provide an implementation for &lt;code&gt;TaskRepository&lt;/code&gt; by calling &lt;code&gt;Resolver.resolve()&lt;/code&gt;, after which we can use the &lt;code&gt;taskRepository&lt;/code&gt; property just like a regular property.&lt;/p&gt;
&lt;p&gt;With this in place, using a different implementation of &lt;code&gt;TaskRepository&lt;/code&gt; in our app is now a matter of changing the registration from &lt;code&gt;register { TestDataTaskRepository() as TaskRepository }.scope(application)&lt;/code&gt; to &lt;code&gt;register { SomeOtherTaskRepository() as TaskRepository }.scope(application)&lt;/code&gt; - it&apos;s as easy as that - no need to touch any other code!&lt;/p&gt;
&lt;h2&gt;Persistence pays off&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_2/implement_disk_repository/start/final/MakeItSo&quot;&gt;&lt;code&gt;stage_2/implement_disk_repository/start&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcworkspace&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;For the grand finale, let&apos;s build a &lt;code&gt;TaskRepository&lt;/code&gt; implementation that persists tasks on disk. To help keep our code as clean as possible, I decided to use &lt;a href=&quot;https://github.com/saoudrizwan/Disk&quot;&gt;Disk&lt;/a&gt;, a nice little framework that abstracts access to the iOS file system. It supports &lt;code&gt;Codable&lt;/code&gt;, so reading and writing from / to disk can be done in as little as one line of code. Have a look for yourself:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: Repositories/TaskRepository.swift

class LocalTaskRepository: BaseTaskRepository, TaskRepository, ObservableObject {
  override init() {
    super.init()
    loadData()
  }

  func addTask(_ task: Task) {
    self.tasks.append(task)
    saveData()
  }

  func removeTask(_ task: Task) {
    if let index = tasks.firstIndex(where: { $0.id == task.id }) {
      tasks.remove(at: index)
      saveData()
    }
  }

  func updateTask(_ task: Task) {
    if let index = self.tasks.firstIndex(where: { $0.id == task.id } ) {
      self.tasks[index] = task
      saveData()
    }
  }

  private func loadData() {
    if let retrievedTasks = try? Disk.retrieve(&quot;tasks.json&quot;, from: .documents, as: [Task].self) { //(1)
      self.tasks = retrievedTasks
    }
  }

  private func saveData() {
    do {
      try Disk.save(self.tasks, to: .documents, as: &quot;tasks.json&quot;) //(2)
    }
    catch let error as NSError {
      fatalError(&quot;&quot;&quot;
        Domain: \(error.domain)
        Code: \(error.code)
        Description: \(error.localizedDescription)
        Failure Reason: \(error.localizedFailureReason ?? &quot;&quot;)
        Suggestions: \(error.localizedRecoverySuggestion ?? &quot;&quot;)
        &quot;&quot;&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of this code should be familiar to you by now, the only differences are some calls to &lt;code&gt;saveData()&lt;/code&gt; and &lt;code&gt;loadData()&lt;/code&gt;. Reading a collection of &lt;code&gt;Codable&lt;/code&gt; objects from disk is a one-liner with Disk: &lt;code&gt;try? Disk.retrieve(&quot;tasks.json&quot;, from: .documents, as: [Task].self)&lt;/code&gt;. Storing data isn&apos;t much more complicated as well: &lt;code&gt;try Disk.save(self.tasks, to: .documents, as: &quot;tasks.json&quot;)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s worth noting that we&apos;ll have to update the code in &lt;code&gt;TaskListViewModel&lt;/code&gt; a bit to reflect the fact that our view model will get updated automatically when making changes to the repository:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// File: ViewModels/TaskListViewModel.swift

class TaskListViewModel: ObservableObject {
  @Published var taskRepository: TaskRepository = Resolver.resolve()
  @Published var taskCellViewModels = [TaskCellViewModel]()

  private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()

  init() {
    taskRepository.$tasks.map { tasks in
      tasks.map { task in
        TaskCellViewModel(task: task) //(2)
      }
    }
    .assign(to: \.taskCellViewModels, on: self)
    .store(in: &amp;amp;cancellables)
  }

  func removeTasks(atOffsets indexSet: IndexSet) {
    // remove from repo
    let viewModels = indexSet.lazy.map { self.taskCellViewModels[$0] }
    viewModels.forEach { taskCellViewModel in
      taskRepository.removeTask(taskCellViewModel.task) //(1)
    }
  }

  func addTask(task: Task) {
    taskRepository.addTask(task)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So instead of removing a task from the repository, and then also removing it from the local &lt;code&gt;taskCellViewModels&lt;/code&gt; collection, we just need to remove it from the repository. This will trigger the subscriber we set up in the initialiser (2), which will duly transform the input models into view models. The same applies to adding new tasks: just add them to the repository, the subscriber will automatically update the local collection of view models.&lt;/p&gt;
&lt;h2&gt;Finish line&lt;/h2&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
If you&apos;re following along, check out the tag
&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_2/finish_line/final/MakeItSo&quot;&gt;&lt;code&gt;stage_2/finish_line&lt;/code&gt;&lt;/a&gt;
and open &lt;code&gt;MakeItSo.xcworkspace&lt;/code&gt; in the &lt;code&gt;final&lt;/code&gt; folder.
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;It&apos;s now time to run the application and enjoy the results of our hard work (it wasn&apos;t actually that hard, was it). Go ahead and hit that &lt;strong&gt;Run&lt;/strong&gt; button to launch the app on your Simulator or your phone, and add some tasks!&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;With very little code, we were able to build a fully functional task list application. In the next part of this series, we&apos;re going to look at how to connect the application to Firebase to persist our data in Cloud Firestore.&lt;/p&gt;
&lt;h2&gt;Homework&lt;/h2&gt;
&lt;p&gt;As a little challenge while you wait for the next article in this series, why don&apos;t you try to implement support for task priorities?&lt;/p&gt;
&lt;p&gt;The iOS Reminders app displays exclamation marks to the left of the task title to indicate a task&apos;s priority. Changing the task priority is a bit less discoverable: you&apos;ll need to tap on the info icon of a task to open its details screen, scroll down a little bit, and then choose the desired task priority from a picker.&lt;/p&gt;
&lt;p&gt;Feel free to implement this behaviour, or come up with your own solution.&lt;/p&gt;
&lt;p&gt;If you&apos;d like to showcase your implementation, do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fork the &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/&quot;&gt;repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Check out the &lt;a href=&quot;https://github.com/peterfriese/MakeItSo/tree/stage_2/finish_line&quot;&gt;stage 2 finish line tag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Implement your solution&lt;/li&gt;
&lt;li&gt;Send a PR&lt;/li&gt;
&lt;li&gt;(optional) tweet a link to your PR as a reply to &lt;a href=&quot;https://twitter.com/peterfriese/status/1221715939651092487&quot;&gt;this tweet&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I will then go through the solutions and re-tweet the ones that are the most creative.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and have fun implementing the homework challenge!&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/peterfriese/MakeItSo/&quot;&gt;Source code for the sample app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/xcode/supporting_dark_mode_in_your_interface&quot;&gt;Supporting Dark Mode in Your Interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/better-programming/modern-dependency-injection-in-swift-952286b308be&quot;&gt;Modern Dependency Injection in Swift&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://quickbirdstudios.com/blog/swiftui-architecture-redux-mvvm/&quot;&gt;SwiftUI Architectures: Model-View, Redux &amp;amp; MVVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.slideshare.net/TaiLunTseng/mvvm-with-swiftui-and-combine&quot;&gt;MVVM with SwiftUI and Combine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hmlongco/Resolver&quot;&gt;Resolver on Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/saoudrizwan/Disk&quot;&gt;Disk on Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Essential Xcode Shortcuts for More Efficient Coding</title><link>https://peterfriese.dev/blog/2019/xcode-shortcuts/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2019/xcode-shortcuts/</guid><description>Learn these essential shortcuts to become a more productive developer</description><pubDate>Mon, 07 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import codeCompletion5 from &apos;./xcode-shortcuts/code_completion_5.mp4&apos;;
import callHierarchy from &apos;./xcode-shortcuts/call_hierarchy.mp4&apos;;
import contentOutline from &apos;./xcode-shortcuts/content_outline.mp4&apos;;
import openQuickly from &apos;./xcode-shortcuts/open_quickly.mp4&apos;;&lt;/p&gt;
&lt;p&gt;As developers, we spend a considerable amount of time in our IDE, so it’s worth becoming an expert in using it. Simply being able to edit code doesn’t cut it - you need to become proficient.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Pragmatic_Programmer&quot;&gt;The Pragmatic Programmer&lt;/a&gt;, the authors recommend choosing one editor, and using it for all editing tasks. The reasoning behind their recommendation is simple: once you’ve memorised all keyboard shortcuts, your productivity will soar.&lt;/p&gt;
&lt;p&gt;While you could certainly use &lt;em&gt;just one&lt;/em&gt; editor like VS Code for &lt;em&gt;all&lt;/em&gt; your source code editing tasks, as iOS / macOS / watchOS / tvOS developers, we’re somewhat bound to use Xcode, as it includes so much more tools than just the source code editor.&lt;/p&gt;
&lt;p&gt;Nevertheless, it pays off to know your way around Xcode and be proficient in the core editing and navigation commands to increase your productivity.&lt;/p&gt;
&lt;p&gt;I’ve collected a list of my favourite keybindings and mouse commands - hope you’ll find them useful as well! All keybindings listed here use Xcode’s default keybinding - with one notable exception.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⌘ = Command&lt;/li&gt;
&lt;li&gt;⌥ = Option/Alt&lt;/li&gt;
&lt;li&gt;⇧ = Shift&lt;/li&gt;
&lt;li&gt;⌃ = Control&lt;/li&gt;
&lt;li&gt;←→ ↑↓ = Arrow keys&lt;/li&gt;
&lt;li&gt;↩ = Enter&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Editing&lt;/h2&gt;
&lt;p&gt;Let’s begin with editing - this is one of the most basic activities, so every little improvement will boost your productivity!&lt;/p&gt;
&lt;h3&gt;Code Completion (&lt;code&gt;⌃ + Space&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;It’s hard to imagine working without code completion - I use it all of the time to explore APIs, and to save time when typing.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={codeCompletion5} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h3&gt;Moving Lines (&lt;code&gt;⌥ + ⌘ + [&lt;/code&gt; and &lt;code&gt;⌥ + ⌘ + ]&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Moving single lines or entire blocks around in your code is immensely useful, for example when organising code that belongs together. Xcode automatically takes care of indentation \o/&lt;/p&gt;
&lt;h3&gt;Delete Entire Line (&lt;code&gt;⌘ + D&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;I often need to delete an entire line - Xcode offers an editor command for this, but unfortunately it is not bound to a key combo in the default key bindings. After some consideration I decided to make an exception and include a custom key combo in this list, just because I think it is so useful. To define custom keybindings, go to Xcode settings (press ⌘ + ,), navigate to the &lt;em&gt;Key Bindings&lt;/em&gt; tab, and use the filter field to search for “Delete Line”. Double click into the &lt;em&gt;Key&lt;/em&gt; field, then press your preferred key combo. I use &lt;code&gt;⌘ + D&lt;/code&gt; (for &lt;strong&gt;D&lt;/strong&gt;elete Line).&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Comment Current Line / Block (&lt;code&gt;⌘ + /&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Useful for temporarily deactivating a bunch of lines in your code. But please remember to not check this in. Your version control system is a better tool for going back in history than a chunk of commented lines that nobody know what they&apos;re here for!&lt;/p&gt;
&lt;h3&gt;Balance Indentation (&lt;code&gt;⌃ + I&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Just like many of the other commands above, this works both on single lines and on entire blocks of code. Useful after pasting code from elsewhere (did somebody say Stack Overflow?).&lt;/p&gt;
&lt;h2&gt;Navigation&lt;/h2&gt;
&lt;p&gt;Estimates say that you read a lot more code than you will write, and I wholeheartedly agree. You read code all of the time: when digging into a new codebase, when debugging, and even when writing new code! Here are a couple of commands that will help you stay on top of your navigation.&lt;/p&gt;
&lt;h3&gt;Going Back and Forth (&lt;code&gt;⌃ + ⌘ + ←&lt;/code&gt; and &lt;code&gt;⌃ + ⌘ + →&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Xcode tracks your entire movement history as soon as you open or create a project. Any file you go to, any symbol you look up - it all is appended to the IDE’s navigation stack. Use &lt;code&gt;⌃ + ⌘ + ←&lt;/code&gt; and &lt;code&gt;⌃ + ⌘ + →&lt;/code&gt; to go back and forth between the source code locations you visited.&lt;/p&gt;
&lt;h3&gt;Jump to Definition (&lt;code&gt;⌃ + ⌘ + J&lt;/code&gt; or &lt;code&gt;⌃ + ⌘ + Click&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;This comes in really handy when learning new APIs or navigating an unknown codebase. Not sure what &lt;code&gt;ObservedObject&lt;/code&gt; does? Navigate to its definition to see which methods and properties it has, which interfaces it implements and which class it inherits from. For most of Apple’s APIs, you’ll also see the documentation (of course, you can always invoke &lt;em&gt;Quick Help&lt;/em&gt; using &lt;code&gt;⌥ + click&lt;/code&gt;)&lt;/p&gt;
&lt;h3&gt;Find Selected Symbol in Workspace (&lt;code&gt;⇧ + ⌃ + ⌘ + F&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;This is essentially the opposite of &lt;em&gt;Jump to definition&lt;/em&gt;, and it’s particularly helpful trying to understand how an API is used in the codebase. It also gives you an feeling for how much grief you’re going to cause your teammates (or other API consumers) when refactoring an API…!&lt;/p&gt;
&lt;h3&gt;Find Call Hierarchy (&lt;code&gt;⇧ + ⌃ + ⌘ + H&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Similar to &lt;em&gt;Find selected symbol in workspace&lt;/em&gt;, but with a focus on methods, this shortcut will open the &lt;em&gt;Call Hierarchy&lt;/em&gt; view to show any places in your code that call the specified method, as well as any methods that call those methods in turn, and so on.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={callHierarchy} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h3&gt;Open Quickly (&lt;code&gt;⇧ + ⌘ + O&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;If you take away only one thing from reading this article, are it this one. &lt;em&gt;Open quickly&lt;/em&gt; allows you to quickly jump into any source location in your project / workspace. Just start typing the name of any class, interface, function, method, enum, … in your project (and any SDK you imported) and it will populate the popup with a list of symbols that match the search term. It performs some fuzzy searching, so you can type partial search terms like &lt;code&gt;UIAD&lt;/code&gt; to search for &lt;code&gt;UIApplicationDelegate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={openQuickly} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h3&gt;Jump to Line (&lt;code&gt;⌘ + L&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Useful for when, you know, you need to navigate to a specific line in the current file.&lt;/p&gt;
&lt;h3&gt;Document outline (&lt;code&gt;⌃ + 6&lt;/code&gt; or &lt;code&gt;⌘ + hovering the minimap&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;These actually are two different pieces of UI, but they basically do the same:&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;⌃ + 6&lt;/code&gt; to drop down a menu from the &lt;em&gt;Jump Bar&lt;/em&gt; (the area directly above the code editor) with all the symbols in the current source file. You can then navigate using the arrow keys, or by typing to filter the list of symbols. Useful not just for large files.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;minimap&lt;/strong&gt; was a welcome addition to the Xcode editor in one of the recent releases of Xcode, and provides a high-level visual overview of your code (Apple, if you’re reading this: can we please get a way to zoom the minimap, e.g. by dragging the divider?). When hovering over the minimap, it will display a flout with the name of the symbol the mouse cursor is hovering over. Press ⌘ while hovering to see a list of all symbols.&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={contentOutline} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;View Management&lt;/h2&gt;
&lt;p&gt;Let switch gears and take a look at view management. The editor takes up most of the screen real estate in Xcode, and it is surrounded by a number of views that display contextual information.&lt;/p&gt;
&lt;h3&gt;Toggle Canvas / SwiftUI Preview (&lt;code&gt;⌥ + ⌘ + ↩&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;When writing SwiftUI code, Canvas provides a live preview of the UI you’re building. Apples has gone to great lengths to make this a two-way experience, i.e. any changes you make in the preview (e.g. by moving UI elements, or adding new ones) will be reflected in the code editor as well, and vice versa. This allows for rapid prototyping with fast turn-around times. You no longer need to launch your application to check if the button looks better in red or in blue.&lt;/p&gt;
&lt;p&gt;However, we don’t always need to see the preview, so it’s good to be able to toggle it as required. Use &lt;code&gt;⌥ + ⌘ + ↩&lt;/code&gt; to hide or show the Canvas.&lt;/p&gt;
&lt;h3&gt;Toggle Views&lt;/h3&gt;
&lt;p&gt;Xcode has three main areas surrounding the code editor, which can be toggled to make more space for editing (or showing context information, just as required):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Left: Navigator (&lt;code&gt;⌘ + 0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Right: Inspectors (&lt;code&gt;⌘ + ⌥ + 0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Bottom: Debug (&lt;code&gt;⇧ + ⌘ + Y&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;And that was a quick tour of my favourite productivity shortcuts in Xcode 11. Here is a table to give you a quick overview of the commands and key bindings:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Editor&lt;/td&gt;
&lt;td&gt;Code completion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌃ + Space&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor&lt;/td&gt;
&lt;td&gt;Move line up&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌥ + ⌘ + [&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor&lt;/td&gt;
&lt;td&gt;Move line down&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌥ + ⌘ + ]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor&lt;/td&gt;
&lt;td&gt;Delete entire line (*)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌘ + D&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor&lt;/td&gt;
&lt;td&gt;Comment line / selection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌘ + /&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor&lt;/td&gt;
&lt;td&gt;Balance indent&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌃ + I&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Go back&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌃ + ⌘ + ←&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Go forward&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌃ + ⌘ + →&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Jump to definition&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌃ + ⌘ + J&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Find selected symbol in workspace&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⇧ + ⌃ + ⌘ + F&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Find call hierarchy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⇧ + ⌃ + ⌘ + H&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Open quickly&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⇧ + ⌘ + O&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Jump to line&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌘ + L&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;td&gt;Show document items&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌃ + 6&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Views&lt;/td&gt;
&lt;td&gt;Toggle canvas&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌥ + ⌘ + ↩&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Views&lt;/td&gt;
&lt;td&gt;Toggle Navigator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌘ + 0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Views&lt;/td&gt;
&lt;td&gt;Toggle Inspectors&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌘ + ⌥ + 0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Views&lt;/td&gt;
&lt;td&gt;Toogle Debug area&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⇧ + ⌘ + Y&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings&lt;/td&gt;
&lt;td&gt;Open settings dialog&lt;/td&gt;
&lt;td&gt;&lt;code&gt;⌘ + ,&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>SwiftUI + Combine = ❤️</title><link>https://peterfriese.dev/blog/2019/swift-combine-love/</link><guid isPermaLink="true">https://peterfriese.dev/blog/2019/swift-combine-love/</guid><description>Learn how SwiftUI and Combine will help you build better apps and have more fun along the way.</description><pubDate>Fri, 13 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { YouTube } from &apos;astro-embed&apos;;
import InfoBox from &apos;../../../components/InfoBox.astro&apos;;&lt;/p&gt;
&lt;p&gt;import demo from &apos;./swift-combine-love/demo.mp4&apos;;&lt;/p&gt;
&lt;p&gt;One of the biggest announcements at WWDC 2019 was SwiftUI - its declarative approach makes building UIs a breeze, and it&apos;s easy to see why people are so excited about it. The hidden gem, however, was the Combine framework, which didn’t get as much fanfare as I think it deserves.&lt;/p&gt;
&lt;p&gt;In this article, we will take a closer look at how to use SwiftUI and Combine together, build better apps, and have more fun along the way.&lt;/p&gt;
&lt;p&gt;Ready? Let’s go!&lt;/p&gt;
&lt;h2&gt;What you are going to learn&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;What the Combine framework is, and how to integrate it with SwiftUI&lt;/li&gt;
&lt;li&gt;What publishers, subscribers, and operators are, and how to use them&lt;/li&gt;
&lt;li&gt;How to organise your code&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;In the Beginning…&lt;/h2&gt;
&lt;p&gt;To help us reason about SwiftUI and Combine, we’re going to use a simple sign-up screen which lets users enter a username and password to create a new account in an application. In a later article, we will add a login screen to demonstrate some of the additional benefits of using Combine.&lt;/p&gt;
&lt;p&gt;The data model for the sign-up screen is simple enough:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users need to enter their desired username&lt;/li&gt;
&lt;li&gt;They also need to pick a password&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The requirements for username and password are pretty straight forward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The username must contain at least 3 characters&lt;/li&gt;
&lt;li&gt;The password must be non-empty and strong enough&lt;/li&gt;
&lt;li&gt;Also, to make sure the user didn’t accidentally mistype, they need to type their password a second time, and both of these passwords need to match up&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s put this down in code!&lt;/p&gt;
&lt;p&gt;I’ve decided to use an MVVM architecture - this results in a clean code base and will make it easier to add new functionality to the app. First, let’s define the &lt;em&gt;ViewModel&lt;/em&gt; which has a couple of properties taking the user’s input (such as the username and passwords), and - for the time being - a property to expose the result of any business logic we’re going to implement shortly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class UserViewModel: ObservableObject {
  // Input
  @Published var username = &quot;&quot;
  @Published var password = &quot;&quot;
  @Published var passwordAgain = &quot;&quot;

  // Output
  @Published var isValid = false
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the sign-up screen, we use a &lt;code&gt;Form&lt;/code&gt; with several &lt;code&gt;Section&lt;/code&gt;s for the various input fields, which gives us a clean look and feel. It gets the job done, but doesn’t look very exciting. In the next episode, we’re going to brush it up to demonstrate how SwiftUI and Combine make it possible to make changes to your UI without having to modify the underlying business logic.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ContentView: View {

  @ObservedObject private var userViewModel = UserViewModel()

  var body: some View {
    Form {
      Section {
        TextField(&quot;Username&quot;, text: $userViewModel.username)
          .autocapitalization(.none)
        }
        Section {
          SecureField(&quot;Password&quot;, text: $userViewModel.password)
          SecureField(&quot;Password again&quot;, text: $userViewModel.passwordAgain)
       }
       Section {
         Button(action: { }) {
           Text(&quot;Sign up&quot;)
         }.disabled(!userViewModel.valid)
       }
     }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how we’re using SwiftUI bindings to access the properties of our view model. The &lt;em&gt;Sign up&lt;/em&gt; button is bound to the &lt;code&gt;isValid&lt;/code&gt; output property of the view model. As this defaults to &lt;code&gt;false&lt;/code&gt;, the button is initially disabled, which is what we want - after all, the user shouldn’t be able to create an account with an empty username and password!&lt;/p&gt;
&lt;p&gt;This is how the UI looks so far:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Introducing Combine&lt;/h2&gt;
&lt;p&gt;Before implementing the validation logic for our sign-up form, let’s spend some time understanding how the Combine framework works.&lt;/p&gt;
&lt;p&gt;According to the Apple documentation:&lt;/p&gt;
&lt;p&gt;&amp;lt;InfoBox&amp;gt;
The Combine framework provides a declarative Swift API for processing values over time. These
values can represent many kinds of asynchronous events. Combine declares publishers to expose
values that can change over time, and subscribers to receive those values from the publishers.
(&lt;a href=&quot;https://developer.apple.com/documentation/combine&quot;&gt;source&lt;/a&gt;)
&amp;lt;/InfoBox&amp;gt;&lt;/p&gt;
&lt;p&gt;Let’s take a closer look at a couple of key concepts here to understand what this means and how it helps us.&lt;/p&gt;
&lt;h3&gt;Publishers&lt;/h3&gt;
&lt;p&gt;Publishers send values to one or more subscribers. They conform to the &lt;code&gt;Publisher&lt;/code&gt; protocol, and declare the type of output and any error they produce:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public protocol Publisher {
  associatedtype Output
  associatedtype Failure : Error
  func receive&amp;lt;S&amp;gt;(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A publisher can send any number of values over time, or fail with an error. The associated type &lt;code&gt;Output&lt;/code&gt; defines which kinds of values a publisher can send, whereas the associated type &lt;code&gt;Failure&lt;/code&gt; defines the type of error it may fail with. A publisher can declare it never fails by specifying the &lt;code&gt;Never&lt;/code&gt; associated type.&lt;/p&gt;
&lt;h3&gt;Subscribers&lt;/h3&gt;
&lt;p&gt;Subscribers, on the other hand, subscribe to one specific publisher instance, and receive a stream of values, until the subscription is cancelled.&lt;/p&gt;
&lt;p&gt;They conform to the &lt;code&gt;Subscriber&lt;/code&gt; protocol. In order to subscribe to a publisher, the subscriber’s associated &lt;code&gt;Input&lt;/code&gt; and &lt;code&gt;Failure&lt;/code&gt; types must conform to the publisher’s associated &lt;code&gt;Output&lt;/code&gt; and &lt;code&gt;Failure&lt;/code&gt; types.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public protocol Subscriber : CustomCombineIdentifierConvertible {
  associatedtype Input
  associatedtype Failure : Error

  func receive(subscription: Subscription)
  func receive(_ input: Self.Input) -&amp;gt; Subscribers.Demand
  func receive(completion: Subscribers.Completion&amp;lt;Self.Failure&amp;gt;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Operators&lt;/h3&gt;
&lt;p&gt;Publishers and subscribers are the backbone of SwiftUI’s two-way synchronisation between the UI and the underlying model. I think you will agree that it has never been easier to keep your UI and model in sync than with SwiftUI, and this is all thanks to this part of the Combine framework.&lt;/p&gt;
&lt;p&gt;Operators, however, are Combine’s superpower. They are methods that operate on a &lt;code&gt;Publisher&lt;/code&gt;, perform some computation, and produce another &lt;code&gt;Publisher&lt;/code&gt; in return.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For example, you can use the &lt;code&gt;filter&lt;/code&gt; operator to ignore values based on certain conditions.&lt;/li&gt;
&lt;li&gt;Or, if you need to perform an expensive task (such as fetching information across the network), you could use the &lt;code&gt;debounce&lt;/code&gt; operator to wait until the user stops typing&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;map&lt;/code&gt; operator allows you to transform input values of a certain type into output values of a different type&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Validating the Username&lt;/h2&gt;
&lt;p&gt;With this in mind, let’s implement a simple validation to make sure the user entered a name that has at least three characters.&lt;/p&gt;
&lt;p&gt;All properties on our view model are wrapped with the &lt;code&gt;@Published&lt;/code&gt; property wrapper. This means each property has its own publisher, which we can subscribe to.&lt;/p&gt;
&lt;p&gt;To indicate whether a username is valid, we transform the user’s input from &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;Bool&lt;/code&gt; using the &lt;code&gt;map&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$username
  .debounce(for: 0.8, scheduler: RunLoop.main)
  .removeDuplicates()
  .map { input in
    return input.count &amp;gt;= 3
  }
  .assign(to: \.valid, on: self)
  .store(in: &amp;amp;cancellableSet)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of this transformation is then consumed by the &lt;code&gt;assign&lt;/code&gt; subscriber, which - as the name implies - assigns the received value to the &lt;code&gt;valid&lt;/code&gt; output property of our view model.&lt;/p&gt;
&lt;p&gt;Thanks to the binding we configured earlier in &lt;code&gt;ContentView.swift&lt;/code&gt;, SwiftUI will automatically update the UI whenever this property changes. We will later see why this approach is a bit problematic, but for now, it works just fine.&lt;/p&gt;
&lt;p&gt;You might wonder what’s this fancy business with the &lt;code&gt;debounce&lt;/code&gt; and &lt;code&gt;removeDuplicate&lt;/code&gt; operators? Well, these are part of what makes Combine such a useful tool for connecting UIs to the underlying business logic. In all user interfaces, we have to deal with the fact that the user might type faster than we can fetch the information they’re requesting. For example, when typing their username, it is not necessary to check whether the username is valid or available for every single letter the user types. It is sufficient to perform this check only once they stop typing (or pause for a moment).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;debounce&lt;/code&gt; operator lets us specify that we want to wait for a pause in the delivery of events, for example when the user stops typing.&lt;/p&gt;
&lt;p&gt;Similarly, the &lt;code&gt;removeDuplicates&lt;/code&gt; operator will publish events only if they are different from any previous events. For example, if the user first types &lt;code&gt;john&lt;/code&gt;, then &lt;code&gt;joe&lt;/code&gt;, and then &lt;code&gt;john&lt;/code&gt; again, we will only receive &lt;code&gt;john&lt;/code&gt; once. This helps make our UI work more efficiently.&lt;/p&gt;
&lt;p&gt;The result of this chain of calls is a &lt;code&gt;Cancellable&lt;/code&gt;, which we can use to cancel processing if required (useful for longer-running chains). We’ll store this (and all the others that we will create later on) into a &lt;code&gt;Set&amp;lt;AnyCancellable&amp;gt;&lt;/code&gt;, so we can clean up upon &lt;code&gt;deinit&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Validating the Password(s)&lt;/h2&gt;
&lt;p&gt;Let’s now switch gears and look into how we can perform multi-staged validation logic. This is required as the password fields on our form need to meet multiple requirements: they must not be empty, they must match up, and (most importantly) the chosen password must be strong enough. In addition to transforming the input values into a &lt;code&gt;Bool&lt;/code&gt; to indicate whether the passwords meet our requirements, we also want to provide some guidance for the user by returning an appropriate warning message.&lt;/p&gt;
&lt;p&gt;Let’s take this one step at a time and begin by implementing the pipeline for validating the passwords the user entered.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private var isPasswordEmptyPublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; {
    // (1)
    $password
      .debounce(for: 0.8, scheduler: RunLoop.main)
      .removeDuplicates()
      .map { password in
        return password == &quot;&quot;
      }
      .eraseToAnyPublisher()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Checking whether the password is empty is pretty straightforward, and you will notice this method is very similar to our implementation of the username validation. However, instead of directly assigning the result of the transformation to the &lt;code&gt;isValid&lt;/code&gt; output property, we return an &lt;code&gt;AnyPublisher&amp;lt;Bool,  Never&amp;gt;&lt;/code&gt;. This is so we can later combine multiple publishers into a multi-stage chain before we subscribe to the final result (valid or not).&lt;/p&gt;
&lt;p&gt;To verify if two separate properties contain equal strings, we make use of the &lt;code&gt;CombineLatest&lt;/code&gt; operator. Remember that the properties bound to the respective &lt;code&gt;SecureField&lt;/code&gt; fire each time the user enters a character, and we want to compare the &lt;em&gt;latest&lt;/em&gt; value for each of those fields. &lt;code&gt;CombineLatest&lt;/code&gt; lets us do that.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private var arePasswordsEqualPublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; {
    Publishers.CombineLatest($password, $passwordAgain)
      .debounce(for: 0.2, scheduler: RunLoop.main)
      .map { password, passwordAgain in
        return password == passwordAgain
      }
      .eraseToAnyPublisher()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To compute the password strength, we use &lt;a href=&quot;https://github.com/jasonnam/Navajo-Swift&quot;&gt;Navajo Swift&lt;/a&gt;, a Swift port of &lt;a href=&quot;https://twitter.com/mattt?lang=en&quot;&gt;Matt&apos;s&lt;/a&gt; excellent &lt;a href=&quot;https://github.com/mattt/Navajo&quot;&gt;Navajo&lt;/a&gt; library (2), and convert the resulting enum into a &lt;code&gt;Bool&lt;/code&gt; by chaining on another publisher (&lt;code&gt;isPasswordStrongEnoughPublisher&lt;/code&gt;). This is the first time we subscribe to one of our own publishers, and very nicely shows how we can combine multiple publishers to produce the required output.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private var passwordStrengthPublisher: AnyPublisher&amp;lt;PasswordStrength, Never&amp;gt; {
    $password
      .debounce(for: 0.2, scheduler: RunLoop.main)
      .removeDuplicates()
      .map { input in
        return Navajo.strength(ofPassword: input) // (2)
      }
      .eraseToAnyPublisher()
  }

  private var isPasswordStrongEnoughPublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; {
    passwordStrengthPublisher
      .map { strength in
        switch strength {
        case .reasonable, .strong, .veryStrong:
          return true
        default:
          return false
        }
      }
      .eraseToAnyPublisher()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In case you’re wondering why we need to call &lt;code&gt;eraseToAnyPublisher()&lt;/code&gt; at the end of each chain: this performs some type erasure that makes sure we don’t end up with some crazy nested return types.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Great - so we now know a lot about the passwords the user entered, let’s boil this down to the single thing we really want to know: &lt;em&gt;is this a valid password&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;As you might have guessed, we will need to use the &lt;code&gt;CombineLatest&lt;/code&gt; operator, but as this time around we have three parameters, we’ll use &lt;code&gt;CombineLatest3&lt;/code&gt;, which takes three input parameters.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  enum PasswordCheck {
    case valid
    case empty
    case noMatch
    case notStrongEnough
  }

  private var isPasswordValidPublisher: AnyPublisher&amp;lt;PasswordCheck, Never&amp;gt; {
    Publishers.CombineLatest3(isPasswordEmptyPublisher, arePasswordsEqualPublisher, isPasswordStrongEnoughPublisher)
      .map { passwordIsEmpty, passwordsAreEqual, passwordIsStrongEnough in
        if (passwordIsEmpty) {
          return .empty
        }
        else if (!passwordsAreEqual) {
          return .noMatch
        }
        else if (!passwordIsStrongEnough) {
          return .notStrongEnough
        }
        else {
          return .valid
        }
      }
      .eraseToAnyPublisher()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main reason why we map the three booleans to a single enum is that we want to be able to produce a suitable warning message depending on the result of the validation. Telling the user that their password is no good is not very helpful, is it? Much better if we tell them &lt;em&gt;why&lt;/em&gt; it’s not valid.&lt;/p&gt;
&lt;h2&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;To compute the final result of the validation, we need to combine the result of the username validation with the result of the password validation. However, before we can do this, we need to refactor the username validation so it also returns a publisher that we include in our validation chain.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private var isUsernameValidPublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; {
    $username
      .debounce(for: 0.8, scheduler: RunLoop.main)
      .removeDuplicates()
      .map { input in
        return input.count &amp;gt;= 3
      }
      .eraseToAnyPublisher()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this done, we can implement the final stage of the form validation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  private var isFormValidPublisher: AnyPublisher&amp;lt;Bool, Never&amp;gt; {
    Publishers.CombineLatest(isUsernameValidPublisher, isPasswordValidPublisher)
      .map { usernameIsValid, passwordIsValid in
        return usernameIsValid &amp;amp;&amp;amp; (passwordIsValid == .valid)
      }
    .eraseToAnyPublisher()
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By now, this should look rather familiar to you.&lt;/p&gt;
&lt;h2&gt;Updating the UI&lt;/h2&gt;
&lt;p&gt;None of this would be very useful without connecting it to the UI. To drive the state of the &lt;em&gt;Sign up&lt;/em&gt; button, we need to update the &lt;code&gt;isValid&lt;/code&gt; output property on our view model.&lt;/p&gt;
&lt;p&gt;To do so, we simply subscribe to the &lt;code&gt;isFormValidPublisher&lt;/code&gt; and assign the values it publishes to the &lt;code&gt;isValid&lt;/code&gt; property:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  init() {
    isFormValidPublisher
      .receive(on: RunLoop.main)
      .assign(to: \.isValid, on: self)
      .store(in: &amp;amp;cancellableSet)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As this code interfaces with the UI, it needs to run on the UI thread. We can tell SwiftUI to execute this code on the UI thread by calling &lt;code&gt;receive(on: RunLoop.main)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s finish off with binding the warning message output properties to the UI to help guide the user through filling out the sign-up form.&lt;/p&gt;
&lt;p&gt;First, we subscribe to the respective publishers to learn when the &lt;code&gt;username&lt;/code&gt; vs. &lt;code&gt;password&lt;/code&gt; properties are invalid. Again, we need to make sure this happens on the UI thread, so we’ll call &lt;code&gt;receive(on:)&lt;/code&gt; and pass the main run loop.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  init() {
    // ...
    isUsernameValidPublisher
      .receive(on: RunLoop.main)
      .map { valid in
        valid ? &quot;&quot; : &quot;username must at leat have 3 characters&quot;
      }
      .assign(to: \.usernameMessage, on: self)
      .store(in: &amp;amp;cancellableSet)

    isPasswordValidPublisher
      .receive(on: RunLoop.main)
      .map { passwordCheck in
        switch passwordCheck {
        case .empty:
          return &quot;Password must not be empty&quot;
        case .noMatch:
          return &quot;Passwords don&apos;t match&quot;
        case .notStrongEnough:
          return &quot;Password not strong enough&quot;
        default:
          return &quot;&quot;
        }
      }
      .assign(to: \.passwordMessage, on: self)
      .store(in: &amp;amp;cancellableSet)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we need to bind the output properties &lt;code&gt;usernameMessage&lt;/code&gt; and &lt;code&gt;passwordMessage&lt;/code&gt; to the UI. The section footers are a convenient place to show error messages, and we can make them stand out nicely by colouring them in red:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  var body: some View {
    Form {
      Section(footer: Text(userModel.usernameMessage).foregroundColor(.red)) {
        // ...
      }
      Section(footer: Text(userModel.passwordMessage).foregroundColor(.red)) {
        // ...
      }
      // ...
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the result of our hard work in all its glory:&lt;/p&gt;
&lt;p&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
&amp;lt;source src={demo} type=&quot;video/mp4&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Building UIs with SwiftUI is a breeze. Apple has sweated the details to give us the tools that make building UIs more productive than ever before. On top of that, SwiftUI follows Apple’s &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/&quot;&gt;Human Interface Guidelines&lt;/a&gt; , automatically adapts to dark mode and has accessibility built right in. All of this helps to build better, and more inclusive apps in less time - what’s not to like?&lt;/p&gt;
&lt;p&gt;Using Combine results in a cleaner and more modular code that (as we will see in the next episode) is more maintainable and easier to extend.&lt;/p&gt;
&lt;p&gt;Of course, like every new paradigm, there is a learning curve, and it will take some time to get to grips with functional reactive programming. But I am convinced it’s worth the effort. By releasing SwiftUI and Combine, Apple have put their sign of approval on Functional Reactive Programming, and soon it will no longer be a technique that only a few development teams use. We will see more and more learning resources to help people get started. Also (and this has been a bit of a sore point in the latest beta releases of Xcode), tooling will get better over time, helping developers to be more productive.&lt;/p&gt;
&lt;p&gt;Now is a great time to get started with SwiftUI and Combine - try using them in one of your next projects to get a head start!&lt;/p&gt;
&lt;h2&gt;Where to go from here?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/peterfriese/SwiftUI-Combine&quot;&gt;Source code for this article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/722/&quot;&gt;Introducing Combine - WWDC 2019 - Videos - Apple Developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/721/&quot;&gt;Combine in Practice - WWDC 2019 - Videos - Apple Developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rotato.xyz/&quot;&gt;Rotato&lt;/a&gt; - create amazing mock-ups&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>