<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Peter Friese</title>
    <description>The latest articles on DEV Community by Peter Friese (@peterfriese).</description>
    <link>https://dev.to/peterfriese</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F146231%2Fe8a3c651-efea-45dc-8d6d-2d65e937a443.png</url>
      <title>DEV Community: Peter Friese</title>
      <link>https://dev.to/peterfriese</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/peterfriese"/>
    <language>en</language>
    <item>
      <title>Essential Xcode Shortcuts for More Efficient Coding</title>
      <dc:creator>Peter Friese</dc:creator>
      <pubDate>Mon, 07 Oct 2019 17:27:00 +0000</pubDate>
      <link>https://dev.to/peterfriese/essential-xcode-shortcuts-for-more-efficient-coding-1n3</link>
      <guid>https://dev.to/peterfriese/essential-xcode-shortcuts-for-more-efficient-coding-1n3</guid>
      <description>&lt;p&gt;As developers, we spent 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="https://en.wikipedia.org/wiki/The_Pragmatic_Programmer"&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 an editor like VS Code to edit your source code, 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;h1&gt;
  
  
  Editing
&lt;/h1&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;h2&gt;
  
  
  Code Completion (⌃ + Space)
&lt;/h2&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CKMyZxIY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/97995f0aadfa4afee6a1860e5a1eae3f/code_completion_5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CKMyZxIY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/97995f0aadfa4afee6a1860e5a1eae3f/code_completion_5.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Lines (⌥ + ⌘ + [and ⌥ + ⌘ +])
&lt;/h2&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;h2&gt;
  
  
  Delete Entire Line (⌘ + D)
&lt;/h2&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.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/102ea0372e2157757d344e99bcf6a78e/af602/delete_line_keybindings.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hej-Y36Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://peterfriese.dev/static/102ea0372e2157757d344e99bcf6a78e/b9e4f/delete_line_keybindings.png" alt="delete line keybindings" title="delete line keybindings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment Current Line / block (⌘ + /)
&lt;/h2&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!&lt;/p&gt;

&lt;h2&gt;
  
  
  Balance Indentation (⌃ + I)
&lt;/h2&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;h1&gt;
  
  
  Navigation
&lt;/h1&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;h2&gt;
  
  
  Going Back and Forth (⌃ + ⌘ + ← and ⌃ + ⌘ + →)
&lt;/h2&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 ⌃ + ⌘ + ← and ⌃ + ⌘ + → to go back and forth between the source code locations you visited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump to Definition (⌃ + ⌘ + J or ⌃ + ⌘ + Click)
&lt;/h2&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 Quick help using ⌥ + click)&lt;/p&gt;

&lt;h2&gt;
  
  
  Find Selected Symbol in Workspace (⇧ + ⌃ + ⌘ + F)
&lt;/h2&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;h2&gt;
  
  
  Find Call Hierarchy (⇧ + ⌃ + ⌘ + H)
&lt;/h2&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zz2LAQsB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/b6a234e01424dad5f6beab1c1d266941/call_hierarchy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zz2LAQsB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/b6a234e01424dad5f6beab1c1d266941/call_hierarchy.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Quickly (⇧ + ⌘ + O)
&lt;/h2&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tpkS14O3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/3f85ac20aaee15f5b6d0982183135811/open_quickly.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tpkS14O3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/3f85ac20aaee15f5b6d0982183135811/open_quickly.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump to Line (⌘ + L)
&lt;/h2&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;h2&gt;
  
  
  Document outline (⌃ + 6 or ⌘ + hovering the minimap)
&lt;/h2&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 ⌃ + 6 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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hUEYW8Js--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/a4c25e90de2f76f4f6dd76bc9a95672f/content_outline.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hUEYW8Js--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/a4c25e90de2f76f4f6dd76bc9a95672f/content_outline.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  View Management
&lt;/h1&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;h2&gt;
  
  
  Toggle Canvas / SwiftUI Preview (⌥ + ⌘ + ↩)
&lt;/h2&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 ⌥ + ⌘ + ↩ to hide or show the Canvas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Toggle Views
&lt;/h2&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 (⌘ + 0)&lt;/li&gt;
&lt;li&gt;Right: Inspectors (⌘ + ⌥ + 0)&lt;/li&gt;
&lt;li&gt;Bottom: Debug (⇧ + ⌘ + Y)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&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;div class="table-wrapper-paragraph"&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;⌃ + Space&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;/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;/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;⌘ + D&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;/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;⌃ + I&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;/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;/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;⌃ + ⌘ + J&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;⇧ + ⌃ + ⌘ + F&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;⇧ + ⌃ + ⌘ + H&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;⇧ + ⌘ + O&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;⌘ + L&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;⌃ + 6&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;/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;⌘ + 0&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;⌘ + ⌥ + 0&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;⇧ + ⌘ + Y&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;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>xcode</category>
      <category>ide</category>
      <category>keybindings</category>
    </item>
    <item>
      <title>SwiftUI + Combine = ❤️</title>
      <dc:creator>Peter Friese</dc:creator>
      <pubDate>Fri, 13 Sep 2019 07:45:56 +0000</pubDate>
      <link>https://dev.to/peterfriese/swiftui-combine-3oa9</link>
      <guid>https://dev.to/peterfriese/swiftui-combine-3oa9</guid>
      <description>&lt;p&gt;&lt;em&gt;Why SwiftUI and Combine will help you to build better apps&lt;/em&gt;&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’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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;UserViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Input&lt;/span&gt;
  &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;passwordAgain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;

  &lt;span class="c1"&gt;// Output&lt;/span&gt;
  &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;@ObservedObject&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;userViewModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UserViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;Section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;userViewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autocapitalization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;none&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kt"&gt;Section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kt"&gt;SecureField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;userViewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="kt"&gt;SecureField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Password again"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;userViewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordAgain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="kt"&gt;Section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sign up"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;userViewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView_Previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PreviewProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;a href="///static/62350705f5718a62ec5ee4b30967d0f8/af534/screenshot-dark2.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LEYaUhXv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://peterfriese.dev/static/62350705f5718a62ec5ee4b30967d0f8/b9e4f/screenshot-dark2.png" alt="screenshot dark2" title="screenshot dark2"&gt;&lt;/a&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;blockquote&gt;
