<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://diamantidis.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://diamantidis.github.io/" rel="alternate" type="text/html" /><updated>2026-04-20T18:42:41+00:00</updated><id>https://diamantidis.github.io/feed.xml</id><title type="html">Ioannis Diamantidis</title><subtitle>A blog where I publish posts about my learnings on software development, my thoughts, my ideas and topics that I find interesting.</subtitle><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><entry><title type="html">Improve In App Video Guides with Picture-in-Picture in SwiftUI</title><link href="https://diamantidis.github.io/2026/04/18/improve-in-app-guides-with-picture-in-picture" rel="alternate" type="text/html" title="Improve In App Video Guides with Picture-in-Picture in SwiftUI" /><published>2026-04-18T09:00:00+00:00</published><updated>2026-04-18T09:00:00+00:00</updated><id>https://diamantidis.github.io/2026/04/18/improve-in-app-guides-with-picture-in-picture</id><content type="html" xml:base="https://diamantidis.github.io/2026/04/18/improve-in-app-guides-with-picture-in-picture"><![CDATA[<p>When we build features like widgets or Safari extensions in our app, short video guides can help our users follow the setup steps that must be performed outside of the app.</p>

<h3 id="the-swiftui-videoplayer">The SwiftUI VideoPlayer</h3>
<p>For this kind of content, SwiftUI provides a built-in <code class="language-plaintext highlighter-rouge">VideoPlayer</code> that makes it straightforward to play videos. With just a few lines of code, we can add a fully functional video player in a SwiftUI view.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">SwiftUI</span>
<span class="kd">import</span> <span class="kt">AVKit</span>

<span class="kd">struct</span> <span class="kt">TutorialVideoView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">player</span><span class="p">:</span> <span class="kt">AVPlayer</span> <span class="o">=</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">module</span><span class="o">.</span><span class="nf">url</span><span class="p">(</span><span class="nv">forResource</span><span class="p">:</span> <span class="s">"widget"</span><span class="p">,</span> <span class="nv">withExtension</span><span class="p">:</span> <span class="s">"mp4"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">fatalError</span><span class="p">(</span><span class="s">"widget.mp4 not found"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="kt">AVPlayer</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
    <span class="p">}()</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VideoPlayer</span><span class="p">(</span><span class="nv">player</span><span class="p">:</span> <span class="n">player</span><span class="p">)</span>
            <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">height</span><span class="p">:</span> <span class="mi">200</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This works well for basic playback, but it falls short for guided onboarding flows. As soon as users leave the app to follow the instructions, the video goes into the background as well. That forces them to return to the app, resume the video, watch the next step, and repeat the same cycle.</p>

<p>Thankfully, there is a solution to this problem: Picture-In-Picture.</p>

<h2 id="the-solution-avplayerviewcontroller-with-picture-in-picture">The Solution: AVPlayerViewController with Picture-in-Picture</h2>

<p>To enable Picture-in-Picture (PiP) support, we’ll use an <code class="language-plaintext highlighter-rouge">AVPlayerViewController</code>, and we’ll wrap it in a <code class="language-plaintext highlighter-rouge">UIViewControllerRepresentable</code> so that we can use it from SwiftUI.</p>

<p>Here is the implementation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">SwiftUI</span>
<span class="kd">import</span> <span class="kt">AVKit</span>

<span class="kd">struct</span> <span class="kt">PiPVideoPlayer</span><span class="p">:</span> <span class="kt">UIViewControllerRepresentable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">player</span><span class="p">:</span> <span class="kt">AVPlayer</span>

    <span class="kd">func</span> <span class="nf">makeUIViewController</span><span class="p">(</span><span class="nv">context</span><span class="p">:</span> <span class="kt">Context</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AVPlayerViewController</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">controller</span> <span class="o">=</span> <span class="kt">AVPlayerViewController</span><span class="p">()</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">player</span> <span class="o">=</span> <span class="n">player</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">allowsPictureInPicturePlayback</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">canStartPictureInPictureAutomaticallyFromInline</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="k">return</span> <span class="n">controller</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">updateUIViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">uiViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">,</span> <span class="nv">context</span><span class="p">:</span> <span class="kt">Context</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">uiViewController</span><span class="o">.</span><span class="n">player</span> <span class="o">=</span> <span class="n">player</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this snippet, we configure two key properties on the <code class="language-plaintext highlighter-rouge">AVPlayerViewController</code>:</p>

<ul>
  <li><strong>allowsPictureInPicturePlayback</strong>: Enables PiP support on the player. Without this, the PiP button won’t appear at all.</li>
  <li><strong>canStartPictureInPictureAutomaticallyFromInline</strong>: Allows PiP to activate automatically when the user navigates away from the app while the video is playing inline (not fullscreen). This is key for our use case — users don’t need to manually enable PiP.</li>
</ul>

<p>Then in our view, we create the player and pass it in:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">TutorialVideoView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">player</span><span class="p">:</span> <span class="kt">AVPlayer</span> <span class="o">=</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">module</span><span class="o">.</span><span class="nf">url</span><span class="p">(</span><span class="nv">forResource</span><span class="p">:</span> <span class="s">"widget"</span><span class="p">,</span> <span class="nv">withExtension</span><span class="p">:</span> <span class="s">"mp4"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">fatalError</span><span class="p">(</span><span class="s">"widget.mp4 not found"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="kt">AVPlayer</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
    <span class="p">}()</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">PiPVideoPlayer</span><span class="p">(</span><span class="nv">player</span><span class="p">:</span> <span class="n">player</span><span class="p">)</span>
            <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">height</span><span class="p">:</span> <span class="mi">200</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this implementation, the system provides a PiP button in the playback controls. Once activated, users can continue watching the video while navigating to system settings or other apps.</p>

<h3 id="prerequisites">Prerequisites</h3>

<p>Before using this implementation, ensure your project meets these requirements:</p>

<ol>
  <li><strong>Minimum iOS version</strong>: iOS 15 or later</li>
  <li><strong>Background Modes</strong>: Enable “Audio, AirPlay, and Picture in Picture” in your app’s capabilities.</li>
</ol>

<h2 id="tracking-picture-in-picture-events">Tracking Picture-in-Picture Events</h2>

<p>To take it a step further, you can implement the delegate methods provided by <code class="language-plaintext highlighter-rouge">AVPlayerViewController</code> to understand how users interact with your onboarding videos. Just create a coordinator that conforms to <code class="language-plaintext highlighter-rouge">AVPlayerViewControllerDelegate</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">AVKit</span>
<span class="kd">import</span> <span class="n">os</span><span class="o">.</span><span class="n">log</span>

<span class="kd">class</span> <span class="kt">VideoPlayerCoordinator</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">AVPlayerViewControllerDelegate</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">logger</span> <span class="o">=</span> <span class="kt">Logger</span><span class="p">(</span><span class="nv">subsystem</span><span class="p">:</span> <span class="s">"com.yourapp.video"</span><span class="p">,</span> <span class="nv">category</span><span class="p">:</span> <span class="s">"PiP"</span><span class="p">)</span>

    <span class="kd">func</span> <span class="nf">playerViewControllerWillStartPictureInPicture</span><span class="p">(</span><span class="n">_</span> <span class="nv">playerViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">logger</span><span class="o">.</span><span class="nf">info</span><span class="p">(</span><span class="s">"PiP will start"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">playerViewControllerDidStartPictureInPicture</span><span class="p">(</span><span class="n">_</span> <span class="nv">playerViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">logger</span><span class="o">.</span><span class="nf">info</span><span class="p">(</span><span class="s">"PiP started"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">playerViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">playerViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">,</span> <span class="n">failedToStartPictureInPictureWithError</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">Error</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">logger</span><span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="s">"PiP failed to start: </span><span class="se">\(</span><span class="n">error</span><span class="o">.</span><span class="n">localizedDescription</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">playerViewControllerWillStopPictureInPicture</span><span class="p">(</span><span class="n">_</span> <span class="nv">playerViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">logger</span><span class="o">.</span><span class="nf">info</span><span class="p">(</span><span class="s">"PiP will stop"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">playerViewControllerDidStopPictureInPicture</span><span class="p">(</span><span class="n">_</span> <span class="nv">playerViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">logger</span><span class="o">.</span><span class="nf">info</span><span class="p">(</span><span class="s">"PiP stopped"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">playerViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">playerViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">,</span> <span class="n">restoreUserInterfaceForPictureInPictureStopWithCompletionHandler</span> <span class="nv">completionHandler</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">logger</span><span class="o">.</span><span class="nf">info</span><span class="p">(</span><span class="s">"Restoring UI for PiP stop"</span><span class="p">)</span>
        <span class="nf">completionHandler</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To use this coordinator, update the <code class="language-plaintext highlighter-rouge">PiPVideoPlayer</code> to include it:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">PiPVideoPlayer</span><span class="p">:</span> <span class="kt">UIViewControllerRepresentable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">player</span><span class="p">:</span> <span class="kt">AVPlayer</span>

    <span class="kd">func</span> <span class="nf">makeCoordinator</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">VideoPlayerCoordinator</span> <span class="p">{</span>
        <span class="kt">VideoPlayerCoordinator</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">makeUIViewController</span><span class="p">(</span><span class="nv">context</span><span class="p">:</span> <span class="kt">Context</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AVPlayerViewController</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">controller</span> <span class="o">=</span> <span class="kt">AVPlayerViewController</span><span class="p">()</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">player</span> <span class="o">=</span> <span class="n">player</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">coordinator</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">allowsPictureInPicturePlayback</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">canStartPictureInPictureAutomaticallyFromInline</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="k">return</span> <span class="n">controller</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">updateUIViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">uiViewController</span><span class="p">:</span> <span class="kt">AVPlayerViewController</span><span class="p">,</span> <span class="nv">context</span><span class="p">:</span> <span class="kt">Context</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">uiViewController</span><span class="o">.</span><span class="n">player</span> <span class="o">=</span> <span class="n">player</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With these delegate methods in place, you can now track when users start or stop PiP and identify failures.</p>

<h2 id="conclusion">Conclusion</h2>

<p>To sum up, the built-in SwiftUI <code class="language-plaintext highlighter-rouge">VideoPlayer</code> is convenient for simple use cases, but it’s insufficient for onboarding flows where users must leave the app to complete setup. With Picture-in-Picture, they can keep the tutorial visible while performing the actual setup steps, resulting in a much smoother onboarding experience.</p>

<p>Thanks for reading! If you found this post useful, consider <a href="https://www.buymeacoffee.com/diamantidis">buying me a coffee</a> to support the blog. For questions or comments, feel free to reach out on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Swift" /><category term="SwiftUI" /><category term="iOS" /><category term="AVKit" /><category term="AVFoundation" /><summary type="html"><![CDATA[A post exploring how to add Picture-in-Picture support to video players in SwiftUI for better user experience in onboarding flows]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/pip-swiftui.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/pip-swiftui.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Flutter: Two-way communication between a WebView and a web page</title><link href="https://diamantidis.github.io/2021/10/17/two-way-communication-between-flutter-webview-and-a-web-page" rel="alternate" type="text/html" title="Flutter: Two-way communication between a WebView and a web page" /><published>2021-10-17T08:00:00+00:00</published><updated>2021-10-17T08:00:00+00:00</updated><id>https://diamantidis.github.io/2021/10/17/two-way-communication-between-flutter-webview-and-a-web-page</id><content type="html" xml:base="https://diamantidis.github.io/2021/10/17/two-way-communication-between-flutter-webview-and-a-web-page"><![CDATA[<p>Displaying web content in an app is quite easy and straightforward with WebViews but what happens when we want to open a communication channel between the app and the web page we load on a WebView?</p>

<p>Let’s say, for example, you want to “listen” to a button click event happening on the web page and take some action in the app. Or you want to update a label of a web page with some data from the app.</p>

<p>In this post, I will show you how you can communicate between the app and the web page of a WebView.</p>

<p>In order to do so, I am going to use an example to demonstrate how to achieve this communication in both directions; both from the web page to the app and from the app to the web page.</p>

<p>Let’s get started!</p>

<blockquote>
  <p>This post is based on <code class="language-plaintext highlighter-rouge">Flutter 2.5.2</code> and <code class="language-plaintext highlighter-rouge">Dart SDK 2.14.3</code></p>
</blockquote>

<h2 id="implementation">Implementation</h2>

<h3 id="the-package">The package</h3>

<p>Flutter doesn’t have built in support for WebViews.
As a result, we are going to use a package with name <a href="https://pub.dev/packages/webview_flutter">webview_flutter</a>.</p>

<p>So first thing first is to edit <code class="language-plaintext highlighter-rouge">pubspec.yaml</code> and add the dependency:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">dependencies</span><span class="pi">:</span>
  <span class="na">flutter</span><span class="pi">:</span>
    <span class="na">sdk</span><span class="pi">:</span> <span class="s">flutter</span>
  <span class="na">webview_flutter</span><span class="pi">:</span> <span class="s">^2.1.1</span>
</code></pre></div></div>

<h3 id="the-html-file">The HTML file</h3>

<p>Next, we are going to create the web page that we will later load in the WebView.</p>

<p>For the sake of this article and for ease, we are going to create an <code class="language-plaintext highlighter-rouge">index.html</code> file which we are going to load in the webview instead of loading a webpage from a URL.</p>

<p>Let’s create an HTML file named <code class="language-plaintext highlighter-rouge">index.html</code> inside the <code class="language-plaintext highlighter-rouge">assets</code> folder (if the folder doesn’t exist, feel free to create one).</p>

<p>Then, we will open this file and add the following content:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
    <span class="nt">&lt;head&gt;</span>
        <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1"</span><span class="nt">/&gt;</span>
        <span class="nt">&lt;style&gt;</span>
            <span class="nc">.switch</span> <span class="p">{</span> <span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span> <span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">60px</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">34px</span><span class="p">;</span> <span class="p">}</span>
            <span class="nc">.switch</span> <span class="nt">input</span> <span class="p">{</span> <span class="nl">opacity</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="p">}</span>
            <span class="nt">label</span><span class="nc">.switch</span> <span class="p">{</span> <span class="nl">outline</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span> <span class="p">}</span>
            <span class="nc">.slider</span> <span class="p">{</span> <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span> <span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span> <span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">right</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">bottom</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">background-color</span><span class="p">:</span> <span class="m">#ccc</span><span class="p">;</span> <span class="nl">-webkit-transition</span><span class="p">:</span> <span class="m">.4s</span><span class="p">;</span> <span class="nl">transition</span><span class="p">:</span> <span class="m">.4s</span><span class="p">;</span> <span class="nl">outline</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span> <span class="p">}</span>
            <span class="nc">.slider</span><span class="nd">:before</span> <span class="p">{</span> <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span> <span class="nl">content</span><span class="p">:</span> <span class="s1">""</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">26px</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">26px</span><span class="p">;</span> <span class="nl">left</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="nl">bottom</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="nl">background-color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span> <span class="nl">-webkit-transition</span><span class="p">:</span> <span class="m">.4s</span><span class="p">;</span> <span class="nl">transition</span><span class="p">:</span> <span class="m">.4s</span><span class="p">;</span> <span class="p">}</span>
            <span class="nt">input</span><span class="nd">:checked</span> <span class="o">+</span> <span class="nc">.slider</span> <span class="p">{</span> <span class="nl">background-color</span><span class="p">:</span> <span class="m">#2196F3</span><span class="p">;</span> <span class="p">}</span>
            <span class="nt">input</span><span class="nd">:focus</span> <span class="o">+</span> <span class="nc">.slider</span> <span class="p">{</span> <span class="nl">box-shadow</span><span class="p">:</span> <span class="m">0</span> <span class="m">0</span> <span class="m">1px</span> <span class="m">#2196F3</span><span class="p">;</span> <span class="p">}</span>
            <span class="nt">input</span><span class="nd">:checked</span> <span class="o">+</span> <span class="nc">.slider</span><span class="nd">:before</span> <span class="p">{</span> <span class="nl">-webkit-transform</span><span class="p">:</span> <span class="n">translateX</span><span class="p">(</span><span class="m">26px</span><span class="p">);</span> <span class="nl">-ms-transform</span><span class="p">:</span> <span class="n">translateX</span><span class="p">(</span><span class="m">26px</span><span class="p">);</span> <span class="nl">transform</span><span class="p">:</span> <span class="n">translateX</span><span class="p">(</span><span class="m">26px</span><span class="p">);</span> <span class="p">}</span>
            <span class="nc">.slider.round</span> <span class="p">{</span> <span class="nl">border-radius</span><span class="p">:</span> <span class="m">34px</span><span class="p">;</span> <span class="p">}</span>
            <span class="nc">.slider.round</span><span class="nd">:before</span> <span class="p">{</span> <span class="nl">border-radius</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span> <span class="p">}</span>
        <span class="nt">&lt;/style&gt;</span>
    <span class="nt">&lt;/head&gt;</span>
    <span class="nt">&lt;body&gt;</span>
        <span class="nt">&lt;h2</span> <span class="na">id=</span><span class="s">"value"</span><span class="nt">&gt;</span>Toggle Switch is off<span class="nt">&lt;/h2&gt;</span>
        <span class="nt">&lt;label</span> <span class="na">class=</span><span class="s">"switch"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"checkbox"</span> <span class="na">name=</span><span class="s">"myCheckbox"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"slider round"</span><span class="nt">&gt;&lt;/span&gt;</span>
        <span class="nt">&lt;/label&gt;</span>
    <span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<blockquote>
  <p>For brevity reasons, the CSS part is minimized.</p>
</blockquote>

<p>This HTML file produces a web page with a label and a toggle switch. Our goal for this article is to listen to the toggle events from the app and update the text on the label to represent the corresponding state of the toggle switch.</p>

<p>To be able to access this <code class="language-plaintext highlighter-rouge">html</code> file from our application, we have to edit <code class="language-plaintext highlighter-rouge">pubspec.yaml</code> and add the following under the <code class="language-plaintext highlighter-rouge">flutter</code> section:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">flutter</span><span class="pi">:</span>

  <span class="na">assets</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">assets/index.html</span>
</code></pre></div></div>

<h3 id="the-app">The app</h3>

<p>Now, with all the preparations done, we can focus on the app!</p>

<p>First of all, we will import the package:</p>
<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'package:webview_flutter/webview_flutter.dart'</span><span class="o">;</span>
</code></pre></div></div>

<p>Then, we can create our webview like this:</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WebViewController</span><span class="o">?</span> <span class="n">_webViewController</span><span class="p">;</span>

<span class="n">Widget</span> <span class="nf">buildWebView</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">WebView</span><span class="p">(</span>
        <span class="nl">initialUrl:</span> <span class="s">'about:blank'</span><span class="p">,</span>
        <span class="nl">javascriptMode:</span> <span class="n">JavascriptMode</span><span class="o">.</span><span class="na">unrestricted</span><span class="p">,</span>
        <span class="nl">onWebViewCreated:</span> <span class="p">(</span><span class="n">WebViewController</span> <span class="n">webViewController</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
            <span class="n">_webViewController</span> <span class="o">=</span> <span class="n">webViewController</span><span class="p">;</span>
            <span class="kt">String</span> <span class="n">fileContent</span> <span class="o">=</span> <span class="k">await</span> <span class="n">rootBundle</span><span class="o">.</span><span class="na">loadString</span><span class="p">(</span><span class="s">'assets/index.html'</span><span class="p">);</span>
            <span class="n">_webViewController</span><span class="o">?.</span><span class="na">loadUrl</span><span class="p">(</span><span class="kt">Uri</span><span class="o">.</span><span class="na">dataFromString</span><span class="p">(</span><span class="n">fileContent</span><span class="p">,</span> <span class="nl">mimeType:</span> <span class="s">'text/html'</span><span class="p">,</span> <span class="nl">encoding:</span> <span class="n">Encoding</span><span class="o">.</span><span class="na">getByName</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span><span class="o">.</span><span class="na">toString</span><span class="p">());</span>
        <span class="p">},</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In this snippet, we added a function that will return a new <code class="language-plaintext highlighter-rouge">WebView</code> and we pass some parameters.</p>

<p>Firstly, we will use <code class="language-plaintext highlighter-rouge">about:black</code> as the initial URL to load an empty page.
Then, we will set <code class="language-plaintext highlighter-rouge">javascriptMode</code> to <code class="language-plaintext highlighter-rouge">unrestricted</code> to allow the execution of Javascript code, and lastly, when the web view is created, we will load the content of the <code class="language-plaintext highlighter-rouge">index.html</code> and load it to the web view.</p>

<p>The only thing left is to call this function from the <code class="language-plaintext highlighter-rouge">Widget build(BuildContext context)</code>, add the required imports and run the app. If you do so, you will be able to see the page with the label and the toggle!</p>

<p><img src="https://diamantidis.github.io/assets/fl_webview/toggle_off.jpeg" alt="WKWebView with a toggle screenshot" /></p>

<h2 id="adding-a-message-handler">Adding a message handler</h2>

<p>The next step is to enable our <code class="language-plaintext highlighter-rouge">WebView</code> to receive messages from the web page. 
For this, we will update the <code class="language-plaintext highlighter-rouge">WebView</code> we created before and add the <code class="language-plaintext highlighter-rouge">javascriptChannels:</code> parameter, like in the following snippet:</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Widget</span> <span class="nf">buildWebView</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">WebView</span><span class="p">(</span>
        <span class="p">.</span>
        <span class="p">.</span>
        <span class="p">.</span>
        <span class="nl">javascriptChannels:</span> <span class="p">&lt;</span><span class="n">JavascriptChannel</span><span class="p">&gt;{</span>
            <span class="n">JavascriptChannel</span><span class="p">(</span>
                <span class="nl">name:</span> <span class="s">'messageHandler'</span><span class="p">,</span>
                <span class="nl">onMessageReceived:</span> <span class="p">(</span><span class="n">JavascriptMessage</span> <span class="n">message</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">print</span><span class="p">(</span><span class="s">"message from the web view=</span><span class="se">\"</span><span class="si">${message.message}</span><span class="se">\"</span><span class="s">"</span><span class="p">);</span>
                <span class="p">},</span>
            <span class="p">)</span>
        <span class="p">},</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here, we create a new <code class="language-plaintext highlighter-rouge">JavascriptChannel</code> with name <code class="language-plaintext highlighter-rouge">messageHandler</code> and provide a callback for when a message is received. For now, we just print the message.</p>

<p>We are now ready to send our messages from the web page.</p>

<p>Let’s see how!</p>

<h3 id="sending-message-from-the-webpage">Sending message from the webpage</h3>

<p>For this part, we are going to use some <code class="language-plaintext highlighter-rouge">JavaScript</code> to listen to the toggle events and then send a message to the app. This message will be different if the toggle is selected or not. Let’s edit our HTML file and before the closing <code class="language-plaintext highlighter-rouge">&lt;/body&gt;</code> tag, add the following content:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">&gt;</span>
    <span class="kd">var</span> <span class="nx">_selector</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">input[name=myCheckbox]</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">_selector</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">change</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">message</span> <span class="o">=</span> <span class="p">(</span><span class="nx">_selector</span><span class="p">.</span><span class="nx">checked</span><span class="p">)</span> <span class="p">?</span> <span class="dl">"</span><span class="s2">Toggle Switch is on</span><span class="dl">"</span> <span class="p">:</span> <span class="dl">"</span><span class="s2">Toggle Switch is off</span><span class="dl">"</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="nx">messageHandler</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">messageHandler</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="nx">message</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="p">});</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<p>In this script, we are adding an <code class="language-plaintext highlighter-rouge">eventListener</code> for the toggle change event, and depending on whether it is selected or not, we are using the <code class="language-plaintext highlighter-rouge">messageHandler</code> that we created previously, to call the function <code class="language-plaintext highlighter-rouge">postMessage</code>, which in turn will trigger the <code class="language-plaintext highlighter-rouge">onMessageReceived</code> of the <code class="language-plaintext highlighter-rouge">JavascriptChannel</code> with the name <code class="language-plaintext highlighter-rouge">messageHandler</code>.</p>

<p>If you run the app and turn the toggle on and off, you will be able to see some messages on the console like <code class="language-plaintext highlighter-rouge">message from the web view="Toggle Switch is on"</code>.</p>

<p>The last thing to achieve our goal is to update the label of the web page when we change the value of the toggle.</p>

<h3 id="evaluating-javascript-to-update-webpages-ui">Evaluating JavaScript to update webpage’s UI</h3>

<p><code class="language-plaintext highlighter-rouge">WebViewController</code> has a function with name <code class="language-plaintext highlighter-rouge">evaluateJavascript</code> and this is what we are going to use to update the UI.</p>

<p>Head over to the definition of the <code class="language-plaintext highlighter-rouge">JavascriptChannel</code> and instead of the <code class="language-plaintext highlighter-rouge">print</code> command on the <code class="language-plaintext highlighter-rouge">onMessageReceived</code> callback, add the following snippet:</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="n">script</span> <span class="o">=</span> <span class="s">"document.getElementById('value').innerText=</span><span class="se">\"</span><span class="si">${message.message}</span><span class="se">\"</span><span class="s">"</span><span class="p">;</span>
<span class="n">_webViewController</span><span class="o">?.</span><span class="na">evaluateJavascript</span><span class="p">(</span><span class="n">script</span><span class="p">);</span>
</code></pre></div></div>

<p>In this snippet, we are creating a variable with the JavaScript code to change the label on the web page, and then use the <code class="language-plaintext highlighter-rouge">evaluateJavaScript</code> to execute this code.</p>

<p>And that’s about it, if you now run the app, the text of the label will change when the toggle is switched on or off.</p>

<p><img src="https://diamantidis.github.io/assets/fl_webview/toggle_animation.gif" alt="WebView with toggle on and off screenshot" /></p>

<h2 id="conclusion">Conclusion</h2>

<p>In this post, we have seen how to achieve bidirectional communication between a WebView and a web page. Using this, we can use events from the website to trigger actions on the app and similarly adjust the content of the loaded website based on the information we have on the app.</p>

<p>I hope that you find this post useful and if you have any questions or comments about this post, feel free to reach out to me on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Flutter" /><summary type="html"><![CDATA[A post about how to communicate between a WebView in Flutter and the website]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/flutter_webview.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/flutter_webview.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Exploring Flutter’s Scrollable.ensureVisible</title><link href="https://diamantidis.github.io/2021/10/10/exploring-flutter-scrollable-ensurevisible" rel="alternate" type="text/html" title="Exploring Flutter’s Scrollable.ensureVisible" /><published>2021-10-10T04:00:00+00:00</published><updated>2021-10-10T04:00:00+00:00</updated><id>https://diamantidis.github.io/2021/10/10/exploring-flutter-scrollable-ensurevisible</id><content type="html" xml:base="https://diamantidis.github.io/2021/10/10/exploring-flutter-scrollable-ensurevisible"><![CDATA[<p>Have you ever had to build an app with a scroll view of multiple widgets with different heights, and you wanted to add a link so that the user can automatically scroll from one widget to another? One such example could be the table of contents for an article or a menu.</p>

<p>In this post, I will showcase how we can implement this and in order to do so, I am going to use a screen with a few sections and a table of contents at the top of the screen with links to the corresponding sections.</p>

<p>Let’s see how!</p>

<blockquote>
  <p>This post is based on <code class="language-plaintext highlighter-rouge">Flutter 2.5.2</code> and <code class="language-plaintext highlighter-rouge">Dart SDK 2.14.3</code></p>
</blockquote>

<h2 id="solution">Solution</h2>

<p>To do so, we are going to use <a href="https://api.flutter.dev/flutter/widgets/Scrollable/ensureVisible.html"><code class="language-plaintext highlighter-rouge">Scrollable.ensureVisible</code></a>  within a <code class="language-plaintext highlighter-rouge">SingleChildScrollView</code> widget with a <code class="language-plaintext highlighter-rouge">Column</code> child.</p>

<p>Briefly, we will create a <code class="language-plaintext highlighter-rouge">GlobalKey()</code> for each section. Then we will use this key as the key of the widget that the link will be targeting. Finally, we are going to use this key when we press the link from the table of contents like in the following snippet:</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="n">targetContext</span> <span class="o">=</span> <span class="n">targetKey</span><span class="o">.</span><span class="na">currentContext</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">targetContext</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Scrollable</span><span class="o">.</span><span class="na">ensureVisible</span><span class="p">(</span>
        <span class="n">targetContext</span><span class="p">,</span>
        <span class="nl">duration:</span> <span class="kd">const</span> <span class="n">Duration</span><span class="p">(</span><span class="nl">milliseconds:</span> <span class="mi">400</span><span class="p">),</span>
        <span class="nl">curve:</span> <span class="n">Curves</span><span class="o">.</span><span class="na">easeInOut</span><span class="p">,</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As a result, the target widget will get visible after 400 milliseconds.</p>

<p>In the next section, I will try to give a more detailed presentation of the implementation!</p>

<h2 id="implementation">Implementation</h2>

<h3 id="prep-work">Prep work</h3>

<p>First of all, let’s create a data structure that will represent the section. It will have a key, a title and a body.</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Section</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="n">GlobalKey</span> <span class="n">key</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">body</span><span class="p">;</span>

  <span class="kd">const</span> <span class="n">Section</span><span class="p">(</span><span class="k">this</span><span class="o">.</span><span class="na">key</span><span class="p">,</span> <span class="k">this</span><span class="o">.</span><span class="na">title</span><span class="p">,</span> <span class="k">this</span><span class="o">.</span><span class="na">body</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The title will be used in the table of contents and the key will be used to scroll to the target section.</p>

<p>Then, we will use this class to generate some dummy data for our example. We can use some lorem ipsum to represent some long text.</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="n">reallyLongBody</span> <span class="o">=</span>
    <span class="s">'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce venenatis pharetra dui, ac semper nulla dapibus ultrices.'</span>
    <span class="s">' Pellentesque sed erat accumsan lorem rhoncus mattis eu eget nulla. Phasellus sagittis vehicula dapibus. Nulla dolor nunc, '</span>
    <span class="s">'feugiat ac ullamcorper vel, commodo sed lacus. Nunc volutpat rutrum euismod. Nullam venenatis imperdiet odio, non porta leo '</span>
    <span class="s">'ullamcorper ac. Aliquam fringilla mauris ut ante faucibus, non tempus elit placerat. Donec sed porttitor tellus. Donec lobortis '</span>
    <span class="s">'arcu id lectus commodo varius. Fusce tincidunt ante in faucibus suscipit. Nulla facilisi. Nunc at nibh dictum sem aliquet '</span>
    <span class="s">'consectetur eu nec neque. Nullam ullamcorper vulputate nisl quis pharetra. Etiam dapibus ullamcorper magna, a iaculis libero '</span>
    <span class="s">'dignissim in. Vestibulum dictum, justo posuere consectetur eleifend, augue mi dictum dui, eu sollicitudin elit mauris vel lacus. '</span>
    <span class="s">'Donec dui felis, dapibus vel urna at, commodo facilisis felis.</span><span class="se">\n</span><span class="s">Curabitur faucibus leo ipsum, in vehicula risus rhoncus id. Donec '</span>
    <span class="s">'ac velit quis nulla suscipit efficitur. Nulla non euismod neque. Sed blandit urna sed ex tempor sagittis. Curabitur condimentum nec '</span>
    <span class="s">'dui quis sollicitudin. Proin consectetur, metus sed rutrum varius, mi augue placerat est, sed posuere risus nunc ac urna. Nam leo '</span>
    <span class="s">'erat, bibendum non nibh sed, sollicitudin aliquet metus. Aliquam finibus turpis vitae leo laoreet molestie.'</span><span class="p">;</span>

<span class="kd">final</span> <span class="n">sections</span> <span class="o">=</span> <span class="p">[</span>
  <span class="n">Section</span><span class="p">(</span><span class="n">GlobalKey</span><span class="p">(),</span> <span class="s">'1. Section'</span><span class="p">,</span> <span class="n">reallyLongBody</span><span class="p">),</span>
  <span class="n">Section</span><span class="p">(</span><span class="n">GlobalKey</span><span class="p">(),</span> <span class="s">'2. Section'</span><span class="p">,</span> <span class="n">reallyLongBody</span><span class="p">),</span>
  <span class="n">Section</span><span class="p">(</span><span class="n">GlobalKey</span><span class="p">(),</span> <span class="s">'3. Section'</span><span class="p">,</span> <span class="n">reallyLongBody</span><span class="p">),</span>
  <span class="n">Section</span><span class="p">(</span><span class="n">GlobalKey</span><span class="p">(),</span> <span class="s">'4. Section'</span><span class="p">,</span> <span class="n">reallyLongBody</span><span class="p">),</span>
  <span class="n">Section</span><span class="p">(</span><span class="n">GlobalKey</span><span class="p">(),</span> <span class="s">'5. Section'</span><span class="p">,</span> <span class="n">reallyLongBody</span><span class="p">),</span>
  <span class="n">Section</span><span class="p">(</span><span class="n">GlobalKey</span><span class="p">(),</span> <span class="s">'6. Section'</span><span class="p">,</span> <span class="n">reallyLongBody</span><span class="p">),</span>
<span class="p">];</span>
</code></pre></div></div>

<h3 id="widgets">Widgets</h3>

<p>Next, let’s create a widget for the <code class="language-plaintext highlighter-rouge">Section</code>, where we are going to show the title and the body of each section</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">SectionWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="n">Section</span> <span class="n">section</span><span class="p">;</span>

  <span class="kd">const</span> <span class="n">SectionWidget</span><span class="p">({</span><span class="n">Key</span><span class="o">?</span> <span class="n">key</span><span class="p">,</span> <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">section</span><span class="p">})</span> <span class="o">:</span> <span class="k">super</span><span class="p">(</span><span class="nl">key:</span> <span class="n">key</span><span class="p">);</span>

  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Container</span><span class="p">(</span>
        <span class="nl">decoration:</span> <span class="kd">const</span> <span class="n">BoxDecoration</span><span class="p">(</span>
          <span class="nl">color:</span> <span class="n">Color</span><span class="p">(</span><span class="mh">0xfffff8e2</span><span class="p">),</span>
          <span class="nl">borderRadius:</span> <span class="n">BorderRadius</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="n">Radius</span><span class="o">.</span><span class="na">circular</span><span class="p">(</span><span class="mf">8.0</span><span class="p">)),</span>
        <span class="p">),</span>
        <span class="nl">margin:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">symmetric</span><span class="p">(</span><span class="nl">horizontal:</span> <span class="mi">16</span><span class="p">,</span> <span class="nl">vertical:</span> <span class="mi">8</span><span class="p">),</span>
        <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">symmetric</span><span class="p">(</span><span class="nl">horizontal:</span> <span class="mi">16</span><span class="p">,</span> <span class="nl">vertical:</span> <span class="mi">8</span><span class="p">),</span>
        <span class="nl">child:</span> <span class="n">Column</span><span class="p">(</span>
          <span class="nl">crossAxisAlignment:</span> <span class="n">CrossAxisAlignment</span><span class="o">.</span><span class="na">stretch</span><span class="p">,</span>
          <span class="nl">children:</span> <span class="p">[</span>
            <span class="n">Text</span><span class="p">(</span>
              <span class="n">section</span><span class="o">.</span><span class="na">title</span><span class="p">,</span>
              <span class="nl">textAlign:</span> <span class="n">TextAlign</span><span class="o">.</span><span class="na">center</span><span class="p">,</span>
              <span class="nl">style:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
                  <span class="o">.</span><span class="na">textTheme</span>
                  <span class="o">.</span><span class="na">headline2</span>
                  <span class="o">?.</span><span class="na">copyWith</span><span class="p">(</span><span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">black</span><span class="p">),</span>
            <span class="p">),</span>
            <span class="kd">const</span> <span class="n">SizedBox</span><span class="p">(</span>
              <span class="nl">height:</span> <span class="mi">36</span><span class="p">,</span>
            <span class="p">),</span>
            <span class="n">Text</span><span class="p">(</span>
              <span class="n">section</span><span class="o">.</span><span class="na">body</span><span class="p">,</span>
              <span class="nl">style:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
                  <span class="o">.</span><span class="na">textTheme</span>
                  <span class="o">.</span><span class="na">bodyText1</span>
                  <span class="o">?.</span><span class="na">copyWith</span><span class="p">(</span><span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">black54</span><span class="p">,</span> <span class="nl">height:</span> <span class="mf">1.3</span><span class="p">),</span>
            <span class="p">)</span>
          <span class="p">],</span>
        <span class="p">));</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After that, let’s create a widget for the link to a section. We will name it <code class="language-plaintext highlighter-rouge">SectionLink</code> and we will pass a section and the callback for the <code class="language-plaintext highlighter-rouge">onTap</code> event of the <code class="language-plaintext highlighter-rouge">InkWell</code>.</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">SectionLink</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="n">Section</span> <span class="n">section</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">void</span> <span class="kt">Function</span><span class="p">(</span><span class="n">Section</span><span class="p">)</span> <span class="n">onTap</span><span class="p">;</span>

  <span class="kd">const</span> <span class="n">SectionLink</span><span class="p">({</span><span class="n">Key</span><span class="o">?</span> <span class="n">key</span><span class="p">,</span> <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">section</span><span class="p">,</span> <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">onTap</span><span class="p">})</span>
      <span class="o">:</span> <span class="k">super</span><span class="p">(</span><span class="nl">key:</span> <span class="n">key</span><span class="p">);</span>

  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">InkWell</span><span class="p">(</span>
      <span class="nl">onTap:</span> <span class="p">()</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">onTap</span><span class="p">(</span><span class="n">section</span><span class="p">),</span>
      <span class="nl">child:</span> <span class="n">Padding</span><span class="p">(</span>
        <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="mf">8.0</span><span class="p">),</span>
        <span class="nl">child:</span> <span class="n">Text</span><span class="p">(</span>
          <span class="n">section</span><span class="o">.</span><span class="na">title</span><span class="p">,</span>
          <span class="nl">style:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
              <span class="o">.</span><span class="na">textTheme</span>
              <span class="o">.</span><span class="na">headline3</span>
              <span class="o">?.</span><span class="na">copyWith</span><span class="p">(</span><span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">black87</span><span class="p">,</span> <span class="nl">fontWeight:</span> <span class="n">FontWeight</span><span class="o">.</span><span class="na">bold</span><span class="p">),</span>
        <span class="p">),</span>
      <span class="p">),</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Next, we are going to add a <code class="language-plaintext highlighter-rouge">TableOfContents</code> widget, where we basically iterate on the sections and for each section, we create a <code class="language-plaintext highlighter-rouge">SectionLink</code>.</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">TableOfContents</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">List</span><span class="p">&lt;</span><span class="n">Section</span><span class="p">&gt;</span> <span class="n">sections</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">void</span> <span class="kt">Function</span><span class="p">(</span><span class="n">Section</span><span class="p">)</span> <span class="n">onItemTap</span><span class="p">;</span>

  <span class="kd">const</span> <span class="n">TableOfContents</span><span class="p">({</span>
    <span class="n">Key</span><span class="o">?</span> <span class="n">key</span><span class="p">,</span>
    <span class="k">this</span><span class="o">.</span><span class="na">sections</span> <span class="o">=</span> <span class="kd">const</span> <span class="p">&lt;</span><span class="n">Section</span><span class="p">&gt;[],</span>
    <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">onItemTap</span><span class="p">,</span>
  <span class="p">})</span> <span class="o">:</span> <span class="k">super</span><span class="p">(</span><span class="nl">key:</span> <span class="n">key</span><span class="p">);</span>

  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Container</span><span class="p">(</span>
      <span class="nl">margin:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span>
      <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">only</span><span class="p">(</span><span class="nl">left:</span> <span class="mi">16</span><span class="p">,</span> <span class="nl">top:</span> <span class="mi">24</span><span class="p">,</span> <span class="nl">right:</span> <span class="mi">16</span><span class="p">,</span> <span class="nl">bottom:</span> <span class="mi">10</span><span class="p">),</span>
      <span class="nl">decoration:</span> <span class="n">BoxDecoration</span><span class="p">(</span>
        <span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">white12</span><span class="o">.</span><span class="na">withOpacity</span><span class="p">(</span><span class="mf">0.3</span><span class="p">),</span>
        <span class="nl">borderRadius:</span> <span class="kd">const</span> <span class="n">BorderRadius</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="n">Radius</span><span class="o">.</span><span class="na">circular</span><span class="p">(</span><span class="mf">8.0</span><span class="p">)),</span>
        <span class="nl">border:</span> <span class="n">Border</span><span class="o">.</span><span class="na">all</span><span class="p">(</span>
          <span class="nl">width:</span> <span class="mi">2</span><span class="p">,</span>
          <span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">grey</span><span class="p">,</span>
        <span class="p">),</span>
      <span class="p">),</span>
      <span class="nl">child:</span> <span class="n">Column</span><span class="p">(</span>
        <span class="nl">crossAxisAlignment:</span> <span class="n">CrossAxisAlignment</span><span class="o">.</span><span class="na">stretch</span><span class="p">,</span>
        <span class="nl">children:</span> <span class="n">sections</span>
            <span class="o">.</span><span class="na">map</span><span class="p">((</span><span class="n">e</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">SectionLink</span><span class="p">(</span><span class="nl">section:</span> <span class="n">e</span><span class="p">,</span> <span class="nl">onTap:</span> <span class="n">onItemTap</span><span class="p">))</span>
            <span class="o">.</span><span class="na">toList</span><span class="p">(),</span>
      <span class="p">),</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Finally, let’s create <code class="language-plaintext highlighter-rouge">ArticlePage</code> where we will tie everything together. In this widget, we will create a <code class="language-plaintext highlighter-rouge">SingleChildScrollView</code> with a <code class="language-plaintext highlighter-rouge">Column</code> widget containing the <code class="language-plaintext highlighter-rouge">TableOfContents</code> and a <code class="language-plaintext highlighter-rouge">ListView</code> with the sections.</p>

<p>For the <code class="language-plaintext highlighter-rouge">TableOfContents</code>, we are going to pass as parameters the section and the callback for when a section is tapped. This callback contains the logic to scroll to the target widget. First, we verify that there is a widget with this key in the tree, by ensuring that the value of the <code class="language-plaintext highlighter-rouge">currentContext</code> is not null. And then, we will pass this context to <code class="language-plaintext highlighter-rouge">Scrollable.ensureVisible</code> to scroll to the target widget.</p>

<p>For the <code class="language-plaintext highlighter-rouge">ListView</code>, we iterate over the sections and for each one, we create a new <code class="language-plaintext highlighter-rouge">SectionWidget</code> using the key from the section as the key for the widget.</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ArticlePage</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">List</span><span class="p">&lt;</span><span class="n">Section</span><span class="p">&gt;</span> <span class="n">sections</span><span class="p">;</span>
  <span class="kd">const</span> <span class="n">ArticlePage</span><span class="p">({</span><span class="n">Key</span><span class="o">?</span> <span class="n">key</span><span class="p">,</span> <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">sections</span><span class="p">})</span> <span class="o">:</span> <span class="k">super</span><span class="p">(</span><span class="nl">key:</span> <span class="n">key</span><span class="p">);</span>

  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>

    <span class="kd">final</span> <span class="n">tableOfContents</span> <span class="o">=</span> <span class="n">TableOfContents</span><span class="p">(</span>
      <span class="nl">sections:</span> <span class="n">sections</span><span class="p">,</span>
      <span class="nl">onItemTap:</span> <span class="p">(</span><span class="n">section</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">final</span> <span class="n">targetContext</span> <span class="o">=</span> <span class="n">section</span><span class="o">.</span><span class="na">key</span><span class="o">.</span><span class="na">currentContext</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">targetContext</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">Scrollable</span><span class="o">.</span><span class="na">ensureVisible</span><span class="p">(</span>
            <span class="n">targetContext</span><span class="p">,</span>
            <span class="nl">duration:</span> <span class="kd">const</span> <span class="n">Duration</span><span class="p">(</span><span class="nl">milliseconds:</span> <span class="mi">400</span><span class="p">),</span>
            <span class="nl">curve:</span> <span class="n">Curves</span><span class="o">.</span><span class="na">easeInOut</span><span class="p">,</span>
          <span class="p">);</span>
        <span class="p">}</span>
      <span class="p">},</span>
    <span class="p">);</span>

    <span class="kd">final</span> <span class="n">listView</span> <span class="o">=</span> <span class="n">ListView</span><span class="o">.</span><span class="na">builder</span><span class="p">(</span>
      <span class="nl">shrinkWrap:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="nl">physics:</span> <span class="kd">const</span> <span class="n">NeverScrollableScrollPhysics</span><span class="p">(),</span>
      <span class="nl">itemCount:</span> <span class="n">sections</span><span class="o">.</span><span class="na">length</span><span class="p">,</span>
      <span class="nl">itemBuilder:</span> <span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">,</span> <span class="kt">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">final</span> <span class="n">section</span> <span class="o">=</span> <span class="n">sections</span><span class="p">[</span><span class="n">index</span><span class="p">];</span>

        <span class="k">return</span> <span class="n">SectionWidget</span><span class="p">(</span>
          <span class="nl">key:</span> <span class="n">section</span><span class="o">.</span><span class="na">key</span><span class="p">,</span>
          <span class="nl">section:</span> <span class="n">section</span><span class="p">,</span>
        <span class="p">);</span>
      <span class="p">},</span>
    <span class="p">);</span>

    <span class="k">return</span> <span class="n">Scaffold</span><span class="p">(</span>
      <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="p">(</span>
        <span class="nl">title:</span> <span class="kd">const</span> <span class="n">Text</span><span class="p">(</span><span class="s">'Home Screen'</span><span class="p">),</span>
      <span class="p">),</span>
      <span class="nl">body:</span> <span class="n">SafeArea</span><span class="p">(</span>
        <span class="nl">child:</span> <span class="n">SingleChildScrollView</span><span class="p">(</span>
          <span class="nl">child:</span> <span class="n">Padding</span><span class="p">(</span>
            <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="mf">8.0</span><span class="p">),</span>
            <span class="nl">child:</span> <span class="n">Column</span><span class="p">(</span>
              <span class="nl">crossAxisAlignment:</span> <span class="n">CrossAxisAlignment</span><span class="o">.</span><span class="na">stretch</span><span class="p">,</span>
              <span class="nl">children:</span> <span class="p">[</span>
                <span class="n">tableOfContents</span><span class="p">,</span>
                <span class="n">listView</span><span class="p">,</span>
              <span class="p">],</span>
            <span class="p">),</span>
          <span class="p">),</span>
        <span class="p">),</span>
      <span class="p">),</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you now build and run the app, when you press on a link from the table of contents, you will have the same with the following video behavior.</p>

<p><img src="https://diamantidis.github.io/assets/scrollable/scrollable_ensureVisible.gif" alt="Demo app gif" /></p>

<blockquote>
  <p>You can also use <a href="https://dartpad.dev/?id=33c44d2f905cf23d4e0f825b45d79d91&amp;null_safety=true">dartpad.dev</a> to find and run the code of this post.</p>
</blockquote>

<h2 id="more-options">More options</h2>

<p>Now that we have seen how we can use <code class="language-plaintext highlighter-rouge">Scrollable.ensureVisible</code>, let’s explore some more options that we can use to customize the transition to the target widget.</p>

<p>Two of those, which we have seen before in the previous example, are <code class="language-plaintext highlighter-rouge">duration</code> and <code class="language-plaintext highlighter-rouge">curve</code>. <code class="language-plaintext highlighter-rouge">duration</code> can be used to set the desired duration that we want the animation from the link to the target widget to take.</p>

<p>With the <code class="language-plaintext highlighter-rouge">curve</code> parameter, we can define the animation curve that the transition will follow. Basically, instead of making the transition at a constant rate, we can set a value to the <code class="language-plaintext highlighter-rouge">curve</code> parameter to change the animation over time, either by speeding it up or slowing it down at specific time frames. It can take values like <code class="language-plaintext highlighter-rouge">Curves.bounceInOut</code>, <code class="language-plaintext highlighter-rouge">Curves.easeInOut</code>, etc. For example, with <code class="language-plaintext highlighter-rouge">Curves.easeInOut</code> the animation will start slowly, then speed up and will then end slowly.</p>

<blockquote>
  <p>Note: A visualization of the different options can be found on <a href="https://api.flutter.dev/flutter/animation/Curves-class.html">api.flutter.dev</a>.</p>
</blockquote>

<p>Another parameter of <code class="language-plaintext highlighter-rouge">Scrollable.ensureVisible</code> is <code class="language-plaintext highlighter-rouge">alignment</code>, and it can be used to set the position of the target widget. If the value is 0.0, the child will be positioned as close to the leading edge of the viewport, 0.5 as close to the center, and 1.0 as close to the trailing edge.</p>

<p>Finally, the last parameter is <code class="language-plaintext highlighter-rouge">alignmentPolicy</code> and can be used to decide the policy when applying the <code class="language-plaintext highlighter-rouge">alignment</code> parameter. This parameter is of type <code class="language-plaintext highlighter-rouge">ScrollPositionAlignmentPolicy</code>, which is an enum with the following options: <code class="language-plaintext highlighter-rouge">explicit</code>, <code class="language-plaintext highlighter-rouge">keepVisibleAtEnd</code> or <code class="language-plaintext highlighter-rouge">keepVisibleAtStart</code>.</p>

<p>When it is set to <code class="language-plaintext highlighter-rouge">explicit</code>, it will use the <code class="language-plaintext highlighter-rouge">alignment</code> property to decide where to align the target object. If it is set to <code class="language-plaintext highlighter-rouge">keepVisibleAtEnd</code>, it will make sure that the bottom of the target item is just visible if the bottom edge of the target item is below the bottom edge of the scroll container. Contrary, <code class="language-plaintext highlighter-rouge">keepVisibleAtStart</code>, will make sure that the top of the target object is just visible if the top edge of the target object is above the top edge of the scroll container</p>

<h2 id="conclusion">Conclusion</h2>

<p>And this is it! I hope that you find this post useful and it has given you some insights into how to use <code class="language-plaintext highlighter-rouge">Scrollable.ensureVisible</code> and all its options to scroll to a specific widget on a scroll view.</p>

<p>If you have any questions or comments about this post, feel free to reach out to me on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Flutter" /><summary type="html"><![CDATA[A post to showcase how to use `Scrollable.ensureVisible` to scroll from one widget to another in a `SingleChildScrollView`]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/scrollable_ensure_visible.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/scrollable_ensure_visible.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Quick and simple load testing with Apache Bench</title><link href="https://diamantidis.github.io/2020/07/15/load-testing-with-apache-bench" rel="alternate" type="text/html" title="Quick and simple load testing with Apache Bench" /><published>2020-07-15T04:00:00+00:00</published><updated>2020-07-15T04:00:00+00:00</updated><id>https://diamantidis.github.io/2020/07/15/load-testing-with-apache-bench</id><content type="html" xml:base="https://diamantidis.github.io/2020/07/15/load-testing-with-apache-bench"><![CDATA[<p><a href="https://httpd.apache.org/docs/2.4/programs/ab.html">Apache Bench</a> or ab for short, is a command-line tool to perform simple load tests on an HTTP server, be it a website or an API.</p>

<p>By running the following command, you will get an overview of how the server is performing under load:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ab <span class="nt">-n</span> 100 <span class="nt">-c</span> 10 &lt;url&gt;
</code></pre></div></div>

<p>So, in this post, I will try to explain how we can use Apache Bench. I will start with how to install it, then proceed on how to use it and the available options, and finally, I explain how to interpret the results.</p>

<p>Let’s get started!</p>

<h2 id="installation">Installation</h2>
<p>One of the advantages of Apache Bench is that you may already have <code class="language-plaintext highlighter-rouge">ab</code> installed, depending on the OS you are using.
For macOS users, it comes pre-installed by default, and if you are using a Linux Distribution, there are a lot of chances that it is also installed as it comes with the <code class="language-plaintext highlighter-rouge">httpd</code> package. Try to run <code class="language-plaintext highlighter-rouge">ab -help</code> to verify if that’s the case.</p>

<p>In case it is not installed, you will need to install the <code class="language-plaintext highlighter-rouge">apache2-utils</code> package.
For example, if you are using <code class="language-plaintext highlighter-rouge">Ubuntu</code> you will have to run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-get update
apt-get <span class="nb">install </span>apache2-utils
</code></pre></div></div>

<p>So, now, that we have Apache Bench installed, let’s see how we can use it and some of the available options.</p>

<h2 id="usage">Usage</h2>

<p>The simplest possible way to use Apache Bench is by running <code class="language-plaintext highlighter-rouge">ab &lt;url&gt;</code>. This command will perform a single network request, but this is not exactly what we would call “load”. For this reason, Apache Bench comes with a plethora of options you can use to define more complex use cases.</p>

<p>Some of the most useful ones are the following:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-n</code>: Number of requests</li>
  <li><code class="language-plaintext highlighter-rouge">-c</code>: Number of concurrent requests</li>
  <li><code class="language-plaintext highlighter-rouge">-H</code>: Add header</li>
  <li><code class="language-plaintext highlighter-rouge">—r</code>: flag to not exit on socket receive errors</li>
  <li><code class="language-plaintext highlighter-rouge">-k</code>: Use HTTP KeepAlive feature</li>
  <li><code class="language-plaintext highlighter-rouge">-p</code>: File containing data to POST</li>
  <li><code class="language-plaintext highlighter-rouge">-T</code>: Content-type header to use for POST/PUT data,</li>
</ul>

<p>Now you can use those options like so:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ab <span class="nt">-n</span> 100 <span class="nt">-c</span> 10 <span class="nt">-H</span> <span class="s2">"Accept-Encoding: gzip, deflate"</span> <span class="nt">-rk</span> https://0.0.0.0:4000/
</code></pre></div></div>

<blockquote>
  <p>:warning: Please note that you need the trailing <code class="language-plaintext highlighter-rouge">/</code> on the URL, or else you will get the error message <code class="language-plaintext highlighter-rouge">ab: invalid URL</code>.</p>
</blockquote>

<p>If you want to perform a benchmark test for a POST request, you can run the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ab <span class="nt">-n</span> 100 <span class="nt">-c</span> 10 <span class="nt">-p</span> data.json <span class="nt">-T</span> application/json <span class="nt">-rk</span> https://0.0.0.0:4000/
</code></pre></div></div>

<blockquote>
  <p>:bulb: TIP: For a full list of options, you can run <code class="language-plaintext highlighter-rouge">ab -help</code>, refer the man page by running <code class="language-plaintext highlighter-rouge">man ab</code>, or visit the <a href="https://httpd.apache.org/docs/2.4/programs/ab.html">documentation</a> online.</p>
</blockquote>

<p>Let’s now see how the output would look like and how to interpret it!</p>

<h2 id="response-interpretation">Response interpretation</h2>

<p>Once <code class="language-plaintext highlighter-rouge">ab</code> completes the HTTP requests, it will generate an output that will look like the following snippet:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Concurrency Level:      10
Time taken for tests:   0.791 seconds
Complete requests:      1000
Failed requests:        0
Keep-Alive requests:    1000
Total transferred:      4649081 bytes
HTML transferred:       3934000 bytes
Requests per second:    1264.17 [#/sec] (mean)
Time per request:       7.910 [ms] (mean)
Time per request:       0.791 [ms] (mean, across all concurrent requests)
Transfer rate:          5739.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   9.4      0     105
Processing:     3    6   7.1      5      77
Waiting:        3    6   6.8      5      65
Total:          3    7  11.9      5     111

Percentage of the requests served within a certain time (ms)
  50%      5
  66%      6
  75%      6
  80%      6
  90%      7
  95%      8
  98%     62
  99%     77
 100%    111 (longest request)
</code></pre></div></div>

<p>In this first section, you can find some useful information like, for example, that the number of <code class="language-plaintext highlighter-rouge">Complete requests</code> was 1000 and the <code class="language-plaintext highlighter-rouge">Concurrency Level</code> was 10. Furthermore, you can see that it performed 1264.17 <code class="language-plaintext highlighter-rouge">Requests per second</code>.</p>

<p>In the <code class="language-plaintext highlighter-rouge">Connection Times</code> section, you can see that the fastest request took 3 ms (<code class="language-plaintext highlighter-rouge">Total</code> row and <code class="language-plaintext highlighter-rouge">min</code> column), the slowest took 111 ms (<code class="language-plaintext highlighter-rouge">Total</code> row and <code class="language-plaintext highlighter-rouge">max</code> column), while the mean was 7 ms (<code class="language-plaintext highlighter-rouge">Total</code> row &amp; <code class="language-plaintext highlighter-rouge">mean</code> column).</p>

<p>In the last section, you get an overview of the response times in a cumulative distribution. In this example, we can see that 95% of requests took 8 ms or less to complete and that total response times of more than 100 ms is an outlier as it covers less than 1% of the sample.</p>

<h2 id="conclusion">Conclusion</h2>

<p>And that’s about for this intro on Apache Bench! By now, you should be able to use the <code class="language-plaintext highlighter-rouge">ab</code> command to perform load tests on an HTTP server and get some insights from the results.</p>

<p>In the end, I would say that Apache Bench is an ideal solution if you want to perform a quick load test since it is probably already installed on your machine and it is really simple to use. In case you want to cover more advanced use cases like flows and random URL entries, then I think that there are other more modern and feature-complete tools. A few examples are <a href="https://jmeter.apache.org/">JMeter</a>, <a href="https://k6.io/">K6</a> and <a href="https://gatling.io/">Gatling</a>.</p>

<p>Thanks for reading, I hope that you find this post useful, and if you have any questions or comments about this post, feel free to reach out to me on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Performance" /><summary type="html"><![CDATA[A brief into to Apache Bench, how to use, available option and result interpretation]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/load-testing-with-apache-bench.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/load-testing-with-apache-bench.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Dart extensions to flatten Flutter’s deep nested widget trees</title><link href="https://diamantidis.github.io/2020/07/07/dart-extensions-flatten-flutter-nested-widget-trees" rel="alternate" type="text/html" title="Dart extensions to flatten Flutter’s deep nested widget trees" /><published>2020-07-07T04:00:00+00:00</published><updated>2020-07-07T04:00:00+00:00</updated><id>https://diamantidis.github.io/2020/07/07/dart-extensions-flatten-flutter-nested-widget-trees</id><content type="html" xml:base="https://diamantidis.github.io/2020/07/07/dart-extensions-flatten-flutter-nested-widget-trees"><![CDATA[<p>In version 2.7, <code class="language-plaintext highlighter-rouge">Dart</code> introduced extensions to allow developers to add further functionality to an existing component. Extensions can be a great tool in our toolkit when we write business logic, but they can also be as useful in other areas! One such example can be the way we construct the widgets.</p>

<p>Coming from an iOS background and inspired by SwiftUI’s ViewModifiers, I was intrigued to use the Dart extensions in a similar fashion to reduce the visual clutter that comes with the deep tree of many nested widgets.</p>

<p>Let’s see an example!</p>

<h2 id="with-nested-widgets">With nested widgets</h2>

<p>Here we have the default widget created on <a href="https://dartpad.dev/flutter">dartpad.dev</a>, where we also wrapped the text inside a <code class="language-plaintext highlighter-rouge">Padding</code> widget.</p>
<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span>
      <span class="n">Padding</span><span class="p">(</span>
        <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="mi">16</span><span class="p">),</span>
        <span class="nl">child:</span> <span class="n">Text</span><span class="p">(</span><span class="s">'Hello, World!'</span><span class="p">,</span> <span class="nl">style:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="p">(</span><span class="n">context</span><span class="p">)</span><span class="o">.</span><span class="na">textTheme</span><span class="o">.</span><span class="na">headline4</span><span class="p">)</span>
      <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, let’s see how we can accomplish the same with the help of Dart extensions.</p>

<h2 id="with-extensions-methods">With extensions methods</h2>

<p>First, we will introduce an extension on <code class="language-plaintext highlighter-rouge">Widget</code> that will wrap the caller with a padding <code class="language-plaintext highlighter-rouge">Widget</code>:</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">extension</span> <span class="n">WidgetModifier</span> <span class="kd">on</span> <span class="n">Widget</span> <span class="p">{</span>
  <span class="n">Widget</span> <span class="n">padding</span><span class="p">([</span><span class="n">EdgeInsetsGeometry</span> <span class="n">value</span> <span class="o">=</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="mi">16</span><span class="p">)])</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Padding</span><span class="p">(</span>
      <span class="nl">padding:</span> <span class="n">value</span><span class="p">,</span>
      <span class="nl">child:</span> <span class="k">this</span><span class="p">,</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this extension in place, we could turn our <code class="language-plaintext highlighter-rouge">Widget</code> to the following:</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Text</span><span class="p">(</span><span class="s">'Hello, World!'</span><span class="p">,</span> <span class="nl">style:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="p">(</span><span class="n">context</span><span class="p">)</span><span class="o">.</span><span class="na">textTheme</span><span class="o">.</span><span class="na">headline4</span><span class="p">)</span>
            <span class="o">.</span><span class="na">padding</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This example is just a small one, but I hope you get the picture!!</p>

<p>We are using the extension method as a syntactic sugar to structure our layout, instead of wrapping a widget inside another one.</p>

<p>Similarly, we can add more functions to our extension and create more complex user interfaces:</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">extension</span> <span class="n">WidgetModifier</span> <span class="kd">on</span> <span class="n">Widget</span> <span class="p">{</span>

  <span class="c1">// ...</span>

  <span class="n">Widget</span> <span class="n">background</span><span class="p">(</span><span class="n">Color</span> <span class="n">color</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">DecoratedBox</span><span class="p">(</span>
      <span class="nl">decoration:</span> <span class="n">BoxDecoration</span><span class="p">(</span>
        <span class="nl">color:</span> <span class="n">color</span><span class="p">,</span>
      <span class="p">),</span>
      <span class="nl">child:</span> <span class="k">this</span><span class="p">,</span>
    <span class="p">);</span>
  <span class="p">}</span>

  <span class="n">Widget</span> <span class="n">cornerRadius</span><span class="p">(</span><span class="n">BorderRadiusGeometry</span> <span class="n">radius</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">ClipRRect</span><span class="p">(</span>
      <span class="nl">borderRadius:</span> <span class="n">radius</span><span class="p">,</span>
      <span class="nl">child:</span> <span class="k">this</span><span class="p">,</span>
    <span class="p">);</span>
  <span class="p">}</span>

  <span class="n">Widget</span> <span class="n">align</span><span class="p">([</span><span class="n">AlignmentGeometry</span> <span class="n">alignment</span> <span class="o">=</span> <span class="n">Alignment</span><span class="o">.</span><span class="na">center</span><span class="p">])</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Align</span><span class="p">(</span>
      <span class="nl">alignment:</span> <span class="n">alignment</span><span class="p">,</span>
      <span class="nl">child:</span> <span class="k">this</span><span class="p">,</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">MyWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Text</span><span class="p">(</span><span class="s">'Hello, World!'</span><span class="p">,</span> <span class="nl">style:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="p">(</span><span class="n">context</span><span class="p">)</span><span class="o">.</span><span class="na">textTheme</span><span class="o">.</span><span class="na">headline4</span><span class="p">)</span>
            <span class="o">.</span><span class="na">padding</span><span class="p">()</span>
            <span class="o">.</span><span class="na">background</span><span class="p">(</span><span class="n">Colors</span><span class="o">.</span><span class="na">lightBlue</span><span class="p">)</span>
            <span class="o">.</span><span class="na">cornerRadius</span><span class="p">(</span><span class="n">BorderRadius</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="n">Radius</span><span class="o">.</span><span class="na">circular</span><span class="p">(</span><span class="mf">8.0</span><span class="p">)))</span>
            <span class="o">.</span><span class="na">padding</span><span class="p">(</span><span class="n">EdgeInsets</span><span class="o">.</span><span class="na">symmetric</span><span class="p">(</span><span class="nl">horizontal:</span> <span class="mi">8</span><span class="p">,</span> <span class="nl">vertical:</span> <span class="mi">16</span><span class="p">))</span>
            <span class="o">.</span><span class="na">background</span><span class="p">(</span><span class="n">Colors</span><span class="o">.</span><span class="na">purple</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="https://diamantidis.github.io/assets/dart_extensions/dart_widget_extensions_example.png" alt="Dart widget extensions example screenshot" /></p>

<p>Isn’t that great? We now have a clean and elegant widget that we can easily understand! Imagine how many levels of nested widgets you would have to use to recreate this layout without the help of extensions.</p>

<h2 id="wrap-up">Wrap-up</h2>

<p>To wrap up, in this post, we have followed an alternative approach on how to structure a widget tree in a Flutter project, by applying a concept similar to SwiftUI’s ViewModifiers.</p>

<p>As a result, this could help us flatten the widget hierarchy and reduce nesting. I have showcased a few examples of such extensions, but you can use the same concept in many other cases as you see fit.</p>

<p>Thanks for reading, I hope you find this post useful!</p>

<p>If you have any questions or comments about this post, feel free to reach out to me on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Flutter" /><category term="Dart" /><summary type="html"><![CDATA[How to use Dart extensions instead of nested widgets to flatten the widget tree in a Flutter project]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/dart-extension-flutter-widget.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/dart-extension-flutter-widget.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Use AppleScript to generate an enum for SF Symbols</title><link href="https://diamantidis.github.io/2020/06/28/applescript-to-generate-enum-for-sf-symbols" rel="alternate" type="text/html" title="Use AppleScript to generate an enum for SF Symbols" /><published>2020-06-28T04:00:00+00:00</published><updated>2020-06-28T04:00:00+00:00</updated><id>https://diamantidis.github.io/2020/06/28/applescript-to-generate-enum-for-sf-symbols</id><content type="html" xml:base="https://diamantidis.github.io/2020/06/28/applescript-to-generate-enum-for-sf-symbols"><![CDATA[<p><a href="https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/">SF Symbols</a> is a great way to add symbols in apps. Introduced during WWDC 2019, and with a new updated version this year, the <a href="https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/">SF Symbols</a> app now provides more than 2300 symbols, supports iOS, Mac Catalyst, tvOS, and watchOS and now macOS, and offers many more features, like multicolor support, RTL, etc.</p>

<p>But despite its greatness, it comes with some shortcomings when we have to use those symbols from an app.
The provided APIs expect a hard-coded string literal for the name of the <code class="language-plaintext highlighter-rouge">SF Symbol</code>. This makes their usage susceptible to errors since we will not get notified by the compiler if we accidentally mistype the name.</p>

<p>Wouldn’t it be much better if we were to have a type-safe solution? Maybe an enum?</p>

<p>That’s the problem we will try to solve in this post. We will create a script using <code class="language-plaintext highlighter-rouge">AppleScript</code> which will open the <code class="language-plaintext highlighter-rouge">SF Symbols</code> app, traverse the list of symbols and generate an enum with a case for each symbol.</p>

<h2 id="script-overview">Script Overview</h2>

<p>To start with, we will use the <code class="language-plaintext highlighter-rouge">Script Editor</code> app to write the <code class="language-plaintext highlighter-rouge">AppleScript</code>. Before we start writing the script, let’s try to figure out the steps we will take on the <code class="language-plaintext highlighter-rouge">SF Symbols</code> app. First, we will open the app. Then, we will select the list layout. Choosing the list layout instead of the grid will make it much easier to traverse the list of symbols.</p>

<p>After that, we will select the option <code class="language-plaintext highlighter-rouge">All</code> from the <code class="language-plaintext highlighter-rouge">Categories</code> menu to make sure that the output will contain the full list of options. Then, we will loop through the symbols, and for each one, we will create an enum case of the format <code class="language-plaintext highlighter-rouge">case &lt;name&gt; = &lt;sf symbol name&gt;</code>.</p>

<p>Let’s see the script!</p>

<h2 id="preparation">Preparation</h2>

<p>We will first define some helper functions to compute the identifier that we will use for the case.</p>

<p>This identifier should be a valid one, so we have to cater for some edge cases. In some cases, the names of some SF Symbols are reserved keywords in Swift, like <code class="language-plaintext highlighter-rouge">return</code> and <code class="language-plaintext highlighter-rouge">repeat</code>. Since we cannot use those words for the case identifier, we will escape them with the <code class="language-plaintext highlighter-rouge">backtick</code> symbol (<code class="language-plaintext highlighter-rouge">`</code>).</p>

<div class="language-applescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">on</span> <span class="nv">convertReservedKeywords</span><span class="p">(</span><span class="nv">theText</span><span class="p">)</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">keywords</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="p">{</span><span class="s2">"repeat"</span><span class="p">,</span><span class="w"> </span><span class="s2">"return"</span><span class="p">,</span><span class="w"> </span><span class="s2">"case"</span><span class="p">}</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nv">theText</span><span class="w">

    </span><span class="k">if</span><span class="w"> </span><span class="nv">keywords</span><span class="w"> </span><span class="ow">contains</span><span class="w"> </span><span class="p">(</span><span class="nv">theText</span><span class="w"> </span><span class="k">as </span><span class="nc">string</span><span class="p">)</span><span class="w"> </span><span class="k">then</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">"`"</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">theText</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="s2">"`"</span><span class="w">
    </span><span class="k">end</span><span class="w"> </span><span class="k">if</span><span class="w">

    </span><span class="nb">return</span><span class="w"> </span><span class="nv">theNewText</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="nv">convertReservedKeywords</span><span class="w">
</span></code></pre></div></div>

<p>Another such case is the <code class="language-plaintext highlighter-rouge">SF Symbols</code> starting with a number literal, like <code class="language-plaintext highlighter-rouge">0.square</code>. In that scenario, we will prepend the name with the string literal <code class="language-plaintext highlighter-rouge">number</code>.</p>

<div class="language-applescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">on</span> <span class="nv">handleNameRestrictions</span><span class="p">(</span><span class="nv">theText</span><span class="p">)</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nv">theText</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">firstCharacter</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">theText</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="nv">isNumber</span><span class="p">(</span><span class="nv">firstCharacter</span><span class="p">)</span><span class="w"> </span><span class="k">then</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">"number"</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">theText</span><span class="w">
    </span><span class="k">end</span><span class="w"> </span><span class="k">if</span><span class="w">

    </span><span class="nb">return</span><span class="w"> </span><span class="nv">convertReservedKeywords</span><span class="p">(</span><span class="nv">theNewText</span><span class="p">)</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="nv">handleNameRestrictions</span><span class="w">

</span><span class="k">on</span> <span class="nv">isNumber</span><span class="p">(</span><span class="nv">theString</span><span class="p">)</span><span class="w">
    </span><span class="k">try</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">theString</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nv">theString</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nv">number</span><span class="w">
        </span><span class="nb">return</span><span class="w"> </span><span class="nb">true</span><span class="w">
    </span><span class="nb">on</span><span class="w"> </span><span class="k">error</span><span class="w">
        </span><span class="nb">return</span><span class="w"> </span><span class="nb">false</span><span class="w">
    </span><span class="k">end</span><span class="w"> </span><span class="k">try</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="nv">isNumber</span><span class="w">
</span></code></pre></div></div>

<p>Also, as you may have noticed, a lot of the <code class="language-plaintext highlighter-rouge">SF Symbols</code> are using the dot (<code class="language-plaintext highlighter-rouge">.</code>) on their name, which is another invalid character for the case identifier. To solve this issue, we will convert the name of the symbol to a camelCase string that we will use as the identifier. To do so, we will remove the dot and instead capitalize the first letter after the dot.</p>

<div class="language-applescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">on</span> <span class="nv">toCamelCase</span><span class="p">(</span><span class="nv">theText</span><span class="p">)</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">""</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">dotIsFound</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">false</span><span class="w">
    </span><span class="k">repeat</span><span class="w"> </span><span class="nv">with</span><span class="w"> </span><span class="nv">aCharacter</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nv">theText</span><span class="w">
        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nv">aCharacter</span><span class="w"> </span><span class="k">as </span><span class="nc">string</span><span class="p">)</span><span class="w"> </span><span class="ow">is equal</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">"."</span><span class="w"> </span><span class="k">then</span><span class="w">
            </span><span class="k">set</span><span class="w"> </span><span class="nv">dotIsFound</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">true</span><span class="w">
        </span><span class="k">else</span><span class="w">
            </span><span class="k">if</span><span class="w"> </span><span class="nv">dotIsFound</span><span class="w"> </span><span class="k">then</span><span class="w">
                </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="p">(</span><span class="nv">theNewText</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">toCapitalized</span><span class="p">(</span><span class="nv">aCharacter</span><span class="p">))</span><span class="w"> </span><span class="k">as </span><span class="nc">string</span><span class="w">
            </span><span class="k">else</span><span class="w">
                </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">aCharacter</span><span class="w">
            </span><span class="k">end</span><span class="w"> </span><span class="k">if</span><span class="w">
            </span><span class="k">set</span><span class="w"> </span><span class="nv">dotIsFound</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">false</span><span class="w">
        </span><span class="k">end</span><span class="w"> </span><span class="k">if</span><span class="w">

    </span><span class="k">end</span><span class="w"> </span><span class="k">repeat</span><span class="w">

    </span><span class="nb">return</span><span class="w"> </span><span class="nv">theNewText</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="nv">toCamelCase</span><span class="w">

</span><span class="k">on</span> <span class="nv">toCapitalized</span><span class="p">(</span><span class="nv">theText</span><span class="p">)</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">""</span><span class="w">

    </span><span class="k">set</span><span class="w"> </span><span class="nv">theComparisonCharacters</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">"abcdefghijklmnopqrstuvwxyz"</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">theSourceCharacters</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">"ABCDEFGHIJKLMNOPQRSTUVWXYZ"</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">firstCharacter</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">theText</span><span class="w">

    </span><span class="k">set</span><span class="w"> </span><span class="nv">stringLength</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">the</span><span class="w"> </span><span class="nv">length</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">theText</span><span class="w">
    </span><span class="k">set</span><span class="w"> </span><span class="nv">restOfText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">""</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="nv">stringLength</span><span class="w"> </span><span class="ow">is greater than</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">then</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">restOfText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">thru</span><span class="w"> </span><span class="o">-</span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">theText</span><span class="w">
    </span><span class="k">end</span><span class="w"> </span><span class="k">if</span><span class="w">

    </span><span class="k">set</span><span class="w"> </span><span class="nv">theOffset</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">offset</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">firstCharacter</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nv">theComparisonCharacters</span><span class="w">
    </span><span class="k">if</span><span class="w"> </span><span class="nv">theOffset</span><span class="w"> </span><span class="ow">is not</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="k">then</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="p">(</span><span class="nv">theNewText</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nb">character</span><span class="w"> </span><span class="nv">theOffset</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">theSourceCharacters</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">restOfText</span><span class="p">)</span><span class="w"> </span><span class="k">as </span><span class="nc">string</span><span class="w">
    </span><span class="k">else</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">theNewText</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="p">(</span><span class="nv">theNewText</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">firstCharacter</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">restOfText</span><span class="p">)</span><span class="w"> </span><span class="k">as </span><span class="nc">string</span><span class="w">
    </span><span class="k">end</span><span class="w"> </span><span class="k">if</span><span class="w">
    </span><span class="nb">return</span><span class="w"> </span><span class="nv">theNewText</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="nv">toCapitalized</span><span class="w">
</span></code></pre></div></div>

<h2 id="implementation">Implementation</h2>

<p>With all those functions in place, we can now shift our focus on the part of our program that will interact with the <code class="language-plaintext highlighter-rouge">SF Symbols</code> app.</p>

<div class="language-applescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">activate</span><span class="w"> </span><span class="nb">application</span><span class="w"> </span><span class="s2">"SF Symbols"</span><span class="w">

</span><span class="k">tell</span><span class="w"> </span><span class="nb">application</span><span class="w"> </span><span class="s2">"System Events"</span><span class="w">
    </span><span class="k">tell</span><span class="w"> </span><span class="nv">process</span><span class="w"> </span><span class="s2">"SF Symbols"</span><span class="w">

        </span><span class="c1">-- Click the “list” radio button.</span><span class="w">
        </span><span class="nv">click</span><span class="w"> </span><span class="nv">radio</span><span class="w"> </span><span class="nb">button</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">radio</span><span class="w"> </span><span class="nv">group</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">group</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="na">toolbar</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="na">window</span><span class="w"> </span><span class="mi">0</span><span class="w">

        </span><span class="k">tell</span><span class="w"> </span><span class="nv">outline</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nb">scroll</span><span class="w"> </span><span class="nv">area</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">splitter</span><span class="w"> </span><span class="nv">group</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="na">window</span><span class="w"> </span><span class="mi">0</span><span class="w">
            </span><span class="nb">select</span><span class="w"> </span><span class="p">(</span><span class="nv">row</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="nb">where</span><span class="w"> </span><span class="nv">value</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">static</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">UI</span><span class="w"> </span><span class="nv">element</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">starts with</span><span class="w"> </span><span class="s2">"All"</span><span class="p">)</span><span class="w">
        </span><span class="k">end</span><span class="w"> </span><span class="k">tell</span><span class="w">

        </span><span class="k">set</span><span class="w"> </span><span class="nv">enumCases</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">""</span><span class="w">

        </span><span class="k">repeat</span><span class="w"> </span><span class="nv">with</span><span class="w"> </span><span class="nv">sfSymbolRow</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nv">rows</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">table</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nb">scroll</span><span class="w"> </span><span class="nv">area</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">group</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">splitter</span><span class="w"> </span><span class="nv">group</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="na">window</span><span class="w"> </span><span class="mi">0</span><span class="w">

            </span><span class="k">set</span><span class="w"> </span><span class="nv">sfSymbolName</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nv">value</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">static</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">UI</span><span class="w"> </span><span class="nv">element</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">sfSymbolRow</span><span class="w">

            </span><span class="k">set</span><span class="w"> </span><span class="nv">caseIdentifier</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="k">my</span><span class="w"> </span><span class="nv">toCamelCase</span><span class="p">(</span><span class="k">my</span><span class="w"> </span><span class="nv">handleNameRestrictions</span><span class="p">(</span><span class="nv">sfSymbolName</span><span class="p">))</span><span class="w">
            </span><span class="k">set</span><span class="w"> </span><span class="nv">enumCases</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nv">enumCases</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="s2">"	case "</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">caseIdentifier</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="s2">" = \""</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">sfSymbolName</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="s2">"\"
"</span><span class="w">
        </span><span class="k">end</span><span class="w"> </span><span class="k">repeat</span><span class="w">

        </span><span class="k">set</span><span class="w"> </span><span class="nv">startOfEnum</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">"public enum SFSymbol: String, CaseIterable {
"</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">endOfEnum</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="s2">"}"</span><span class="w">
        </span><span class="k">set</span><span class="w"> </span><span class="nv">enum</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nv">startOfEnum</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">enumCases</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nv">endOfEnum</span><span class="w">

        </span><span class="nb">set the clipboard to</span><span class="w"> </span><span class="p">{</span><span class="nb">text</span><span class="p">:(</span><span class="nv">enum</span><span class="w"> </span><span class="k">as </span><span class="nc">string</span><span class="p">),</span><span class="w"> </span><span class="nv">Unicode</span><span class="w"> </span><span class="nb">text</span><span class="p">:</span><span class="nv">enum</span><span class="p">}</span><span class="w">
        </span><span class="nv">enum</span><span class="w">
    </span><span class="k">end</span><span class="w"> </span><span class="k">tell</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="k">tell</span><span class="w">
</span></code></pre></div></div>

<p>In this snippet, we initially select the list layout and the option <code class="language-plaintext highlighter-rouge">All</code> from the <code class="language-plaintext highlighter-rouge">Categories</code> as we explained earlier. Then, we create a string variable that will gather all the enum cases.
After that, we loop through the <code class="language-plaintext highlighter-rouge">SF Symbols</code>, and for each one of them, we use the functions we defined earlier to compute the identifier of the case. Then, we append a new case to the string we defined outside of the loop.</p>

<p>After the loop, we will define two more variables that will contain the definition and the trailing bracket of the enum respectively. We will then, merge those three variables to construct a variable with the final version of the enum.</p>

<p>Finally, we will add this to the clipboard to make it easier to paste on the project and use it as the output of the script.</p>

<p>And that’s about it when it comes to the script!</p>

<h2 id="but-what-about-sf-symbols-2">But what about SF Symbols 2?</h2>

<p>This script is also compatible with the new version of the <code class="language-plaintext highlighter-rouge">SF Symbols</code> app with only a minor change. Just replace the two occurrences of <code class="language-plaintext highlighter-rouge">"SF Symbols"</code> to <code class="language-plaintext highlighter-rouge">"SF Symbols beta"</code>.</p>

<p>A word of caution, though: be extra careful because there are some new <code class="language-plaintext highlighter-rouge">SF Symbols</code> that are only available on the latest OS versions while some others like the <code class="language-plaintext highlighter-rouge">bin.xmark</code> are deprecated in favor of new ones (<code class="language-plaintext highlighter-rouge">xmark.bin</code>).</p>

<blockquote>
  <p>You can find the script as well as the enums generated from both the <code class="language-plaintext highlighter-rouge">SF Symbols</code> and <code class="language-plaintext highlighter-rouge">SF Symbols 2</code> app on <a href="https://gist.github.com/diamantidis/7dfa8de52aa2f36a3c64ff30a16dd22a">this GitHub Gist</a>.</p>
</blockquote>

<p>Let’s now see how we can run it!</p>

<h2 id="how-to-run">How to run</h2>

<p>You can use either the <code class="language-plaintext highlighter-rouge">Script Editor</code> app or the <code class="language-plaintext highlighter-rouge">Terminal</code> app. If you use the <code class="language-plaintext highlighter-rouge">Script Editor</code> app, press the play button or use the shortcut Command (<code class="language-plaintext highlighter-rouge">⌘</code>) + R.</p>

<p>If you decide to run the script from the <code class="language-plaintext highlighter-rouge">Terminal</code>, you can use the command <code class="language-plaintext highlighter-rouge">osascript &lt;name of the script&gt;.scpt</code>. The output of this command will be the enum, which you can redirect to a Swift file on your project: <code class="language-plaintext highlighter-rouge">osascript &lt;name of the script&gt;.scpt &gt; GeneratedSFSymbols.swift</code>.</p>

<blockquote>
  <p>:bulb: <strong>TIP</strong>: You can place the script on a specific folder and then use the absolute path of this folder to create an alias on your <code class="language-plaintext highlighter-rouge">~/.bash_profile</code> or <code class="language-plaintext highlighter-rouge">~/.zshrc</code> file. <br /><br /> <code class="language-plaintext highlighter-rouge">alias sfsymbols='&lt;path to the script&gt;/&lt;name of the script&gt;.scpt'</code>. <br /><br /> This will allow you to run this script with ease from any project: <code class="language-plaintext highlighter-rouge">sfsymbols &gt; GeneratedSFSymbols.swift</code></p>
</blockquote>

<blockquote>
  <p>:grey_exclamation: <strong>INFO</strong>: Regardless of how you run the script, you will probably get a prompt to grant Accessibility Access like in the following screenshot. To grant the required access, go to <code class="language-plaintext highlighter-rouge">System Preferences</code> &gt; <code class="language-plaintext highlighter-rouge">Security &amp; Privacy</code> &gt; <code class="language-plaintext highlighter-rouge">Privacy</code> &gt; <code class="language-plaintext highlighter-rouge">Accessibility</code> and select the program that you are using to run the script.  <img src="https://diamantidis.github.io/assets/sf_symbols_applescript/apple_script_warning.png" alt="AppleScript warning screenshot" /></p>
</blockquote>

<p>Now that we have added the enum in our project, it’s time to find out how we can use it!</p>

<h2 id="how-to-use">How to use</h2>

<p>You can simply add an extension for the component you are interested in.</p>

<p>For example, if you are using SwiftUI, you can add an extension to the <code class="language-plaintext highlighter-rouge">Image</code> struct:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">SwiftUI</span>

<span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">13.0</span><span class="p">,</span> <span class="n">macOS</span> <span class="mf">10.15</span><span class="p">,</span> <span class="n">tvOS</span> <span class="mf">13.0</span><span class="p">,</span> <span class="n">watchOS</span> <span class="mf">6.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">Image</span> <span class="p">{</span>
    <span class="c1">/// Creates an image object containing a system symbol image.</span>
    <span class="c1">///</span>
    <span class="c1">/// - Parameter sfSymbol: The name of the `SFSymbol`</span>
    <span class="c1">/// - Usage</span>
    <span class="c1">///   ```</span>
    <span class="c1">///   Image(sfSymbol: .checkmarkCircleFill)</span>
    <span class="c1">///   ```</span>
    <span class="kd">@available</span><span class="p">(</span><span class="kt">OSX</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">)</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">sfSymbol</span><span class="p">:</span> <span class="kt">SFSymbol</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="n">sfSymbol</span><span class="o">.</span><span class="n">rawValue</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Similarly, if you are using UIKit’s UIImage, you can use the following extension:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">UIKit</span>

<span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">13.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UIImage</span> <span class="p">{</span>
    <span class="c1">/// Creates an image object containing a system symbol image.</span>
    <span class="c1">///</span>
    <span class="c1">/// - Parameter sfSymbol: The name of the `SFSymbol`</span>
    <span class="c1">/// - Usage</span>
    <span class="c1">///   ```</span>
    <span class="c1">///   UIImage(sfSymbol: .checkmarkCircleFill)</span>
    <span class="c1">///   ```</span>
    <span class="kd">convenience</span> <span class="nf">init</span><span class="p">?(</span><span class="nv">sfSymbol</span><span class="p">:</span> <span class="kt">SFSymbol</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="n">sfSymbol</span><span class="o">.</span><span class="n">rawValue</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>And that’s about it! In this post, we have seen how we can utilize the ability of AppleScript to interact with other macOS apps to generate an enum for all the symbols on the <code class="language-plaintext highlighter-rouge">SF Symbols</code> app.
This will alleviate all the pains that come with the use of an error-prone string literal when we create an Image based on an <code class="language-plaintext highlighter-rouge">SF Symbol</code>. Instead, with this enum we will be able to create those Images in a type-safe manner.</p>

<p>Thanks for reading, I hope you find this post useful.</p>

<p>If you like this post and you want to get notified when a new post is published, you can follow me on <a href="https://x.com/diamantidis_io">X</a> or subscribe to the <a href="https://diamantidis.github.io/feed.xml">RSS feed</a>.</p>

<p>Also, if you have any questions or comments about this post, feel free to contact me on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="AppleScript" /><category term="SF Symbols" /><summary type="html"><![CDATA[Use SF Symbols in a type-safe manner with an enum generated with AppleScript]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/applescript-to-generate-enum-for-sf-symbols.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/applescript-to-generate-enum-for-sf-symbols.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Keyboards options for SwiftUI fields</title><link href="https://diamantidis.github.io/2020/06/21/keyboard-options-for-swiftui-fields" rel="alternate" type="text/html" title="Keyboards options for SwiftUI fields" /><published>2020-06-21T04:00:00+00:00</published><updated>2021-10-30T04:00:00+00:00</updated><id>https://diamantidis.github.io/2020/06/21/keyboard-options-for-swiftui-fields</id><content type="html" xml:base="https://diamantidis.github.io/2020/06/21/keyboard-options-for-swiftui-fields"><![CDATA[<p>One of the ways to improve the user experience when they fill a form on iOS apps is the use of keyboard types. Keyboard types can be used to show different keyboards based on the context of the field. If it’s an email field, for example, you would probably prefer to present a keyboard with easy access to characters like the at (<code class="language-plaintext highlighter-rouge">@</code>) and the dot (<code class="language-plaintext highlighter-rouge">.</code>).</p>

<p>In this post, we are going to see how different keyboard options work with SwiftUI. First, we will cover the “common” scenarios, which include keyboards for fields like name, email, number, etc. Then, we will take a look at how we can add more custom options like a keyboard with a picker view.</p>

<h2 id="the-common-cases">The common cases</h2>

<p>For the most common cases, SwiftUI provides the function <code class="language-plaintext highlighter-rouge">keyboardType</code> in a <code class="language-plaintext highlighter-rouge">View</code> extension. This function has a parameter of type <code class="language-plaintext highlighter-rouge">UIKeyboardType</code>, which is an enum with cases like <code class="language-plaintext highlighter-rouge">emailAddress</code>, <code class="language-plaintext highlighter-rouge">numberPad</code>, <code class="language-plaintext highlighter-rouge">URL</code>, etc.</p>

<blockquote>
  <p>For the full set of cases, you can refer to the <a href="https://developer.apple.com/documentation/uikit/uikeyboardtype">documentation page</a>.</p>
</blockquote>

<p>You can use this extension from any SwiftUI View in the following way:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">TextField</span><span class="p">(</span><span class="s">"Type the email..."</span><span class="p">,</span> <span class="nv">text</span><span class="p">:</span> <span class="err">$</span><span class="n">email</span><span class="p">)</span>
    <span class="o">.</span><span class="nf">keyboardType</span><span class="p">(</span><span class="o">.</span><span class="n">emailAddress</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="https://diamantidis.github.io/assets/swiftui_textfield_keyboards/email-keyboard.png" alt="Email keyboard screenshot" /></p>

<p>But what if we want to add a custom view? For example, a picker view, just like we can do using UIKit?</p>

<h2 id="keyboard-with-picker-view">Keyboard with picker view</h2>

<p>As you may have guessed, the answer lies exactly there.</p>

<p>We will have to use UIKit’s <code class="language-plaintext highlighter-rouge">UITextField</code> and make it available to SwiftUI by creating a struct with conformance to the <code class="language-plaintext highlighter-rouge">UIViewRepresentable</code> protocol.</p>

<p>But let’s take it step by step and see how we can create <code class="language-plaintext highlighter-rouge">PickerField</code>; a field that will show a keyboard with a picker, just like in the following screenshot.</p>

<p><img src="https://diamantidis.github.io/assets/swiftui_textfield_keyboards/picker-view-keyboard.png" alt="Picker view keyboard screenshot" /></p>

<p>To keep things separated, let’s create a subclass of <code class="language-plaintext highlighter-rouge">UITextField</code> where we will implement the logic to show a keyboard with a picker.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">SwiftUI</span>

<span class="kd">class</span> <span class="kt">PickerTextField</span><span class="p">:</span> <span class="kt">UITextField</span> <span class="p">{</span>
    <span class="c1">// MARK: - Public properties</span>
    <span class="k">var</span> <span class="nv">data</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selectionIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span>

    <span class="c1">// MARK: - Initializers</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">],</span> <span class="nv">selectionIndex</span><span class="p">:</span> <span class="kt">Binding</span><span class="o">&lt;</span><span class="kt">Int</span><span class="p">?</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
        <span class="k">self</span><span class="o">.</span><span class="n">_selectionIndex</span> <span class="o">=</span> <span class="n">selectionIndex</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="o">.</span><span class="n">zero</span><span class="p">)</span>

        <span class="k">self</span><span class="o">.</span><span class="n">inputView</span> <span class="o">=</span> <span class="n">pickerView</span>
        <span class="k">self</span><span class="o">.</span><span class="n">inputAccessoryView</span> <span class="o">=</span> <span class="n">toolbar</span>
        <span class="k">self</span><span class="o">.</span><span class="n">tintColor</span> <span class="o">=</span> <span class="o">.</span><span class="n">clear</span>

        <span class="k">guard</span> <span class="k">let</span> <span class="nv">selectionIndex</span> <span class="o">=</span> <span class="n">selectionIndex</span><span class="o">.</span><span class="n">wrappedValue</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span>
        <span class="p">}</span>

        <span class="k">self</span><span class="o">.</span><span class="n">pickerView</span><span class="o">.</span><span class="nf">selectRow</span><span class="p">(</span><span class="n">selectionIndex</span><span class="p">,</span> <span class="nv">inComponent</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">@available</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">unavailable</span><span class="p">)</span>
    <span class="kd">required</span> <span class="nf">init</span><span class="p">?(</span><span class="nv">coder</span><span class="p">:</span> <span class="kt">NSCoder</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">fatalError</span><span class="p">(</span><span class="s">"init(coder:) has not been implemented"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="c1">// MARK: - Private properties</span>
    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">pickerView</span><span class="p">:</span> <span class="kt">UIPickerView</span> <span class="o">=</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">pickerView</span> <span class="o">=</span> <span class="kt">UIPickerView</span><span class="p">()</span>
        <span class="n">pickerView</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span>
        <span class="n">pickerView</span><span class="o">.</span><span class="n">dataSource</span> <span class="o">=</span> <span class="k">self</span>
        <span class="k">return</span> <span class="n">pickerView</span>
    <span class="p">}()</span>

    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">toolbar</span><span class="p">:</span> <span class="kt">UIToolbar</span> <span class="o">=</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">toolbar</span> <span class="o">=</span> <span class="kt">UIToolbar</span><span class="p">()</span>

        <span class="k">let</span> <span class="nv">flexibleSpace</span> <span class="o">=</span> <span class="kt">UIBarButtonItem</span><span class="p">(</span><span class="nv">barButtonSystemItem</span><span class="p">:</span> <span class="o">.</span><span class="n">flexibleSpace</span><span class="p">,</span> <span class="nv">target</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>

        <span class="k">let</span> <span class="nv">doneButton</span> <span class="o">=</span> <span class="kt">UIBarButtonItem</span><span class="p">(</span>
            <span class="nv">title</span><span class="p">:</span> <span class="s">"Done"</span><span class="p">,</span>
            <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">done</span><span class="p">,</span>
            <span class="nv">target</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span>
            <span class="nv">action</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">donePressed</span><span class="kd">)</span>
        <span class="p">)</span>

        <span class="n">toolbar</span><span class="o">.</span><span class="nf">setItems</span><span class="p">([</span><span class="n">flexibleSpace</span><span class="p">,</span> <span class="n">doneButton</span><span class="p">],</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
        <span class="n">toolbar</span><span class="o">.</span><span class="nf">sizeToFit</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">toolbar</span>
    <span class="p">}()</span>

    <span class="c1">// MARK: - Private methods</span>
    <span class="kd">@objc</span>
    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">donePressed</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">selectionIndex</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">pickerView</span><span class="o">.</span><span class="nf">selectedRow</span><span class="p">(</span><span class="nv">inComponent</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">endEditing</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// MARK: - UIPickerViewDataSource &amp; UIPickerViewDelegate extension</span>
<span class="kd">extension</span> <span class="kt">PickerTextField</span><span class="p">:</span> <span class="kt">UIPickerViewDataSource</span><span class="p">,</span> <span class="kt">UIPickerViewDelegate</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">numberOfComponents</span><span class="p">(</span><span class="k">in</span> <span class="nv">pickerView</span><span class="p">:</span> <span class="kt">UIPickerView</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mi">1</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">pickerView</span><span class="p">(</span><span class="n">_</span> <span class="nv">pickerView</span><span class="p">:</span> <span class="kt">UIPickerView</span><span class="p">,</span> <span class="n">numberOfRowsInComponent</span> <span class="nv">component</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">count</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">pickerView</span><span class="p">(</span><span class="n">_</span> <span class="nv">pickerView</span><span class="p">:</span> <span class="kt">UIPickerView</span><span class="p">,</span> <span class="n">titleForRow</span> <span class="nv">row</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">forComponent</span> <span class="nv">component</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="n">row</span><span class="p">]</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">pickerView</span><span class="p">(</span><span class="n">_</span> <span class="nv">pickerView</span><span class="p">:</span> <span class="kt">UIPickerView</span><span class="p">,</span> <span class="n">didSelectRow</span> <span class="nv">row</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">inComponent</span> <span class="nv">component</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">selectionIndex</span> <span class="o">=</span> <span class="n">row</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this snippet, first, we declare two properties; one with the options of the picker and a <code class="language-plaintext highlighter-rouge">Binding</code> property which acts both as an input in case the field is pre-filled and as an output for when the user changes the value of the picker.</p>

<p>Then, in the initializer, we are expecting two arguments that we will use to instantiate the two properties. After that, we set the property <code class="language-plaintext highlighter-rouge">inputView</code> to the instance of the <code class="language-plaintext highlighter-rouge">UIPickerView</code>, and use the <code class="language-plaintext highlighter-rouge">inputAccessoryView</code> to set a <code class="language-plaintext highlighter-rouge">UIToolbar</code> with a “Done” button to help users dismiss the keyboard.</p>

<p>Finally, if there is a pre-selected value, we select this option from the <code class="language-plaintext highlighter-rouge">UIPickerView</code>.</p>

<p>In this file, we also add an extension to provide the implementation for the functions to conform to <code class="language-plaintext highlighter-rouge">UIPickerViewDataSource</code> and <code class="language-plaintext highlighter-rouge">UIPickerViewDelegate</code> and set the <code class="language-plaintext highlighter-rouge">dataSource</code> and the <code class="language-plaintext highlighter-rouge">delegate</code> for the <code class="language-plaintext highlighter-rouge">UIPickerView</code> to self.</p>

<p>Worth noting is the implementation of <code class="language-plaintext highlighter-rouge">didSelectRow</code>, where we set the selected row to the <code class="language-plaintext highlighter-rouge">Binding</code> property <code class="language-plaintext highlighter-rouge">selectionIndex</code> to pass this info to the parent view.</p>

<p>With that in place, we will create a new struct that we will use to communicate between the UIKit view and the SwiftUI world.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">SwiftUI</span>

<span class="kd">struct</span> <span class="kt">PickerField</span><span class="p">:</span> <span class="kt">UIViewRepresentable</span> <span class="p">{</span>
    <span class="c1">// MARK: - Public properties</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selectionIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span>

    <span class="c1">// MARK: - Initializers</span>
    <span class="kd">init</span><span class="o">&lt;</span><span class="kt">S</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">S</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">],</span> <span class="nv">selectionIndex</span><span class="p">:</span> <span class="kt">Binding</span><span class="o">&lt;</span><span class="kt">Int</span><span class="p">?</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">where</span> <span class="kt">S</span><span class="p">:</span> <span class="kt">StringProtocol</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">placeholder</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="n">title</span><span class="p">)</span>
        <span class="k">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
        <span class="k">self</span><span class="o">.</span><span class="n">_selectionIndex</span> <span class="o">=</span> <span class="n">selectionIndex</span>

        <span class="n">textField</span> <span class="o">=</span> <span class="kt">PickerTextField</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="n">data</span><span class="p">,</span> <span class="nv">selectionIndex</span><span class="p">:</span> <span class="n">selectionIndex</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="c1">// MARK: - Public methods</span>
    <span class="kd">func</span> <span class="nf">makeUIView</span><span class="p">(</span><span class="nv">context</span><span class="p">:</span> <span class="kt">UIViewRepresentableContext</span><span class="o">&lt;</span><span class="kt">PickerField</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UITextField</span> <span class="p">{</span>
        <span class="n">textField</span><span class="o">.</span><span class="n">placeholder</span> <span class="o">=</span> <span class="n">placeholder</span>
        <span class="k">return</span> <span class="n">textField</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">updateUIView</span><span class="p">(</span><span class="n">_</span> <span class="nv">uiView</span><span class="p">:</span> <span class="kt">UITextField</span><span class="p">,</span> <span class="nv">context</span><span class="p">:</span> <span class="kt">UIViewRepresentableContext</span><span class="o">&lt;</span><span class="kt">PickerField</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">index</span> <span class="o">=</span> <span class="n">selectionIndex</span> <span class="p">{</span>
            <span class="n">uiView</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">uiView</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s">""</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// MARK: - Private properties</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">placeholder</span><span class="p">:</span> <span class="kt">String</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">data</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">textField</span><span class="p">:</span> <span class="kt">PickerTextField</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Same as the <code class="language-plaintext highlighter-rouge">PickerTextField</code> class, this struct will also have two properties; a list with the options and a binding property for the value of the field.</p>

<p>Once again, we will use the initializer to pass the values of these properties via its arguments. The initializer takes one more argument for the placeholder of the <code class="language-plaintext highlighter-rouge">UITextField</code> if the field has no value.</p>

<p>Then, we provide the implementations for the <code class="language-plaintext highlighter-rouge">makeUIView</code> and <code class="language-plaintext highlighter-rouge">updateUIView</code> requirements of the <code class="language-plaintext highlighter-rouge">UIViewRepresentable</code> protocol.</p>

<p>In the first one, we set the initial state for the text field by setting the value for the placeholder. In the second, we update the value of the <code class="language-plaintext highlighter-rouge">PickerField</code> with any new information we might get from SwiftUI.</p>

<p>Finally, from any SwiftUI view, we can use this struct in the following way:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@State</span> <span class="k">var</span> <span class="nv">selectedIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="k">let</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s">"GraphQL"</span><span class="p">,</span> <span class="s">"Swift"</span><span class="p">,</span> <span class="s">"Vapor"</span><span class="p">]</span>

<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="c1">// ...</span>
    <span class="kt">PickerField</span><span class="p">(</span><span class="s">"Select an option"</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">options</span><span class="p">,</span> <span class="nv">selectionIndex</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="err">$</span><span class="n">selectedIndex</span><span class="p">)</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can find the code for this <a href="https://gist.github.com/diamantidis/061d101853f6400f76780345614b2c90#file-pickertextfield-swift"><code class="language-plaintext highlighter-rouge">PickerTextField</code></a>, as well as <a href="https://gist.github.com/diamantidis/061d101853f6400f76780345614b2c90#file-pickerfield-swift">the rest of the code</a> and <a href="https://gist.github.com/diamantidis/061d101853f6400f76780345614b2c90#file-contentview-swift">an example of how to use it from a SwiftUI View</a> on this <a href="https://gist.github.com/diamantidis/061d101853f6400f76780345614b2c90">Gist</a>.</p>

<h2 id="conclusion">Conclusion</h2>
<p>And that’s about it! In this post, we have first seen what is the current level of support for keyboard types on SwiftUI’s <code class="language-plaintext highlighter-rouge">TextField</code>. Then, we investigated how we can provide support for more options by relying on the UIKit’s UITextField, and its property <code class="language-plaintext highlighter-rouge">inputView</code>. This way, we can present a keyboard with a <code class="language-plaintext highlighter-rouge">UIPickerView</code>, or any other view we may want.</p>

<p>Thanks for reading, I hope you find this post useful and if you have any questions or comments about this post, feel free to contact me on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Swift" /><category term="SwiftUI" /><category term="iOS" /><summary type="html"><![CDATA[A post exploring the keyboard options on SwiftUI. From the keyboardType function to using custom views like a picker view]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/keyboard-options-for-swiftui-fields.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/keyboard-options-for-swiftui-fields.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">GraphQL mutations for iOS apps (with the help of Combine’s Future)</title><link href="https://diamantidis.github.io/2020/06/14/graphql-mutations-for-ios-app" rel="alternate" type="text/html" title="GraphQL mutations for iOS apps (with the help of Combine’s Future)" /><published>2020-06-14T04:00:00+00:00</published><updated>2020-06-14T04:00:00+00:00</updated><id>https://diamantidis.github.io/2020/06/14/graphql-mutations-for-ios-app</id><content type="html" xml:base="https://diamantidis.github.io/2020/06/14/graphql-mutations-for-ios-app"><![CDATA[<p>Nowadays, more and more apps rely on a server to support their functionality. One part of this communication is the ability of the app to create new and modify existing data stored on the server.</p>

<p>In GraphQL terms, this can be accomplished with the so-called <code class="language-plaintext highlighter-rouge">Mutations</code>. GraphQL uses the term <code class="language-plaintext highlighter-rouge">Mutations</code> to distinguish the queries that will result in some kind of side-effect on the server-side data, from the normal queries that we are using to just fetch data.</p>

<p>Long story short, in this post, we are going to see how we can perform GraphQL mutations from an iOS app using the <a href="https://www.apollographql.com/docs/ios/">Apollo iOS client</a> and with a little help from <a href="https://developer.apple.com/documentation/combine/future">Combine’s Future</a>.</p>

<p>The scenario that we want to accomplish is quite simple. First, we are going to fetch some data from the server and then we are going to create a new entry. Once it is created, we will update it and finally delete it.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>This post is a continuation of a series of posts about GraphQL and Swift. In the previous posts, we have seen <a href="/2020/05/24/swift-loves-graphql-server-with-vapor-and-ios-app-client">how to setup an iOS project to fetch</a> and <a href="/2020/05/31/custom-graphql-types-on-swift-projects">decode data from a GraphQL server</a>. This time we will see how to modify server-side data using GraphQL’s mutations.</p>

<p>For the server, we are going to use a GraphQL server built with the Vapor framework. The project, along with instructions on how to set it up, is available on <a href="https://github.com/diamantidis/vapor-graphql">GitHub</a> or you can refer to the previous posts to learn more on <a href="/2020/05/24/swift-loves-graphql-server-with-vapor-and-ios-app-client">how to setup</a>, <a href="/2020/05/31/custom-graphql-types-on-swift-projects">add fields with custom types</a> and <a href="/2020/06/07/mutations-on-a-graphql-server-built-with-vapor">add mutations</a>.</p>

<p>Also, the iOS project will be based on the project we have built on the previous posts. This project and the instructions on how to set it up are also available on <a href="https://github.com/diamantidis/ios-graphql">GitHub</a>.</p>

<p>In those previous posts, I was using a model representing a post and I will continue doing so on this post as well.</p>

<h2 id="a-future-and-promise-primer">A Future and Promise Primer</h2>

<p>As I mentioned before, we are going to use <a href="https://developer.apple.com/documentation/combine/future">Combine’s Future</a>.</p>

<p>Future was introduced on iOS 13 and is a <code class="language-plaintext highlighter-rouge">Publisher</code> that represents the result of an asynchronous operation. Practically, it will generate a single value or an error and then it will finish.</p>

<p>Future is defined as a generic with two types, one for the type of the value and one for the type of the error.
Its initializer takes a single argument, which is a closure of type <code class="language-plaintext highlighter-rouge">(Promise) -&gt; Void</code>.</p>

<p>As you can see, the closure has an argument of type <code class="language-plaintext highlighter-rouge">Promise</code>. <code class="language-plaintext highlighter-rouge">Promise</code> itself is a closure that takes a <code class="language-plaintext highlighter-rouge">Result</code> as a single parameter and is defined as a typealias for <code class="language-plaintext highlighter-rouge">(Result&lt;Output, Failure&gt;) -&gt; Void</code>.</p>

<p>Inside Future’s closure, we are using the instance of <code class="language-plaintext highlighter-rouge">Promise</code> and pass either <code class="language-plaintext highlighter-rouge">.success</code> or <code class="language-plaintext highlighter-rouge">.failure</code> as a parameter to determine if the asynchronous operation was successful or not.</p>

<p>Let’s see a simple example:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">AppError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">random</span>
<span class="p">}</span>

<span class="k">let</span> <span class="nv">future</span> <span class="o">=</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">AppError</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">promise</span> <span class="k">in</span>
    <span class="k">if</span> <span class="kc">true</span> <span class="p">{</span>
        <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="s">"🎉"</span><span class="p">))</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="kt">AppError</span><span class="o">.</span><span class="n">random</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In this example, our Future can either generate a <code class="language-plaintext highlighter-rouge">String</code> value or it will fail with an error of type <code class="language-plaintext highlighter-rouge">AppError</code>. Then, inside the closure, we are passing the result to the promise closure.</p>

<p>And that’s brief intro to Combine’s Future. Let’s now jump to the GraphQL mutations!</p>

<h2 id="implementation">Implementation</h2>

<p>Having setup the project as it was at the end of the <a href="/2020/05/31/custom-graphql-types-on-swift-projects">previous post</a>, we will have to update the schema to fetch the definitions for the mutations.</p>

<p>With the server running, run the following command from the root directory of the iOS project to update the <code class="language-plaintext highlighter-rouge">schema.json</code>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apollo schema:download <span class="nt">--endpoint</span><span class="o">=</span>http://127.0.0.1:8080/graphql iOSGraphQL/GraphQL/schema.json
</code></pre></div></div>

<p>Once completed, we will head over to Xcode and inside the GraphQL group, we will create three files for the three mutations: <code class="language-plaintext highlighter-rouge">CreatePost.graphql</code>, <code class="language-plaintext highlighter-rouge">EditPost.graphql</code> and <code class="language-plaintext highlighter-rouge">DeletePost.graphql</code>.</p>

<p>Add the following snippets as content on these files respectively:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">#</span> <span class="nx">CreatePost</span><span class="p">.</span><span class="nx">graphql</span>
<span class="nx">mutation</span> <span class="nx">CreatePost</span><span class="p">(</span><span class="nx">$input</span><span class="p">:</span> <span class="nx">PostInput</span><span class="o">!</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">createPost</span><span class="p">(</span><span class="nx">input</span><span class="p">:</span> <span class="nx">$input</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">id</span>
    <span class="nx">title</span>
    <span class="nx">publishedAt</span>
    <span class="nx">tags</span>
    <span class="nx">author</span> <span class="p">{</span>
      <span class="p">...</span><span class="nx">AuthorDetails</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">#</span> <span class="nx">EditPost</span><span class="p">.</span><span class="nx">graphql</span>
<span class="nx">mutation</span> <span class="nx">EditPost</span><span class="p">(</span><span class="nx">$id</span><span class="p">:</span> <span class="nx">CustomUUID</span><span class="o">!</span><span class="p">,</span> <span class="nx">$title</span><span class="p">:</span> <span class="nb">String</span><span class="o">!</span><span class="p">,</span> <span class="nx">$tags</span><span class="p">:</span> <span class="p">[</span><span class="nx">Tag</span><span class="o">!</span><span class="p">]</span><span class="o">!</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">editPost</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="nx">$id</span><span class="p">,</span> <span class="nx">tags</span><span class="p">:</span> <span class="nx">$tags</span><span class="p">,</span> <span class="nx">title</span><span class="p">:</span> <span class="nx">$title</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">id</span>
    <span class="nx">title</span>
    <span class="nx">publishedAt</span>
    <span class="nx">tags</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">#</span> <span class="nx">DeletePost</span><span class="p">.</span><span class="nx">graphql</span>
<span class="nx">mutation</span> <span class="nx">DeletePost</span><span class="p">(</span><span class="nx">$id</span><span class="p">:</span> <span class="nx">CustomUUID</span><span class="o">!</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">deletePost</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="nx">$id</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>All those queries follow the same pattern. We set the arguments, pass them to the mutation, and define the result data. With all the queries in place, we will run the <code class="language-plaintext highlighter-rouge">apollo codegen:generate</code> command to update the <code class="language-plaintext highlighter-rouge">API.swift</code>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./Pods/Apollo/scripts/apollo/bin/apollo codegen:generate <span class="nt">--target</span><span class="o">=</span>swift <span class="s1">'--includes=./**/*.graphql'</span> <span class="nt">--localSchemaFile</span><span class="o">=</span>./iOSGraphQL/GraphQL/schema.json <span class="nt">--passthroughCustomScalars</span> ./iOSGraphQL/GraphQL/API.swift
</code></pre></div></div>

<p>Before adding the implementation for the mutation, let’s create a function that will be responsible for fetching the existing posts. The return type will be a <code class="language-plaintext highlighter-rouge">Future</code> with <code class="language-plaintext highlighter-rouge">[Post]</code> as the <code class="language-plaintext highlighter-rouge">Output</code> type and <code class="language-plaintext highlighter-rouge">GraphQLError</code> as the <code class="language-plaintext highlighter-rouge">Failure</code> type and it will look like the following snippet:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">func</span> <span class="nf">fetchPosts</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="p">[</span><span class="kt">Post</span><span class="p">],</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">query</span> <span class="o">=</span> <span class="kt">AllPostsQuery</span><span class="p">()</span>

    <span class="k">let</span> <span class="nv">future</span> <span class="o">=</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="p">[</span><span class="kt">Post</span><span class="p">],</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">promise</span> <span class="k">in</span>
        <span class="kt">GraphQLClient</span><span class="o">.</span><span class="n">apollo</span><span class="o">.</span><span class="nf">fetch</span><span class="p">(</span><span class="nv">query</span><span class="p">:</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="n">result</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span><span class="o">.</span><span class="n">data</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="kt">GraphQLError</span><span class="o">.</span><span class="n">fetchError</span><span class="p">))</span>
            <span class="p">}</span>
            <span class="k">let</span> <span class="nv">posts</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">posts</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="kt">Post</span><span class="p">(</span><span class="nv">post</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
            <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">posts</span><span class="p">))</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">future</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this snippet, we are creating an instance of the query that we want to perform and then we create a future. Inside the Future’s closure, we will initialize the fetch request and once it’s completed, we will try to get the response. If the response is an error, then we are going to reject the Future by passing an error to its promise.
Otherwise, we will map the response to the <code class="language-plaintext highlighter-rouge">Post</code> model and then pass <code class="language-plaintext highlighter-rouge">.success</code> with this list of posts to the promise.</p>

<p>For the scope of this post, the <code class="language-plaintext highlighter-rouge">GraphQLError</code> will be a simple Error enum:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">GraphQLError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">fetchError</span>
    <span class="k">case</span> <span class="n">createError</span>
    <span class="k">case</span> <span class="n">editError</span>
    <span class="k">case</span> <span class="n">deleteError</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, let’s see how we can perform the mutation queries!</p>

<p>Following the same logic as with the <code class="language-plaintext highlighter-rouge">fetchPosts</code>, we are going to add three more functions to create, update, and delete a post:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">createPost</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="kt">Tag</span><span class="p">],</span> <span class="nv">authorId</span><span class="p">:</span> <span class="kt">CustomUUID</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">CreatePostMutation</span><span class="o">.</span><span class="kt">Data</span><span class="o">.</span><span class="kt">CreatePost</span><span class="p">,</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">input</span> <span class="o">=</span> <span class="kt">PostInput</span><span class="p">(</span><span class="nv">authorId</span><span class="p">:</span> <span class="n">authorId</span><span class="p">,</span> <span class="nv">tags</span><span class="p">:</span> <span class="n">tags</span><span class="p">,</span> <span class="nv">title</span><span class="p">:</span> <span class="n">title</span><span class="p">)</span>
        <span class="k">let</span> <span class="nv">mutation</span> <span class="o">=</span> <span class="kt">CreatePostMutation</span><span class="p">(</span><span class="nv">input</span><span class="p">:</span> <span class="n">input</span><span class="p">)</span>

        <span class="k">let</span> <span class="nv">future</span> <span class="o">=</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">CreatePostMutation</span><span class="o">.</span><span class="kt">Data</span><span class="o">.</span><span class="kt">CreatePost</span><span class="p">,</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">promise</span> <span class="k">in</span>
            <span class="kt">GraphQLClient</span><span class="o">.</span><span class="n">apollo</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span><span class="nv">mutation</span><span class="p">:</span> <span class="n">mutation</span><span class="p">)</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
                <span class="k">guard</span> <span class="k">let</span> <span class="nv">post</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="n">result</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span><span class="o">.</span><span class="n">data</span><span class="p">?</span><span class="o">.</span><span class="n">createPost</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="o">.</span><span class="n">createError</span><span class="p">))</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">post</span><span class="p">))</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">future</span>
    <span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">editPost</span><span class="p">(</span><span class="n">with</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span><span class="p">,</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="kt">Tag</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">EditPostMutation</span><span class="o">.</span><span class="kt">Data</span><span class="o">.</span><span class="kt">EditPost</span><span class="p">,</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">mutation</span> <span class="o">=</span> <span class="kt">EditPostMutation</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="n">id</span><span class="p">,</span> <span class="nv">title</span><span class="p">:</span> <span class="n">title</span><span class="p">,</span> <span class="nv">tags</span><span class="p">:</span> <span class="n">tags</span><span class="p">)</span>

        <span class="k">let</span> <span class="nv">future</span> <span class="o">=</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">EditPostMutation</span><span class="o">.</span><span class="kt">Data</span><span class="o">.</span><span class="kt">EditPost</span><span class="p">,</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">promise</span> <span class="k">in</span>
            <span class="kt">GraphQLClient</span><span class="o">.</span><span class="n">apollo</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span><span class="nv">mutation</span><span class="p">:</span> <span class="n">mutation</span><span class="p">)</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
                <span class="k">guard</span> <span class="k">let</span> <span class="nv">post</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="n">result</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span><span class="o">.</span><span class="n">data</span><span class="p">?</span><span class="o">.</span><span class="n">editPost</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="o">.</span><span class="n">editError</span><span class="p">))</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">post</span><span class="p">))</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">future</span>
    <span class="p">}</span>
</code></pre></div></div>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">deletePost</span><span class="p">(</span><span class="n">with</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">Bool</span><span class="p">,</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">mutation</span> <span class="o">=</span> <span class="kt">DeletePostMutation</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="n">id</span><span class="p">)</span>

        <span class="k">let</span> <span class="nv">future</span> <span class="o">=</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">Bool</span><span class="p">,</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">promise</span> <span class="k">in</span>
            <span class="kt">GraphQLClient</span><span class="o">.</span><span class="n">apollo</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span><span class="nv">mutation</span><span class="p">:</span> <span class="n">mutation</span><span class="p">)</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
                <span class="k">guard</span> <span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="n">result</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span><span class="o">.</span><span class="n">data</span><span class="p">?</span><span class="o">.</span><span class="n">deletePost</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="o">.</span><span class="n">deleteError</span><span class="p">))</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">future</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>In all these 3 functions, we are following the same logic. We accept a set of arguments, which we use to create an instance of a mutation. Those Mutation classes were generated based on the GraphQL queries when we ran the <code class="language-plaintext highlighter-rouge">apollo codegen:generate</code> command.</p>

<p>Then we create a future and inside its closure we perform the mutation request. Once it is completed, we check if the response is successful or not, and based on that we pass the corresponding result on the promise.</p>

<p>Lastly, we will need to provide an extension for the <code class="language-plaintext highlighter-rouge">CustomUUID</code> structure to make it conform to the <code class="language-plaintext highlighter-rouge">JSONEncodable</code> protocol. This will allow us to use it as an argument on the GraphQL mutation queries.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">CustomUUID</span><span class="p">:</span> <span class="kt">JSONEncodable</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="k">var</span> <span class="nv">jsonValue</span><span class="p">:</span> <span class="kt">JSONValue</span> <span class="p">{</span>
        <span class="k">return</span> <span class="p">[</span><span class="s">"value"</span><span class="p">:</span> <span class="n">value</span><span class="o">.</span><span class="n">uuidString</span><span class="p">]</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, let’s see how we can use those functions to make the requests.
<code class="language-plaintext highlighter-rouge">Future</code> conforms to <code class="language-plaintext highlighter-rouge">Publisher</code> which means that we can use any of the <code class="language-plaintext highlighter-rouge">Publisher</code>’s functions. In our case, we are going to use <code class="language-plaintext highlighter-rouge">flatMap</code> and <code class="language-plaintext highlighter-rouge">sink</code> to combine and trigger the sequence of operations.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cancellable</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="nf">fetchPosts</span><span class="p">()</span>
    <span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">posts</span> <span class="k">in</span>
        <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="nf">createPost</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"New post"</span><span class="p">,</span> <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="n">swift</span><span class="p">],</span> <span class="nv">authorId</span><span class="p">:</span> <span class="n">posts</span><span class="o">.</span><span class="n">first</span><span class="o">!.</span><span class="n">author</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">post</span> <span class="o">-&gt;</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">EditPostMutation</span><span class="o">.</span><span class="kt">Data</span><span class="o">.</span><span class="kt">EditPost</span><span class="p">,</span> <span class="kt">GraphQLError</span><span class="o">&gt;</span> <span class="k">in</span>
        <span class="k">let</span> <span class="nv">updatedTags</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">tags</span> <span class="o">+</span> <span class="p">[</span><span class="o">.</span><span class="n">vapor</span><span class="p">,</span> <span class="o">.</span><span class="n">graphQl</span><span class="p">]</span>
        <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="nf">editPost</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">post</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="nv">title</span><span class="p">:</span> <span class="s">"Updated Title"</span><span class="p">,</span> <span class="nv">tags</span><span class="p">:</span> <span class="n">updatedTags</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">post</span> <span class="k">in</span>
        <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="nf">deletePost</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">post</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="o">.</span><span class="nf">sink</span><span class="p">(</span>
        <span class="nv">receiveCompletion</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">},</span>
        <span class="nv">receiveValue</span><span class="p">:</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">)</span>
</code></pre></div></div>

<p>In this snippet, we first call <code class="language-plaintext highlighter-rouge">fetchPosts</code> to fetch the list of posts and then using <code class="language-plaintext highlighter-rouge">flatMap</code> we pass the response and trigger the next request. Finally, we call <code class="language-plaintext highlighter-rouge">sink</code>. As you can see from the snippet, <code class="language-plaintext highlighter-rouge">sink</code> takes two parameters. The first one (<code class="language-plaintext highlighter-rouge">receiveComplete</code>) is a closure that gets executed on completion, be it a success or an error, while the second one (<code class="language-plaintext highlighter-rouge">receiveValue</code>) is a closure that gets executed every time we receive a new value from the publisher.</p>

<p>Since <code class="language-plaintext highlighter-rouge">Future</code> performs operations asynchronously, we need to keep a reference to the cancellable that the call to <code class="language-plaintext highlighter-rouge">sink</code> returns. Otherwise, Swift will destroy it by the time it exits the scope, and thus the closures will never get called.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// class scope</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">cancellable</span><span class="p">:</span> <span class="kt">AnyCancellable</span><span class="p">?</span>
</code></pre></div></div>

<p>Finally, make sure to call <code class="language-plaintext highlighter-rouge">cancellable?.cancel()</code> on the <code class="language-plaintext highlighter-rouge">deinit</code>.</p>

<p>And that was it! If you now run the app, it will execute those queries in order. If you want to “see” the flow of events, you can use the <code class="language-plaintext highlighter-rouge">.print()</code> function between the <code class="language-plaintext highlighter-rouge">.flatMap</code> calls and it will print the events.</p>

<blockquote>
  <p>All the code from this post is also available on <a href="https://github.com/diamantidis/ios-graphql/tree/94d390a">GitHub</a>.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>To sum up, in this post, we have made a brief introduction to Combine’s <code class="language-plaintext highlighter-rouge">Future</code> and <code class="language-plaintext highlighter-rouge">Promise</code> and we have seen how we can use them to perform GraphQL mutations. More specifically, we have seen how to implement the logic to sequentially create, edit, and delete a post.</p>

<p>Thanks for reading, I hope you find this post useful.</p>

<p>If you like this post and you want to get notified when a new post is published, you can follow me on <a href="https://x.com/diamantidis_io">X</a> or subscribe to the <a href="https://diamantidis.github.io/feed.xml">RSS feed</a>.</p>

<p>Also, if you have any questions or comments about this post, feel free to contact me on <a href="https://x.com/diamantidis_io">X</a>!</p>

<p>Until next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Swift" /><category term="GraphQL" /><category term="iOS" /><summary type="html"><![CDATA[A guide on how to perform GraphQL mutations on an iOS app built with Swift using Combine's Future and Promises]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/graphql-mutations-for-ios-apps.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/graphql-mutations-for-ios-apps.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Mutations on a GraphQL server built with Vapor</title><link href="https://diamantidis.github.io/2020/06/07/mutations-on-a-graphql-server-built-with-vapor" rel="alternate" type="text/html" title="Mutations on a GraphQL server built with Vapor" /><published>2020-06-07T04:00:00+00:00</published><updated>2020-06-07T04:00:00+00:00</updated><id>https://diamantidis.github.io/2020/06/07/mutations-on-a-graphql-server-built-with-vapor</id><content type="html" xml:base="https://diamantidis.github.io/2020/06/07/mutations-on-a-graphql-server-built-with-vapor"><![CDATA[<p>One characteristic of a complete API is its ability to allow clients to modify the server-side data. This can be in the form of creating new entries, updating or deleting existing ones, etc. Building a GraphQL server couldn’t be any different.</p>

<p>To describe this kind of features, GraphQL uses the term <code class="language-plaintext highlighter-rouge">Mutations</code>. Mutations are just like normal queries. The only difference is that they make it explicit that they will result in some kind of side-effect on the server-side data.</p>

<p>And that’s where this post will focus on. I am going to describe how to add the mutations to create, edit, and delete an entry on a GraphQL server built with Vapor.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>The code in this post is a continuation of the code from my two latest posts. In the first one, I described <a href="/2020/05/24/swift-loves-graphql-server-with-vapor-and-ios-app-client">how to setup a GraphQL server with Vapor</a> and in the second I focused on <a href="/2020/05/31/custom-graphql-types-on-swift-projects">how to use custom types on this GraphQL server</a>.</p>

<p>If you want to go through the code as you read the post, the code from the previous posts as well as the code from this post is available on <a href="https://github.com/diamantidis/vapor-graphql/tree/3db78ea">GitHub</a>.</p>

<p>Now, let’s get started!</p>

<h2 id="implementation">Implementation</h2>

<h3 id="prepare-the-models">Prepare the models</h3>

<p>First, and before we jump into the mutations, we need to adjust our existing models to support the mutations.</p>

<p>For example, when it comes to the edit mutation, we will have to update some properties of the <code class="language-plaintext highlighter-rouge">Post</code> model from constant to variable properties.</p>
<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code> struct Post: Codable {
     let id: CustomUUID
<span class="gd">-    let title: String
</span><span class="gi">+    var title: String
</span>     let publishedAt: Date
<span class="gd">-    let tags: [Tag]
</span><span class="gi">+    var tags: [Tag]
</span>     let author: Author
 }
</code></pre></div></div>

<p>Similarly, in order to be able to filter the posts by the <code class="language-plaintext highlighter-rouge">id</code> property, we will have to make <code class="language-plaintext highlighter-rouge">CustomUUID</code> conform to the <code class="language-plaintext highlighter-rouge">Equatable</code> protocol.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">-struct CustomUUID: Codable {
</span><span class="gi">+struct CustomUUID: Codable, Equatable {
</span></code></pre></div></div>

<p>With our models ready, we can now move on and start adding the logic for the mutations!</p>

<h2 id="delete-mutation">Delete mutation</h2>

<p>Let’s start with the logic for the <code class="language-plaintext highlighter-rouge">deletePost</code> mutation. For this mutation, we will expect the <code class="language-plaintext highlighter-rouge">id</code> of the <code class="language-plaintext highlighter-rouge">Post</code> to delete as an argument. Then, using this <code class="language-plaintext highlighter-rouge">id</code>, we will search for the <code class="language-plaintext highlighter-rouge">Post</code> in the in-memory list. If we manage to find the post, we are going to remove it and return <code class="language-plaintext highlighter-rouge">true</code> to the client. Otherwise, we will return <code class="language-plaintext highlighter-rouge">false</code> to let the client know that we didn’t manage to find the <code class="language-plaintext highlighter-rouge">Post</code>.</p>

<blockquote>
  <p>For the sake of this post, I am using a <code class="language-plaintext highlighter-rouge">Bool</code> as a return type. Ideally, we should return an error Type, but to keep this post focused on the mutations, I decided to go with the <code class="language-plaintext highlighter-rouge">Bool</code>.</p>
</blockquote>

<p>So, let’s add an extension to the <code class="language-plaintext highlighter-rouge">PostController</code> with this logic.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">PostController</span> <span class="p">{</span>
    <span class="kd">struct</span> <span class="kt">DeletePostArguments</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">deletePost</span><span class="p">(</span><span class="nv">request</span><span class="p">:</span> <span class="kt">Request</span><span class="p">,</span> <span class="nv">arguments</span><span class="p">:</span> <span class="kt">DeletePostArguments</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">postIndex</span> <span class="o">=</span> <span class="n">posts</span><span class="o">.</span><span class="n">firstIndex</span><span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">arguments</span><span class="o">.</span><span class="n">id</span> <span class="p">}</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">index</span> <span class="o">=</span> <span class="n">postIndex</span><span class="p">?</span><span class="o">.</span><span class="n">indexValue</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">false</span>
        <span class="p">}</span>
        <span class="n">posts</span><span class="o">.</span><span class="nf">remove</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">index</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">true</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Similarly, let’s add the logic for the <code class="language-plaintext highlighter-rouge">editPost</code> mutation!</p>

<h2 id="edit-mutation">Edit mutation</h2>

<p>The edit mutation will allow the user to change the value of the title and the tags for a given post. As a result, this time, we are going to require three arguments; an <code class="language-plaintext highlighter-rouge">id</code>, which we will use to find the <code class="language-plaintext highlighter-rouge">Post</code> to edit, as well as a <code class="language-plaintext highlighter-rouge">title</code> and a <code class="language-plaintext highlighter-rouge">tags</code> argument, which will contain the updated values for the <code class="language-plaintext highlighter-rouge">title</code> and <code class="language-plaintext highlighter-rouge">tags</code> properties respectively.</p>

<p>To keep the logic visually separated, let’s add a new extension to <code class="language-plaintext highlighter-rouge">PostController</code> for the logic related to the edit mutation. This extension will contain a new structure named <code class="language-plaintext highlighter-rouge">EditPostArguments</code> and a function <code class="language-plaintext highlighter-rouge">editPost</code> which will be responsible for editing a post.</p>

<p>The implementation will look like the following snippet:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">PostController</span> <span class="p">{</span>
    <span class="kd">struct</span> <span class="kt">EditPostArguments</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span>
        <span class="k">let</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span>
        <span class="k">let</span> <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="kt">Tag</span><span class="p">]</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">editPost</span><span class="p">(</span><span class="nv">request</span><span class="p">:</span> <span class="kt">Request</span><span class="p">,</span> <span class="nv">arguments</span><span class="p">:</span> <span class="kt">EditPostArguments</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Post</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">postIndex</span> <span class="o">=</span> <span class="n">posts</span><span class="o">.</span><span class="n">firstIndex</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">arguments</span><span class="o">.</span><span class="n">id</span> <span class="p">}</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">index</span> <span class="o">=</span> <span class="n">postIndex</span><span class="p">?</span><span class="o">.</span><span class="n">indexValue</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span>
        <span class="p">}</span>

        <span class="n">posts</span><span class="p">[</span><span class="n">index</span><span class="p">]</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">arguments</span><span class="o">.</span><span class="n">title</span>
        <span class="n">posts</span><span class="p">[</span><span class="n">index</span><span class="p">]</span><span class="o">.</span><span class="n">tags</span> <span class="o">=</span> <span class="n">arguments</span><span class="o">.</span><span class="n">tags</span>
        <span class="k">return</span> <span class="n">posts</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">EditPostArguments</code> structure has the three properties that we mentioned before. Then, the <code class="language-plaintext highlighter-rouge">editPost</code> function will accept those arguments as a parameter and will use the <code class="language-plaintext highlighter-rouge">id</code> to search for the <code class="language-plaintext highlighter-rouge">Post</code>. If we can’t find it, we will return <code class="language-plaintext highlighter-rouge">nil</code> to the client. If we manage to find it, we will update the properties of the <code class="language-plaintext highlighter-rouge">Post</code> with the values on the arguments and return the updated post to the client.</p>

<p>Last but not least, let’s see how we can add the functionality to create a new post entity!</p>

<h2 id="create-mutation">Create mutation</h2>

<p>To create a new <code class="language-plaintext highlighter-rouge">Post</code> we would need a title, a set of tags, and the id of the author.</p>

<p>It’s time to introduce a new GraphQL concept, the inputs. GraphQL distinguishes the types that can be used as input from those that can be used as outputs to queries. For example, in <a href="/2020/05/31/custom-graphql-types-on-swift-projects">a previous post</a>, I have used the type <code class="language-plaintext highlighter-rouge">Author</code> to return the author’s data. This is a typical example of an Output type. This kind of types, though, cannot be used when we want to pass arguments, be it in a query or a mutation. In order to define complex types for arguments, there is the concept of <code class="language-plaintext highlighter-rouge">Input</code>. <code class="language-plaintext highlighter-rouge">Input</code> is just like a type, with the only difference being the purpose of use and that it can only include scalar, enums, strings, int, float, bool, and other input types. We can not use an <code class="language-plaintext highlighter-rouge">Output</code> type as a field on an <code class="language-plaintext highlighter-rouge">Input</code> type.</p>

<p>So, let’s create a <code class="language-plaintext highlighter-rouge">PostInput</code> type and add the properties that we need to create a new <code class="language-plaintext highlighter-rouge">Post</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">PostInput</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">let</span> <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="kt">Tag</span><span class="p">]</span>
    <span class="k">let</span> <span class="nv">authorId</span><span class="p">:</span> <span class="kt">CustomUUID</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, we can define the function to create a new <code class="language-plaintext highlighter-rouge">Post</code> on an extension of the <code class="language-plaintext highlighter-rouge">PostController</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">PostController</span> <span class="p">{</span>
    <span class="kd">struct</span> <span class="kt">CreatePostArguments</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">input</span><span class="p">:</span> <span class="kt">PostInput</span>
    <span class="p">}</span>
    <span class="kd">func</span> <span class="nf">createPost</span><span class="p">(</span><span class="nv">request</span><span class="p">:</span> <span class="kt">Request</span><span class="p">,</span> <span class="nv">arguments</span><span class="p">:</span> <span class="kt">CreatePostArguments</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Post</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="n">author</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">arguments</span><span class="o">.</span><span class="n">input</span><span class="o">.</span><span class="n">authorId</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span>
        <span class="p">}</span>
        <span class="k">let</span> <span class="nv">post</span> <span class="o">=</span> <span class="kt">Post</span><span class="p">(</span>
            <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">UUID</span><span class="p">()),</span>
            <span class="nv">title</span><span class="p">:</span> <span class="n">arguments</span><span class="o">.</span><span class="n">input</span><span class="o">.</span><span class="n">title</span><span class="p">,</span>
            <span class="nv">publishedAt</span><span class="p">:</span> <span class="kt">Date</span><span class="p">(),</span>
            <span class="nv">tags</span><span class="p">:</span> <span class="n">arguments</span><span class="o">.</span><span class="n">input</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span>
            <span class="nv">author</span><span class="p">:</span> <span class="n">author</span>
        <span class="p">)</span>
        <span class="n">posts</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">post</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">post</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the same way as for the other functions, we define a structure for the type of the arguments. This structure contains a sole field of the type <code class="language-plaintext highlighter-rouge">PostInput</code> that we defined earlier. Then the function <code class="language-plaintext highlighter-rouge">createPost</code> takes an instance of this <code class="language-plaintext highlighter-rouge">CreatePostArguments</code> structure and uses it to create a new <code class="language-plaintext highlighter-rouge">Post</code> entity, which we later append to the list of existing posts.</p>

<p>And that’s about it for the logic part of our mutations. Now, it’s time to integrate them into the GraphQL server.</p>

<h2 id="graphql">GraphQL</h2>

<p>To make those functions and arguments available on the GraphQL schema, we will have to update the <code class="language-plaintext highlighter-rouge">FieldKeyProvider</code> extension of the <code class="language-plaintext highlighter-rouge">PostController</code> and add the keys for them. We will use those keys to map the function and the arguments of <code class="language-plaintext highlighter-rouge">PostController</code> to the mutations and the arguments of the GraphQL schema.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    enum FieldKeys: String {
<span class="gi">+       case id
+       case title
+       case tags
+       case input
+
</span>        case posts
<span class="gi">+       case deletePost
+       case editPost
+       case createPost
</span>    }
</code></pre></div></div>

<p>We will also provide conformance to the <code class="language-plaintext highlighter-rouge">FieldKeyProvider</code> protocol and define keys for the <code class="language-plaintext highlighter-rouge">PostInput</code> structure.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">PostInput</span><span class="p">:</span> <span class="kt">FieldKeyProvider</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">FieldKey</span> <span class="o">=</span> <span class="kt">FieldKeys</span>

    <span class="kd">enum</span> <span class="kt">FieldKeys</span> <span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">title</span>
        <span class="k">case</span> <span class="n">tags</span>
        <span class="k">case</span> <span class="n">authorId</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Lastly, we will update the GraphQL schema definition on <code class="language-plaintext highlighter-rouge">Schema.swift</code> by adding the definition for the <code class="language-plaintext highlighter-rouge">PostInput</code> input type and the definitions for the mutations using the keys we defined on the <code class="language-plaintext highlighter-rouge">FieldKeyProvider</code> extensions.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        Query([
            Field(.posts, at: PostController.fetchPosts),
        ]),
<span class="gi">+
+       Input(PostInput.self, [
+           InputField(.title, at: \.title),
+           InputField(.tags, at: \.tags),
+           InputField(.authorId, at: \.authorId)
+       ]),
+
+       Mutation([
+           Field(.deletePost, at: PostController.deletePost)
+               .argument(.id, at: \.id),
+
+           Field(.editPost, at: PostController.editPost)
+               .argument(.id, at: \.id)
+               .argument(.title, at: \.title)
+               .argument(.tags, at: \.tags),
+
+           Field(.createPost, at: PostController.createPost)
+               .argument(.input, at: \.input)
+       ])
+   ])
</span></code></pre></div></div>
<p>And that’s all folks! We can now build and run the vapor server!</p>

<h2 id="how-to-test">How to test?</h2>

<p>To verify what we have done so far, we are going to use a tool named <a href="https://github.com/prisma-labs/graphql-playground">GraphQL Playground</a>.</p>

<p>The installation process is quite simple, you just have to run <code class="language-plaintext highlighter-rouge">brew cask install graphql-playground</code>. Then, you can use the  Spotlight Search (<code class="language-plaintext highlighter-rouge">⌘</code> + <code class="language-plaintext highlighter-rouge">Space bar</code>) and open the application <code class="language-plaintext highlighter-rouge">GraphQL Playground</code>.</p>

<p>Once GraphQL Playground is running, we could run the following query to fetch the available posts:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">query</span> <span class="nx">AllPosts</span> <span class="p">{</span>
  <span class="nx">posts</span> <span class="p">{</span>
    <span class="nx">id</span>
    <span class="nx">author</span> <span class="p">{</span>
        <span class="nx">id</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>From the response, we are going to keep the id of the author and we will use it to create a new post with the following query (replace <code class="language-plaintext highlighter-rouge">00000000-0000-0000-0000-000000000000</code> with the UUID from the response):</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">mutation</span> <span class="nx">CreatePost</span> <span class="p">{</span>
  <span class="nx">createPost</span><span class="p">(</span><span class="nx">input</span><span class="p">:</span> <span class="p">{</span>
    <span class="nl">authorId</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">value</span><span class="p">:</span> <span class="dl">"</span><span class="s2">00000000-0000-0000-0000-000000000000</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="nx">tags</span><span class="p">:</span> <span class="p">[</span><span class="nx">Swift</span><span class="p">,</span> <span class="nx">Vapor</span><span class="p">]</span>
    <span class="nx">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">A new post</span><span class="dl">"</span>
  <span class="p">})</span> <span class="p">{</span>
    <span class="nx">id</span>
    <span class="nx">title</span>
    <span class="nx">publishedAt</span>
    <span class="nx">tags</span>
    <span class="nx">author</span> <span class="p">{</span>
      <span class="nx">id</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This time, keep the id of the post from the response, and use it on the next query to edit the title and the tags of the post:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">mutation</span> <span class="nx">EditPost</span> <span class="p">{</span>
  <span class="nx">editPost</span><span class="p">(</span>
    <span class="nx">id</span><span class="p">:</span> <span class="p">{</span>
      <span class="nl">value</span><span class="p">:</span> <span class="dl">"</span><span class="s2">00000000-0000-0000-0000-000000000000</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="nx">tags</span><span class="p">:</span> <span class="p">[</span><span class="nx">Swift</span><span class="p">,</span> <span class="nx">Vapor</span><span class="p">,</span> <span class="nx">GraphQL</span><span class="p">],</span> 
    <span class="nx">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">A new post with an updated title</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">id</span>
    <span class="nx">title</span>
    <span class="nx">publishedAt</span>
    <span class="nx">tags</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Finally, we can delete the post that we have created using the id of the post from the previous query on the following query:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">mutation</span> <span class="nx">DeletePost</span> <span class="p">{</span>
  <span class="nx">deletePost</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="p">{</span>
    <span class="nl">value</span><span class="p">:</span> <span class="dl">"</span><span class="s2">00000000-0000-0000-0000-000000000000</span><span class="dl">"</span>
  <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>And that’s about it! In this post, we have seen how to add mutations to create, edit and delete a <code class="language-plaintext highlighter-rouge">Post</code> on a GraphQL server built with Vapor. We have also seen how to take advantage of GraphQL’s Inputs for arguments with complex types and how to use <a href="https://github.com/prisma-labs/graphql-playground">GraphQL Playground</a> to run our GraphQL queries.</p>

<p>In the next post, I am going to continue this GraphQL &amp; Swift journey and I am going to investigate how to use the mutations that we defined in this post from an iOS app. 
So stay tuned and follow me on <a href="https://x.com/diamantidis_io">X</a> should you want to get notified once the next post is published or you have a question or comment about this post.</p>

<p>Thanks for reading this post, and see you next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Swift" /><category term="Vapor" /><category term="GraphQL" /><summary type="html"><![CDATA[A post describing how to add mutations to create, edit and delete entries on a GraphQL server built with Vapor]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/mutations-on-a-graphql-server-built-with-vapor.png" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/mutations-on-a-graphql-server-built-with-vapor.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Custom GraphQL types on Swift projects</title><link href="https://diamantidis.github.io/2020/05/31/custom-graphql-types-on-swift-projects" rel="alternate" type="text/html" title="Custom GraphQL types on Swift projects" /><published>2020-05-31T04:00:00+00:00</published><updated>2020-05-31T04:00:00+00:00</updated><id>https://diamantidis.github.io/2020/05/31/custom-graphql-types-on-swift-projects</id><content type="html" xml:base="https://diamantidis.github.io/2020/05/31/custom-graphql-types-on-swift-projects"><![CDATA[<p>By default, <a href="https://graphql.org/">GraphQL</a> supports only a handful of basic types that we can use on the schema definition. This list includes <code class="language-plaintext highlighter-rouge">Int</code>, <code class="language-plaintext highlighter-rouge">Float</code>, <code class="language-plaintext highlighter-rouge">String</code>, <code class="language-plaintext highlighter-rouge">Boolean</code> and <code class="language-plaintext highlighter-rouge">ID</code>. But as you can easily understand relying solely on these types is quite restrictive. What if we want to add a date field? Or some other kind of data?</p>

<p>Hopefully for us, GraphQL offers plenty of flexibility when it comes to constructing a model with custom types; be it a custom object type, a custom scalar type, enums or a list to name a few.</p>

<p>And that’s what we are going to investigate in this post. We will see how we can use all these custom types on Swift projects.</p>

<h2 id="before-we-start">Before we start</h2>

<p>In a <a href="/2020/05/24/swift-loves-graphql-server-with-vapor-and-ios-app-client">previous post</a>, I described how to create a simple GraphQL server in Swift using <a href="https://github.com/vapor/vapor">Vapor</a> and an iOS app to fetch information from this server using the <a href="https://www.apollographql.com/docs/ios/">Apollo iOS</a> client. For that post, I used a simple <code class="language-plaintext highlighter-rouge">Post</code> type with an <code class="language-plaintext highlighter-rouge">id</code> and a <code class="language-plaintext highlighter-rouge">title</code> property.</p>

<p>This time, we are going one step further and we will enrich the <code class="language-plaintext highlighter-rouge">Post</code> type by adding some more properties. We are going to introduce a custom scalar type that we will use for the id of the model, a Date field for the date that the post was published, a list of enums for the tags and a custom object for the author of the post.</p>

<p>As a side note before we start, the code in this post is a continuation of the code from the <a href="/2020/05/24/swift-loves-graphql-server-with-vapor-and-ios-app-client">previous post</a> that I mentioned earlier. If you want to go through the code as you read this post, you can find on Github a branch on the version of the project at the end of the previous post (<a href="https://github.com/diamantidis/vapor-graphql/tree/b3afc1f">server</a> &amp; <a href="https://github.com/diamantidis/ios-graphql/tree/4bc583a">client</a>) and a branch with the version of the code at the end of this post (<a href="https://github.com/diamantidis/vapor-graphql/tree/f414bb1">server</a> &amp; <a href="https://github.com/diamantidis/ios-graphql/tree/c95ebe4">client</a>). If you want to learn more about the setup and how to run those projects, either refer to my <a href="/2020/05/24/swift-loves-graphql-server-with-vapor-and-ios-app-client">previous post</a> or to the README file of each project.</p>

<p>Following the same pattern as in the previous post, let’s start with the updates on the server side and then we will update the iOS app.</p>

<h2 id="server">Server</h2>

<p>To begin with, let’s introduce three new types. The first one will be a new structure named <code class="language-plaintext highlighter-rouge">CustomUUID</code> that we will as a type for the <code class="language-plaintext highlighter-rouge">id</code> property. The second one will be an <code class="language-plaintext highlighter-rouge">enum</code> named <code class="language-plaintext highlighter-rouge">Tag</code> that we are going to use for the type of the list of tags and lastly a structure name <code class="language-plaintext highlighter-rouge">Author</code> to represent the author of the post.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// CustomUUID.swift</span>
<span class="kd">struct</span> <span class="kt">CustomUUID</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">UUID</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Tag.swift</span>
<span class="kd">enum</span> <span class="kt">Tag</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="kt">Codable</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">swift</span> <span class="o">=</span> <span class="s">"Swift"</span>
    <span class="k">case</span> <span class="n">vapor</span> <span class="o">=</span> <span class="s">"Vapor"</span>
    <span class="k">case</span> <span class="n">graphql</span> <span class="o">=</span> <span class="s">"GraphQL"</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Author.swift</span>
<span class="kd">struct</span> <span class="kt">Author</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span>
    <span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">let</span> <span class="nv">twitter</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Once all those types are added, we can update our <code class="language-plaintext highlighter-rouge">Post</code> model to look like the following snippet:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Post.swift</span>
<span class="kd">struct</span> <span class="kt">Post</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span>
    <span class="k">let</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">let</span> <span class="nv">publishedAt</span><span class="p">:</span> <span class="kt">Date</span>
    <span class="k">let</span> <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="kt">Tag</span><span class="p">]</span>
    <span class="k">let</span> <span class="nv">author</span><span class="p">:</span> <span class="kt">Author</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After that, we will update the <code class="language-plaintext highlighter-rouge">FieldKeyProvider</code> extension for the <code class="language-plaintext highlighter-rouge">Post</code> and add a new key for each new property. We will later use those keys on the schema definition to map the fields of the GraphQL schema to the properties of the <code class="language-plaintext highlighter-rouge">Post</code> structure. The final version of the <code class="language-plaintext highlighter-rouge">Post</code> extension will look like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// GraphQL+FieldKeyProvider.swift</span>
<span class="kd">extension</span> <span class="kt">Post</span><span class="p">:</span> <span class="kt">FieldKeyProvider</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">FieldKey</span> <span class="o">=</span> <span class="kt">FieldKeys</span>

    <span class="kd">enum</span> <span class="kt">FieldKeys</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">id</span>
        <span class="k">case</span> <span class="n">title</span>
        <span class="k">case</span> <span class="n">publishedAt</span>
        <span class="k">case</span> <span class="n">tags</span>
        <span class="k">case</span> <span class="n">author</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we have add a new structure, the <code class="language-plaintext highlighter-rouge">Author</code>, we will also add an extension for this structure to add conformance to the <code class="language-plaintext highlighter-rouge">FieldKeyProvider</code> protocol. The extension will be like in the following snippet:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// GraphQL+FieldKeyProvider.swift</span>
<span class="kd">extension</span> <span class="kt">Author</span><span class="p">:</span> <span class="kt">FieldKeyProvider</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">FieldKey</span> <span class="o">=</span> <span class="kt">FieldKeys</span>

    <span class="kd">enum</span> <span class="kt">FieldKeys</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">id</span>
        <span class="k">case</span> <span class="n">name</span>
        <span class="k">case</span> <span class="n">twitter</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, we can move on and update the definition of the GraphQL schema:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Schema.swift</span>
<span class="kd">enum</span> <span class="kt">Schemas</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">postSchema</span> <span class="o">=</span> <span class="kt">Schema</span><span class="o">&lt;</span><span class="kt">PostController</span><span class="p">,</span> <span class="kt">Request</span><span class="o">&gt;</span><span class="p">([</span>
        <span class="kt">Enum</span><span class="p">(</span><span class="kt">Tag</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="p">[</span>
            <span class="kt">Value</span><span class="p">(</span><span class="o">.</span><span class="n">swift</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">description</span><span class="p">(</span><span class="s">"About Swift"</span><span class="p">),</span>
            <span class="kt">Value</span><span class="p">(</span><span class="o">.</span><span class="n">vapor</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">description</span><span class="p">(</span><span class="s">"About Vapor"</span><span class="p">),</span>
            <span class="kt">Value</span><span class="p">(</span><span class="o">.</span><span class="n">graphql</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">description</span><span class="p">(</span><span class="s">"About GraphQL"</span><span class="p">),</span>
        <span class="p">])</span>
            <span class="o">.</span><span class="nf">description</span><span class="p">(</span><span class="s">"Tags"</span><span class="p">),</span>

        <span class="kt">Scalar</span><span class="p">(</span><span class="kt">CustomUUID</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
            <span class="o">.</span><span class="nf">description</span><span class="p">(</span><span class="s">"My custom UUID"</span><span class="p">),</span>

        <span class="kt">Scalar</span><span class="p">(</span><span class="kt">Date</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
            <span class="o">.</span><span class="nf">description</span><span class="p">(</span><span class="s">"Date Type"</span><span class="p">),</span>

        <span class="kt">Type</span><span class="p">(</span><span class="kt">Author</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">fields</span><span class="p">:</span> <span class="p">[</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">id</span><span class="p">),</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">name</span><span class="p">),</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">twitter</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">twitter</span><span class="p">)</span>
        <span class="p">]),</span>

        <span class="kt">Type</span><span class="p">(</span><span class="kt">Post</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">fields</span><span class="p">:</span> <span class="p">[</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">id</span><span class="p">),</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">title</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">title</span><span class="p">),</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">publishedAt</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">publishedAt</span><span class="p">),</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">tags</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">tags</span><span class="p">),</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">author</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">author</span><span class="p">),</span>
        <span class="p">]),</span>

        <span class="kt">Query</span><span class="p">([</span>
            <span class="kt">Field</span><span class="p">(</span><span class="o">.</span><span class="n">posts</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="kt">PostController</span><span class="o">.</span><span class="n">fetchPosts</span><span class="p">),</span>
        <span class="p">]),</span>
    <span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this snippet, we have added the definition of the enum and all of its cases, the definition of the <code class="language-plaintext highlighter-rouge">CustomUUID</code> and Swift’s <code class="language-plaintext highlighter-rouge">Date</code>, a definition of the new <code class="language-plaintext highlighter-rouge">Author</code> type and updated the definition of the <code class="language-plaintext highlighter-rouge">Post</code> type to include the new fields.</p>

<blockquote>
  <p><strong>WARNING</strong>: The order of the definitions in the schema does matter. If we were to place the definition of the <code class="language-plaintext highlighter-rouge">CustomUUID</code> after the definition of the <code class="language-plaintext highlighter-rouge">Post</code>, we would get an exception saying <code class="language-plaintext highlighter-rouge">Fatal error: 'try!' expression unexpectedly raised an error: Cannot use type "CustomUUID" for field "id". Type does not map to a GraphQL type.</code>.</p>
</blockquote>

<p>Lastly, we are going to update the data that the GraphQL server will return to the client and add values for the new fields:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// PostController.swift</span>
<span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">author</span> <span class="o">=</span> <span class="kt">Author</span><span class="p">(</span>
    <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">UUID</span><span class="p">()),</span>
    <span class="nv">name</span><span class="p">:</span> <span class="s">"Ioannis Diamantidis"</span><span class="p">,</span>
    <span class="nv">twitter</span><span class="p">:</span> <span class="s">"@diamantidis_io"</span>
<span class="p">)</span>

<span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">posts</span> <span class="o">=</span> <span class="p">[</span>
    <span class="kt">Post</span><span class="p">(</span>
        <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">UUID</span><span class="p">()),</span>
        <span class="nv">title</span><span class="p">:</span> <span class="s">"My first post"</span><span class="p">,</span>
        <span class="nv">publishedAt</span><span class="p">:</span> <span class="kt">Date</span><span class="p">(),</span>
        <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="n">swift</span><span class="p">,</span> <span class="o">.</span><span class="n">graphql</span><span class="p">,</span> <span class="o">.</span><span class="n">vapor</span><span class="p">],</span>
        <span class="nv">author</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">author</span>
    <span class="p">)</span>
<span class="p">]</span>
</code></pre></div></div>

<blockquote>
  <p><strong>NOTE:</strong> I could have used <code class="language-plaintext highlighter-rouge">UUID</code> directly instead of the <code class="language-plaintext highlighter-rouge">CustomUUID</code>, but for the sake of demonstration, I preferred to use a custom container structure. If you want to use the <code class="language-plaintext highlighter-rouge">UUID</code>, you can follow the same logic as with the <code class="language-plaintext highlighter-rouge">Date</code> type.</p>
</blockquote>

<p>And this is it for the server side! You can now build and run the project!</p>

<blockquote>
  <p>The code with all those changes is also available on <a href="https://github.com/diamantidis/vapor-graphql/tree/f414bb1">GitHub</a>.</p>
</blockquote>

<p>Let’s now jump on to the client side and the iOS app!</p>

<h2 id="ios">iOS</h2>

<p>First and foremost, we will get the updated version of the GraphQL schema. With the server running, run the following command from the root directory of the iOS project to update the <code class="language-plaintext highlighter-rouge">schema.json</code>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apollo schema:download <span class="nt">--endpoint</span><span class="o">=</span>http://127.0.0.1:8080/graphql iOSGraphQL/GraphQL/schema.json
</code></pre></div></div>

<p>Once this is done, let’s open Xcode and update the query file(<code class="language-plaintext highlighter-rouge">AllPosts.graphql</code>) to add the new fields. We are going to add the <code class="language-plaintext highlighter-rouge">tags</code> and the <code class="language-plaintext highlighter-rouge">publishedAt</code> in the same way as we added the <code class="language-plaintext highlighter-rouge">id</code> and <code class="language-plaintext highlighter-rouge">title</code> fields, but for the <code class="language-plaintext highlighter-rouge">author</code> field, we are going to use the concept of <a href="https://graphql.org/learn/queries/#fragments">GraphQL’s fragments</a>. Fragments are reusable components that you can use to split the query definition in smaller chunks and use them in multiple queries.</p>

<p>In our case, we will define a fragment on the <code class="language-plaintext highlighter-rouge">Author</code> type and then use it on the <code class="language-plaintext highlighter-rouge">AllPosts</code> query to fetch the author of the post.</p>

<p>The final version of the query file will be like the following snippet:</p>

<div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fragment</span><span class="w"> </span><span class="n">AuthorDetails</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">Author</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">id</span><span class="w">
  </span><span class="n">name</span><span class="w">
  </span><span class="n">twitter</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="k">query</span><span class="w"> </span><span class="n">AllPosts</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">posts</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">id</span><span class="w">
    </span><span class="n">title</span><span class="w">
    </span><span class="n">publishedAt</span><span class="w">
    </span><span class="n">tags</span><span class="w">
    </span><span class="n">author</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="p">...</span><span class="n">AuthorDetails</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Now, let’s jump back to the Terminal and run the following command from the root directory to update <code class="language-plaintext highlighter-rouge">API.swift</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./Pods/Apollo/scripts/run-bundled-codegen.sh codegen:generate <span class="se">\</span>
    <span class="nt">--target</span><span class="o">=</span>swift <span class="se">\</span>
    <span class="s1">'--includes=./**/*.graphql'</span>  <span class="se">\</span>
    <span class="nt">--localSchemaFile</span><span class="o">=</span>./path/to/GraphQL/schema.json <span class="se">\</span>
    ./path/to/GraphQL/API.swift
</code></pre></div></div>

<p>If you build and run the app right now, you will get some errors like <code class="language-plaintext highlighter-rouge">Type of expression is ambiguous without more context</code> and <code class="language-plaintext highlighter-rouge">Use of unresolved identifier 'CustomUUID'</code>.</p>

<p>To fix those errors, we will have to add definition for the new structures that we introduced on the GraphQL schema.</p>

<p>Let’s start with the <code class="language-plaintext highlighter-rouge">Author</code> which will have the following definition:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Author.swift</span>
<span class="kd">struct</span> <span class="kt">Author</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span>
    <span class="k">var</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">var</span> <span class="nv">twitter</span><span class="p">:</span> <span class="kt">String</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">author</span><span class="p">:</span> <span class="kt">AuthorDetails</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">author</span><span class="o">.</span><span class="n">id</span>
        <span class="k">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">author</span><span class="o">.</span><span class="n">name</span>
        <span class="k">self</span><span class="o">.</span><span class="n">twitter</span> <span class="o">=</span> <span class="n">author</span><span class="o">.</span><span class="n">twitter</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this snippet, we map the properties of the structure <code class="language-plaintext highlighter-rouge">AuthorDetails</code> to the properties of our domain model. <code class="language-plaintext highlighter-rouge">AuthorDetails</code> is the structure that the <a href="https://www.apollographql.com/docs/ios/">Apollo iOS</a> client generated when we run the <code class="language-plaintext highlighter-rouge">run-bundled-codegen.sh</code> command.</p>

<p>Next, let’s add the definition for the <code class="language-plaintext highlighter-rouge">CustomUUID</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// CustomUUID.swift</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">CustomUUID</span><span class="p">:</span> <span class="kt">JSONDecodable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">UUID</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">jsonValue</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">JSONValue</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">stringValue</span> <span class="o">=</span> <span class="p">(</span><span class="n">value</span> <span class="k">as</span> <span class="kt">AnyObject</span><span class="p">)[</span><span class="s">"value"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">String</span><span class="p">,</span> <span class="k">let</span> <span class="nv">uuid</span> <span class="o">=</span> <span class="kt">UUID</span><span class="p">(</span><span class="nv">uuidString</span><span class="p">:</span> <span class="n">stringValue</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="kt">JSONDecodingError</span><span class="o">.</span><span class="nf">couldNotConvert</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="n">value</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="kt">CustomUUID</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">uuid</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here, we make <code class="language-plaintext highlighter-rouge">CustomUUID</code> conform to Apollo’s protocol <code class="language-plaintext highlighter-rouge">JSONDecodable</code> and fulfill the <code class="language-plaintext highlighter-rouge">init(jsonValue value: JSONValue)</code> requirement. 
<code class="language-plaintext highlighter-rouge">JSONDecodable</code> is a protocol that we use to add the logic about decoding the values of custom scalar types.</p>

<p>The same applies for the <code class="language-plaintext highlighter-rouge">publishedAt</code> field and its <code class="language-plaintext highlighter-rouge">Date</code> type, with the only difference being that this time we will add an extension to the Swift’s <code class="language-plaintext highlighter-rouge">Date</code> structure instead of adding a new one.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Date</span><span class="p">:</span> <span class="kt">JSONDecodable</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">jsonValue</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">JSONValue</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
        
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">timeInterval</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="kt">TimeInterval</span><span class="p">(</span><span class="nv">jsonValue</span><span class="p">:</span> <span class="n">value</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="kt">JSONDecodingError</span><span class="o">.</span><span class="nf">couldNotConvert</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="n">value</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="kt">Date</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">self</span> <span class="o">=</span> <span class="kt">Date</span><span class="p">(</span><span class="nv">timeIntervalSinceReferenceDate</span><span class="p">:</span> <span class="n">timeInterval</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Lastly, we will update the <code class="language-plaintext highlighter-rouge">Post</code> structure, where we will add the new fields and update the initializer to instantiate them.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Post.swift</span>
<span class="kd">struct</span> <span class="kt">Post</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">CustomUUID</span>
    <span class="k">let</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">let</span> <span class="nv">publishedAt</span><span class="p">:</span> <span class="kt">Date</span>
    <span class="k">let</span> <span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="kt">Tag</span><span class="p">]</span>
    <span class="k">var</span> <span class="nv">author</span><span class="p">:</span> <span class="kt">Author</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">post</span><span class="p">:</span> <span class="kt">AllPostsQuery</span><span class="o">.</span><span class="kt">Data</span><span class="o">.</span><span class="kt">Post</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">id</span>
        <span class="k">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">title</span>
        <span class="k">self</span><span class="o">.</span><span class="n">publishedAt</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">publishedAt</span>
        <span class="k">self</span><span class="o">.</span><span class="n">tags</span> <span class="o">=</span> <span class="n">post</span><span class="o">.</span><span class="n">tags</span>
        <span class="k">self</span><span class="o">.</span><span class="n">author</span> <span class="o">=</span> <span class="kt">Author</span><span class="p">(</span><span class="nv">author</span><span class="p">:</span> <span class="n">post</span><span class="o">.</span><span class="n">author</span><span class="o">.</span><span class="n">fragments</span><span class="o">.</span><span class="n">authorDetails</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we are ready to build and run the app. If you do so, you should be able to see the post object with all its properties on the console! :tada:</p>

<blockquote>
  <p>The code with all those changes is also available on <a href="https://github.com/diamantidis/ios-graphql/tree/c95ebe4">GitHub</a>.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>And that’s about it! In this post, we have seen how to use GraphQL’s features like custom scalar types, enums, lists and custom objects to enhance the <code class="language-plaintext highlighter-rouge">Post</code> model with fields like the <code class="language-plaintext highlighter-rouge">uuid</code>, the <code class="language-plaintext highlighter-rouge">publishedAt</code>, the <code class="language-plaintext highlighter-rouge">tags</code> and the <code class="language-plaintext highlighter-rouge">author</code>.</p>

<p>Knowing about those possibilities is really valuable when working with GraphQL and can make a huge difference when it comes to designing a GraphQL schema.</p>

<p>In the posts to come, we are going to see how further improve this project by adding support for sorting, filtering, creating a new post, editing an existing, deleting, etc. So stay tuned and follow me on <a href="https://x.com/diamantidis_io">X</a> should you want to get notified once these posts are published or you have a question or comment about this post.</p>

<p>Thanks for reading this post, and see you next time!</p>]]></content><author><name>Ioannis Diamantidis</name><email>diamantidis@outlook.com</email></author><category term="Swift" /><category term="Vapor" /><category term="GraphQL" /><category term="Apollo" /><category term="iOS" /><summary type="html"><![CDATA[A post about the use of GraphQL custom types like scalar, object, enums and list on Swift projects (Vapor on the server side project and an iOS app on the client side)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://diamantidis.github.io/assets/social/graphql-types-swift.jpg" /><media:content medium="image" url="https://diamantidis.github.io/assets/social/graphql-types-swift.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>