&lt;p&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="https://developer.apple.com/documentation/combine"&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;Publisher&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;associatedtype&lt;/span&gt; &lt;span class="kt"&gt;Output&lt;/span&gt;
  &lt;span class="kd"&gt;associatedtype&lt;/span&gt; &lt;span class="kt"&gt;Failure&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
  &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Subscriber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Failure&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Input&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;Subscriber&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CustomCombineIdentifierConvertible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;associatedtype&lt;/span&gt; &lt;span class="kt"&gt;Input&lt;/span&gt;
  &lt;span class="kd"&gt;associatedtype&lt;/span&gt; &lt;span class="kt"&gt;Failure&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;

  &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Subscribers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Demand&lt;/span&gt;
  &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Subscribers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Completion&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeDuplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cancellableSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isPasswordEmptyPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Never&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// (1)&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeDuplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;arePasswordsEqualPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Never&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Publishers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;CombineLatest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;passwordAgain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordAgain&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;passwordAgain&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To compute the password strength, we use &lt;a href="https://github.com/jasonnam/Navajo-Swift"&gt;Navajo Swift&lt;/a&gt;, a Swift port of &lt;a href="https://twitter.com/mattt?lang=en"&gt;https://twitter.com/mattt?lang=en&lt;/a&gt; excellent &lt;a href="https://github.com/mattt/Navajo"&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;passwordStrengthPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;PasswordStrength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Never&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeDuplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;Navajo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ofPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// (2)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isPasswordStrongEnoughPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Never&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;passwordStrengthPublisher&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;strength&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;strength&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reasonable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;veryStrong&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;a href="///static/509087384ca5b95b704aa26c9da81be0/34e8a/we-need-to-go-deeper.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NKp5mzYx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://peterfriese.netlify.com/static/509087384ca5b95b704aa26c9da81be0/b9e4f/we-need-to-go-deeper.png" alt="we need to go deeper" title="we need to go deeper"&gt;&lt;/a&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;PasswordCheck&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;noMatch&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;notStrongEnough&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isPasswordValidPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;PasswordCheck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Never&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Publishers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;CombineLatest3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isPasswordEmptyPublisher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arePasswordsEqualPublisher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isPasswordStrongEnoughPublisher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;passwordIsEmpty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordsAreEqual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordIsStrongEnough&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;passwordIsEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;passwordsAreEqual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;noMatch&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;passwordIsStrongEnough&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notStrongEnough&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isUsernameValidPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Never&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeDuplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this done, we can implement the final stage of the form validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isFormValidPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AnyPublisher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Never&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Publishers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;CombineLatest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isUsernameValidPublisher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isPasswordValidPublisher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;usernameIsValid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordIsValid&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;usernameIsValid&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;passwordIsValid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eraseToAnyPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;isFormValidPublisher&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cancellableSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;isUsernameValidPublisher&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"username must at leat have 3 characters"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usernameMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cancellableSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;isPasswordValidPublisher&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;RunLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;passwordCheck&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;passwordCheck&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Password must not be empty"&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;noMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Passwords don't match"&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;notStrongEnough&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Password not strong enough"&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cancellableSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;Section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usernameMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kt"&gt;Section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;passwordMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the result of our hard work in all its glory:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Aqi9ts8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/f036eaf9870f8b70910fca3d7b583e09/demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Aqi9ts8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://peterfriese.dev/f036eaf9870f8b70910fca3d7b583e09/demo.gif" alt=""&gt;&lt;/a&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="https://developer.apple.com/design/human-interface-guidelines/"&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="https://github.com/peterfriese/SwiftUI-Combine"&gt;Source code for this article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2019/722/"&gt;Introducing Combine - WWDC 2019 - Videos - Apple Developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2019/721/"&gt;Combine in Practice - WWDC 2019 - Videos - Apple Developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rotato.xyz/"&gt;Rotato&lt;/a&gt; - create amazing mock-ups&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>swiftui</category>
      <category>combine</category>
      <category>ios</category>
      <category>frp</category>
    </item>
  </channel>
</rss>
