Jekyll2024-02-06T15:49:30+00:00https://kaitlin.dev/feed.xmlKaitlin Maharsoftware engineerFull-Stack Swift: Building an iOS App with a Vapor Backend2022-03-23T16:00:00+00:002022-03-23T16:00:00+00:00https://kaitlin.dev/2022/03/23/full-stack-swift<p>I recently <a href="https://twitter.com/k__mahar/status/1494118397491269636">revealed on Twitter</a> something that may have come as a surprise to many of my followers from the Swift/iOS community: I had never written an iOS app before! I’ve been writing Swift for a few years now but have focused entirely on <a href="https://www.youtube.com/watch?v=9-fdbG9jNt4">library development</a> and <a href="https://www.swift.org/server/">server-side Swift</a>.</p>
<p>A highly compelling feature of Swift is that it allows you to write an iOS app and a corresponding backend – a complete, end-to-end application – all in the same language. This is similar to how using <a href="https://nodejs.org/en/">Node.js</a> for a web app backend allows you to write Javascript everywhere.</p>
<p>To test this out and learn about iOS development, I decided to build a full-stack application entirely in Swift. I settled on a familiar CRUD app I’ve created a web version of before, an application that allows the user to manage a list of kittens and information about them.</p>
<p>I chose to build the app using the following components:</p>
<ul>
<li>A backend server, written using the popular Swift web framework <a href="https://vapor.codes/">Vapor</a> and using the <a href="https://github.com/mongodb/mongo-swift-driver">MongoDB Swift driver</a> via <a href="https://github.com/mongodb/mongodb-vapor">MongoDBVapor</a> to store data in MongoDB</li>
<li>An iOS application built with <a href="https://developer.apple.com/xcode/swiftui/">SwiftUI</a> and using <a href="https://github.com/mongodb/swift-bson">SwiftBSON</a> to support serializing/deserializing data to/from <a href="https://docs.mongodb.com/manual/reference/mongodb-extended-json/">extended JSON</a>, a version of JSON with MongoDB-specific extensions to simplify type preservation</li>
<li>A <a href="https://www.swift.org/package-manager/">SwiftPM</a> package containing the code I wanted to share between the two above components</li>
</ul>
<p>I was able to combine all of this into a single code base with a folder structure as follows:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FullStackSwiftExample/
├── Models/
│ ├── Package.swift
│ └── Sources/
│ └── Models/
│ └── Models.swift
├── Backend/
│ ├── Package.swift
│ └── Sources/
│ ├── App/
│ │ ├── configure.swift
│ │ └── routes.swift
│ └── Run/
│ └── main.swift
└── iOSApp/
└── Kittens/
├── KittensApp.swift
├── Utilities.swift
├── ViewModels/
│ ├── AddKittenViewModel.swift
│ ├── KittenListViewModel.swift
│ └── ViewUpdateDeleteKittenViewModel.swift
└── Views/
├── AddKitten.swift
├── KittenList.swift
└── ViewUpdateDeleteKitten.swift
</code></pre></div></div>
<p>Overall, it was a great learning experience for me, and although the app is pretty basic, I’m proud of what I was able to put together! <a href="https://github.com/mongodb/mongo-swift-driver/tree/main/Examples/FullStackSwiftExample">Here</a> is the finished application, instructions to run it, and documentation on each component.</p>
<p>In the rest of this post, I’ll discuss some of my takeaways from this experience.</p>
<h2 id="1-sharing-data-model-types-made-it-straightforward-to-consistently-represent-my-data-throughout-the-stack">1. Sharing data model types made it straightforward to consistently represent my data throughout the stack.</h2>
<p>As I mentioned above, I created a shared SwiftPM package for any code I wanted to use both in the frontend and backend of my application. In that package, I defined <code class="language-plaintext highlighter-rouge">Codable</code> types modeling the data in my application, for example:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
* Represents a kitten.
* This type conforms to `Codable` to allow us to serialize it to and deserialize it from extended JSON and BSON.
* This type conforms to `Identifiable` so that SwiftUI is able to uniquely identify instances of this type when they
* are used in the iOS interface.
*/</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Kitten</span><span class="p">:</span> <span class="kt">Identifiable</span><span class="p">,</span> <span class="kt">Codable</span> <span class="p">{</span>
<span class="c1">/// Unique identifier.</span>
<span class="kd">public</span> <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">BSONObjectID</span>
<span class="c1">/// Name.</span>
<span class="kd">public</span> <span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="c1">/// Fur color.</span>
<span class="kd">public</span> <span class="k">let</span> <span class="nv">color</span><span class="p">:</span> <span class="kt">String</span>
<span class="c1">/// Favorite food.</span>
<span class="kd">public</span> <span class="k">let</span> <span class="nv">favoriteFood</span><span class="p">:</span> <span class="kt">CatFood</span>
<span class="c1">/// Last updated time.</span>
<span class="kd">public</span> <span class="k">let</span> <span class="nv">lastUpdateTime</span><span class="p">:</span> <span class="kt">Date</span>
<span class="kd">private</span> <span class="kd">enum</span> <span class="kt">CodingKeys</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="kt">CodingKey</span> <span class="p">{</span>
<span class="c1">// We store the identifier under the name `id` on the struct to satisfy the requirements of the `Identifiable`</span>
<span class="c1">// protocol, which this type conforms to in order to allow usage with certain SwiftUI features. However,</span>
<span class="c1">// MongoDB uses the name `_id` for unique identifiers, so we need to use `_id` in the extended JSON</span>
<span class="c1">// representation of this type.</span>
<span class="k">case</span> <span class="n">id</span> <span class="o">=</span> <span class="s">"_id"</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">color</span><span class="p">,</span> <span class="n">favoriteFood</span><span class="p">,</span> <span class="n">lastUpdateTime</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>When you use separate code/programming languages to represent data on the frontend versus backend of an application, it’s easy for implementations to get out of sync. But in this application, since the same exact model type gets used for the frontend <strong>and</strong> backend representations of kittens, there can’t be any inconsistency.</p>
<p>Since this type conforms to the <code class="language-plaintext highlighter-rouge">Codable</code> protocol, we also get a single, consistent definition for a kitten’s representation in external data formats. The formats used in this application are:</p>
<ul>
<li><a href="https://docs.mongodb.com/manual/reference/mongodb-extended-json/">Extended JSON</a>, which the frontend and backend use to communicate via HTTP, and</li>
<li><a href="https://docs.mongodb.com/manual/reference/bson-types/">BSON</a>, which the backend and MongoDB use to communicate</li>
</ul>
<p>For a concrete example of using a model type throughout the stack, when a user adds a new kitten via the UI, the data flows through the application as follows:</p>
<ol>
<li>The iOS app creates a new <code class="language-plaintext highlighter-rouge">Kitten</code> instance containing the user-provided data</li>
<li>The <code class="language-plaintext highlighter-rouge">Kitten</code> instance is serialized to extended JSON via <code class="language-plaintext highlighter-rouge">ExtendedJSONEncoder</code> and sent in a POST request to the backend</li>
<li>The Vapor backend deserializes a new instance of <code class="language-plaintext highlighter-rouge">Kitten</code> from the extended JSON data using <code class="language-plaintext highlighter-rouge">ExtendedJSONDecoder</code></li>
<li>The <code class="language-plaintext highlighter-rouge">Kitten</code> is passed to the MongoDB driver method <code class="language-plaintext highlighter-rouge">MongoCollection<Kitten>.insertOne()</code></li>
<li>The MongoDB driver uses its built-in <code class="language-plaintext highlighter-rouge">BSONEncoder</code> to serialize the <code class="language-plaintext highlighter-rouge">Kitten</code> to BSON and send it via the MongoDB <a href="https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/">wire protocol</a> to the database</li>
</ol>
<p>With all these transformations, it can be tricky to ensure that both the frontend and backend remain in sync in terms of how they model, serialize, and deserialize data. Using Swift everywhere and sharing these <code class="language-plaintext highlighter-rouge">Codable</code> data types allowed me to avoid those problems altogether in this app.</p>
<h2 id="2-working-in-a-single-familiar-language-made-the-development-experience-seamless">2. Working in a single, familiar language made the development experience seamless.</h2>
<p>Despite having never built an iOS app before, I found my existing Swift experience made it surprisingly easy to pick up on the concepts I needed to implement the iOS portion of my application. I suspect it’s more common that someone would go in the opposite direction, but I think iOS experience would translate well to writing a Swift backend too!</p>
<p>I used several Swift language features such as <a href="https://docs.swift.org/swift-book/LanguageGuide/Protocols.html">protocols</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/Closures.html">trailing closures</a>, and <a href="https://docs.swift.org/swift-book/LanguageGuide/Properties.html">computed properties</a> in both the iOS and backend code. I was also able to take advantage of Swift’s new built-in features for <a href="https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html">concurrency</a> throughout the stack. I used the <code class="language-plaintext highlighter-rouge">async</code> APIs on <a href="https://developer.apple.com/documentation/foundation/urlsession"><code class="language-plaintext highlighter-rouge">URLSession</code></a> to send HTTP requests from the frontend, and I used Vapor and the MongoDB driver’s <code class="language-plaintext highlighter-rouge">async</code> APIs to handle requests on the backend. It was much easier to use a consistent model and syntax for concurrent, asynchronous programming throughout the application than to try to keep straight in my head the concurrency models for two different languages at once.</p>
<p>In general, using the same language really made it feel like I was building a single application rather than two distinct ones, and greatly reduced the amount of context-switching I had to do as I alternated between work on the frontend and backend.</p>
<h2 id="3-swiftui-and-ios-development-are-really-cool">3. SwiftUI and iOS development are really cool!</h2>
<p>Many of my past experiences trying to cobble together a frontend for school or personal projects using HTML and Javascript were frustrating. This time around, the combination of using my favorite programming language and an elegant, declarative framework made writing the frontend very enjoyable. More generally, it was great to finally learn a bit about iOS development and what most people writing Swift and that I know from the Swift community do!</p>
<hr />
<p>In conclusion, my first foray into iOS development building this full-stack Swift app was a lot of fun and a great learning experience. It strongly demonstrated to me the benefits of using a single language to build an entire application, and using a language you’re already familiar with as you venture into programming in a new domain.</p>
<p>I’ve included a list of references below, including a link to the example application. Please feel free to get in touch with any questions or suggestions regarding the application or the MongoDB libraries listed below – the best way to get in touch with me and my team is by filing a <a href="https://github.com/mongodb/mongo-swift-driver/issues/new/choose">GitHub issue</a> or <a href="http://jira.mongodb.org/browse/SWIFT">Jira ticket</a>!</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/mongodb/mongo-swift-driver/tree/main/Examples/FullStackSwiftExample">Example app source code</a></li>
<li><a href="https://github.com/mongodb/mongo-swift-driver">MongoDB Swift driver</a> and <a href="https://mongodb.github.io/mongo-swift-driver/docs/current/MongoSwift/index.html">documentation</a></li>
<li><a href="https://github.com/mongodb/mongodb-vapor">MongoDBVapor</a> and <a href="https://mongodb.github.io/mongodb-vapor/current/index.html">documentation</a></li>
<li><a href="https://github.com/mongodb/swift-bson">SwiftBSON</a> and <a href="https://mongodb.github.io/swift-bson/docs/current/SwiftBSON/index.html">documentation</a></li>
<li><a href="https://vapor.codes/">Vapor</a></li>
<li><a href="https://developer.apple.com/xcode/swiftui/">SwiftUI</a></li>
</ul>I recently revealed on Twitter something that may have come as a surprise to many of my followers from the Swift/iOS community: I had never written an iOS app before! I’ve been writing Swift for a few years now but have focused entirely on library development and server-side Swift.Adopting Structured Concurrency in the MongoDB Swift Driver2022-01-28T14:00:00+00:002022-01-28T14:00:00+00:00https://kaitlin.dev/2022/01/28/adopting-structured-concurrency<p>Swift 5.5 introduced built-in support for writing structured, asynchronous, concurrent code, which you can find a great overview of in the <a href="https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html">Swift Language Guide</a>. While in prior versions of Swift it was possible to write this type of code without the built-in support, such code used paradigms such as futures/promises and callbacks and was harder to read, understand, and reason about.</p>
<p>These new features are very exciting, and we’ve just begun to adopt them in <a href="https://github.com/mongodb/mongo-swift-driver">the MongoDB Swift driver</a>. As we’ve just put out a <a href="https://github.com/mongodb/mongo-swift-driver/releases/tag/v1.3.0-alpha.1">version 1.3.0 pre-release</a>, I wanted to write a short blog post highlighting the new features structured concurrency has enabled us to add so far.</p>
<h1 id="asyncawait-apis">Async/Await APIs</h1>
<p>Our users could previously only write asynchronous code by using our API methods that return <a href="https://github.com/apple/swift-nio">SwiftNIO</a>’s <a href="https://apple.github.io/swift-nio/docs/current/NIOCore/Classes/EventLoopFuture.html"><code class="language-plaintext highlighter-rouge">EventLoopFuture</code></a>s, and then using API methods on that type to register callbacks. For example, code to create a collection, insert a document to it, and then print the resulting document’s <code class="language-plaintext highlighter-rouge">_id</code> field would look something like:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="nf">createCollection</span><span class="p">(</span>
<span class="s">"kittens"</span><span class="p">,</span>
<span class="nv">withType</span><span class="p">:</span> <span class="kt">Kitten</span><span class="o">.</span><span class="k">self</span>
<span class="p">)</span><span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">collection</span> <span class="k">in</span>
<span class="n">collection</span><span class="o">.</span><span class="nf">insertOne</span><span class="p">(</span><span class="kt">Kitten</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Chester"</span><span class="p">,</span> <span class="nv">color</span><span class="p">:</span> <span class="s">"tan"</span><span class="p">))</span>
<span class="p">}</span><span class="o">.</span><span class="n">flatMapThrowing</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">result</span><span class="p">?</span><span class="o">.</span><span class="n">insertedID</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We have now added new, <code class="language-plaintext highlighter-rouge">async</code> versions of every API method which can be used with Swift’s new async/await syntax to allow writing this code in a more natural and readable manner. The example above would become:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">collection</span> <span class="o">=</span> <span class="k">try</span> <span class="n">await</span> <span class="n">db</span><span class="o">.</span><span class="nf">createCollection</span><span class="p">(</span><span class="s">"kittens"</span><span class="p">,</span> <span class="nv">withType</span><span class="p">:</span> <span class="kt">Kitten</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="k">try</span> <span class="n">await</span> <span class="n">collection</span><span class="o">.</span><span class="nf">insertOne</span><span class="p">(</span><span class="kt">Kitten</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Chester"</span><span class="p">,</span> <span class="nv">color</span><span class="p">:</span> <span class="s">"tan"</span><span class="p">))</span>
<span class="nf">print</span><span class="p">(</span><span class="n">result</span><span class="p">?</span><span class="o">.</span><span class="n">insertedID</span><span class="p">)</span>
</code></pre></div></div>
<h1 id="improved-cursor-and-change-stream-ergonomics">Improved Cursor and Change Stream Ergonomics</h1>
<p>Structured concurrency has also enabled us to improve the experience of working with two driver types that produce sequences of values, <code class="language-plaintext highlighter-rouge">MongoCursor</code> and <code class="language-plaintext highlighter-rouge">ChangeStream</code>.</p>
<p>Previously, user code to loop over the values in a cursor or change stream and print each one, and then close the cursor might look like this:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">coll</span><span class="o">.</span><span class="nf">find</span><span class="p">()</span><span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">cursor</span> <span class="k">in</span>
<span class="n">cursor</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="n">doc</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">doc</span><span class="p">)</span>
<span class="p">}</span><span class="o">.</span><span class="n">always</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="n">_</span> <span class="o">=</span> <span class="n">cursor</span><span class="o">.</span><span class="nf">kill</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Through a <code class="language-plaintext highlighter-rouge">forEach</code> method we added to these types, users were able to register a callback that we would execute for each value in the sequence. While this allowed users to get the job done, it was not ideal that we needed to provide a custom version of a method that would be generally useful for any asynchronous sequence. It would also be non-trivial to write more complicated looping logic, for example to break out of the loop after encountering a particular value.</p>
<p>To provide an easier way to work with the sequences of values cursors and change streams produce, we added conformance to the new <a href="https://developer.apple.com/documentation/swift/asyncsequence"><code class="language-plaintext highlighter-rouge">AsyncSequence</code></a> protocol for these types. This protocol provides a number of conveniences for working with asynchronously produced sequences of values, including a familiar, simpler way to loop over the values using a for-in loop:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="k">try</span> <span class="n">await</span> <span class="n">doc</span> <span class="k">in</span> <span class="k">try</span> <span class="n">await</span> <span class="n">coll</span><span class="o">.</span><span class="nf">find</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">doc</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In the future-based API, users were required to add logic to “kill” the cursor when they were done using it before it went out of scope. This was necessary as proper cleanup of this type can require communicating with the connected MongoDB deployment in order to instruct it to destroy its corresponding resources. Ideally, resource cleanup would happen in the type’s <code class="language-plaintext highlighter-rouge">deinit</code>, but in the past there was no easy way for us to kick off such work in a non-blocking manner from <code class="language-plaintext highlighter-rouge">deinit</code>.</p>
<p>With structured concurrency, we now have a more straightforward way to do this by creating a new <code class="language-plaintext highlighter-rouge">Task</code>:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">deinit</span> <span class="p">{</span>
<span class="kt">Task</span> <span class="p">{</span>
<span class="c1">// ... kick off asynchronous cleanup work here</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, on any Swift versions and platforms where structured concurrency is available, we can automatically do this cleanup for users. One caveat to note is that background cleanup prevents users from synchronizing cleanup with other operations since there is no way to tell it is complete, but we don’t expect that to be a common need, and the <code class="language-plaintext highlighter-rouge">kill()</code> method is still available if this is necessary (if this method has been called, we won’t re-attempt cleanup in <code class="language-plaintext highlighter-rouge">deinit</code>).</p>
<p>With these new features, the example above becomes:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="k">try</span> <span class="n">await</span> <span class="n">doc</span> <span class="k">in</span> <span class="k">try</span> <span class="n">await</span> <span class="n">coll</span><span class="o">.</span><span class="nf">find</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">doc</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// cursor cleaned up in the background here</span>
</code></pre></div></div>
<p>For long-running cursors and change streams where this loop may continue indefinitely, we’ve also made it straightforward to gracefully interrupt such a loop by canceling the <code class="language-plaintext highlighter-rouge">Task</code> the cursor or change stream is running in. For example:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">changeStreamTask</span> <span class="o">=</span> <span class="kt">Task</span> <span class="p">{</span>
<span class="k">for</span> <span class="k">try</span> <span class="n">await</span> <span class="n">changeEvent</span> <span class="k">in</span> <span class="k">try</span> <span class="n">await</span> <span class="n">coll</span><span class="o">.</span><span class="nf">watch</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// process change event</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// later</span>
<span class="n">changeStreamTask</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
</code></pre></div></div>
<p>Under the hood, the loop above repeatedly calls the <code class="language-plaintext highlighter-rouge">AsyncSequence</code> protocol method <code class="language-plaintext highlighter-rouge">next()</code>, which returns either the next value in the sequence or <code class="language-plaintext highlighter-rouge">nil</code> if the end of the sequence has been reached. In our implementation of that protocol method, we check whether the <code class="language-plaintext highlighter-rouge">Task</code> is cancelled via <code class="language-plaintext highlighter-rouge">Task.isCancelled</code>, and if so, return <code class="language-plaintext highlighter-rouge">nil</code>. This will terminate any loops over the sequence and allow their <code class="language-plaintext highlighter-rouge">Task</code>s to complete. For this reason, we now recommend using long-running cursors and change streams in their own dedicated <code class="language-plaintext highlighter-rouge">Task</code>s.</p>
<h1 id="future-directions">Future Directions</h1>
<p>Our primary goal with this release was to provide an async version of our existing future-based API. That said, going forward we think there is room to improve other aspects of our API using these new features as well. For example, the driver provides an API for subscribing to command events, which currently allows registering either a callback or a custom listener type to process events. In the future, we’d like to allow users to subscribe to these via an <code class="language-plaintext highlighter-rouge">AsyncSequence</code>, like:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="k">try</span> <span class="n">await</span> <span class="n">event</span> <span class="k">in</span> <span class="n">client</span><span class="o">.</span><span class="n">commandEvents</span> <span class="p">{</span>
<span class="c1">// process event</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In general, as Swift concurrency matures we will be following along and ensuring the driver continues to integrate well with the rest of the ecosystem. If you have any suggestions for improvement, please reach out!</p>
<h1 id="trying-it-out">Trying it Out</h1>
<p>You can try this out today by adding the pre-release tag <code class="language-plaintext highlighter-rouge">1.3.0-alpha.1</code> as a dependency in your <code class="language-plaintext highlighter-rouge">Package.swift</code> file:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// swift-tools-version:5.5</span>
<span class="kd">import</span> <span class="kt">PackageDescription</span>
<span class="k">let</span> <span class="nv">package</span> <span class="o">=</span> <span class="kt">Package</span><span class="p">(</span>
<span class="nv">name</span><span class="p">:</span> <span class="s">"MyPackage"</span><span class="p">,</span>
<span class="nv">platforms</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">macOS</span><span class="p">(</span><span class="o">.</span><span class="n">v12</span><span class="p">)</span>
<span class="p">],</span>
<span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="s">"https://github.com/mongodb/mongo-swift-driver"</span><span class="p">,</span> <span class="o">.</span><span class="nf">exact</span><span class="p">(</span><span class="s">"1.3.0-alpha.1"</span><span class="p">))</span>
<span class="p">],</span>
<span class="nv">targets</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">target</span><span class="p">(</span>
<span class="nv">name</span><span class="p">:</span> <span class="s">"MyTarget"</span><span class="p">,</span>
<span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">product</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"MongoSwift"</span><span class="p">,</span> <span class="nv">package</span><span class="p">:</span> <span class="s">"mongo-swift-driver"</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>Or, if you’re using <a href="https://vapor.codes/">Vapor</a>, we recommend consuming the driver via <a href="https://github.com/mongodb/mongodb-vapor"><code class="language-plaintext highlighter-rouge">MongoDBVapor</code></a>, a small library integrating the driver with Vapor. In that case, you can depend on the latest alpha release of that library:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// swift-tools-version:5.5</span>
<span class="kd">import</span> <span class="kt">PackageDescription</span>
<span class="k">let</span> <span class="nv">package</span> <span class="o">=</span> <span class="kt">Package</span><span class="p">(</span>
<span class="nv">name</span><span class="p">:</span> <span class="s">"MyPackage"</span><span class="p">,</span>
<span class="nv">platforms</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">macOS</span><span class="p">(</span><span class="o">.</span><span class="n">v12</span><span class="p">)</span>
<span class="p">],</span>
<span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="s">"https://github.com/mongodb/mongodb-vapor"</span><span class="p">,</span> <span class="o">.</span><span class="nf">exact</span><span class="p">(</span><span class="s">"1.1.0-alpha.1"</span><span class="p">)),</span>
<span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="s">"https://github.com/vapor/vapor"</span><span class="p">,</span> <span class="o">.</span><span class="nf">upToNextMajor</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="s">"4.50.0"</span><span class="p">))</span>
<span class="p">],</span>
<span class="nv">targets</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">target</span><span class="p">(</span>
<span class="nv">name</span><span class="p">:</span> <span class="s">"MyTarget"</span><span class="p">,</span>
<span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">product</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"MongoDBVapor"</span><span class="p">,</span> <span class="nv">package</span><span class="p">:</span> <span class="s">"mongodb-vapor"</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>We have a complete example project using the driver and Vapor’s async/await APIs as well, available <a href="https://github.com/mongodb/mongo-swift-driver/tree/main/Examples/VaporExample">here</a>.</p>
<p>We’ve also updated our <a href="https://github.com/mongodb/mongo-swift-driver/blob/main/README.md">README</a> and <a href="https://mongodb.github.io/mongo-swift-driver/docs/current/MongoSwift/General%20Guides.html">documentation guides</a> to contain examples using async/await syntax.</p>
<p>If you have any questions or feedback for us, please feel free to get in touch, either by filing an issue on our <a href="https://github.com/mongodb/mongo-swift-driver/issues">GitHub repo</a> or our <a href="https://jira.mongodb.org/browse/SWIFT">Jira project</a>.</p>
<h1 id="references">References</h1>
<ul>
<li><a href="https://github.com/mongodb/mongo-swift-driver">MongoDB Swift driver</a>
<ul>
<li><a href="https://mongodb.github.io/mongo-swift-driver/docs/current/MongoSwift/index.html">API documentation</a></li>
</ul>
</li>
<li><a href="https://github.com/mongodb/mongodb-vapor">MongoDBVapor</a>
<ul>
<li><a href="https://mongodb.github.io/mongodb-vapor/current/index.html">API documentation</a></li>
</ul>
</li>
<li><a href="https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html">Swift Language Guide - Concurrency</a></li>
<li><a href="https://developer.apple.com/documentation/swift/asyncsequence">AsyncSequence</a></li>
<li><a href="https://github.com/apple/swift-nio">SwiftNIO</a></li>
<li><a href="https://vapor.codes/">Vapor</a></li>
</ul>Swift 5.5 introduced built-in support for writing structured, asynchronous, concurrent code, which you can find a great overview of in the Swift Language Guide. While in prior versions of Swift it was possible to write this type of code without the built-in support, such code used paradigms such as futures/promises and callbacks and was harder to read, understand, and reason about.Getting Started with Server-Side Swift with MongoDB and Vapor2021-07-30T14:00:00+00:002021-07-30T14:00:00+00:00https://kaitlin.dev/2021/07/30/mongodb-vapor<p><em>Tutorials not your style? You can find a complete example project using MongoDB with Vapor <a href="https://github.com/mongodb/mongo-swift-driver/tree/main/Examples/VaporExample">on GitHub</a>.</em></p>
<p>This blog post will take you through the basics of creating a server-side Swift application using <a href="https://www.mongodb.com/">MongoDB</a> and <a href="https://vapor.codes/">Vapor</a>.</p>
<p>This post assumes you have Swift 5.2 (or newer) installed on your machine. You can find instructions to install Swift on various platforms <a href="https://swift.org/getting-started/#installing-swift">here</a>.</p>
<ul>
<li><a href="#step-1-set-up-mongodb">Step 1: Set up MongoDB</a>
<ul>
<li><a href="#atlas-recommended">Atlas (recommended)</a></li>
<li><a href="#local-mongodb">Local MongoDB</a></li>
</ul>
</li>
<li><a href="#step-2-install-vapor-toolbox">Step 2: Install Vapor Toolbox</a></li>
<li><a href="#step-3-create-a-new-project">Step 3: Create a new project</a></li>
<li><a href="#step-4-build-and-run-your-application">Step 4: Build and run your application</a>
<ul>
<li><a href="#from-the-command-line">From the command line</a></li>
<li><a href="#or-from-xcode">Or, from Xcode</a></li>
</ul>
</li>
<li><a href="#step-5-test-out-the-application">Step 5: Test out the application</a></li>
<li><a href="#step-6-explore-the-code-and-modify-to-your-liking">Step 6: Explore the code and modify to your liking</a>
<ul>
<li><a href="#packageswift"><code class="language-plaintext highlighter-rouge">Package.swift</code></a></li>
<li><a href="#resources"><code class="language-plaintext highlighter-rouge">Resources</code></a></li>
<li><a href="#sources"><code class="language-plaintext highlighter-rouge">Sources</code></a>
<ul>
<li><a href="#sourcesapp"><code class="language-plaintext highlighter-rouge">Sources/App</code></a></li>
<li><a href="#sourcesrun"><code class="language-plaintext highlighter-rouge">Sources/Run</code></a></li>
</ul>
</li>
<li><a href="#tests"><code class="language-plaintext highlighter-rouge">Tests</code></a></li>
</ul>
</li>
<li><a href="#step-7-now-what">Step 7: Now what?</a></li>
<li><a href="#references">References</a></li>
</ul>
<h1 id="step-1-set-up-mongodb">Step 1: Set up MongoDB</h1>
<p>You have two options for this: either downloading and running MongoDB <a href="#local-mongodb">locally</a>, or creating a free MongoDB cluster hosted in the cloud using <a href="#atlas-recommended">Atlas</a>, MongoDB’s official database-as-a-service.</p>
<h2 id="atlas-recommended">Atlas (recommended)</h2>
<p><a href="https://www.mongodb.com/cloud/atlas">Atlas</a> is the easiest way to get started using MongoDB.</p>
<p>First, follow parts 1-4 in the “Getting Started” tutorial available <a href="https://docs.atlas.mongodb.com/getting-started/">here</a>.</p>
<p>Next, in the “Database Deployments” view on Atlas, click the “Connect” button next to your cluster name:</p>
<p><img src="/files/vapor-post/vapor_1.png" alt="" /><br /><br /></p>
<p>Then select “Connect your application”:</p>
<p><img src="/files/vapor-post/vapor_2.png" alt="" /><br /><br /></p>
<p>In the drop-down menu*, select “C” as your driver and “version 1.17 or later” as your version.</p>
<p>Step 2 will then display a connection string in the form <code class="language-plaintext highlighter-rouge">mongodb+srv://...</code>. <strong>Copy this connection string and save it for use later on in the tutorial.</strong></p>
<p><img src="/files/vapor-post/vapor_3.png" alt="" /><br /><br /></p>
<p>*<em>Unfortunately this drop-down does not include the Swift driver yet, but it will soon! In the meantime, since the Swift driver depends on the C driver, the connection string needed is identical.</em></p>
<h2 id="local-mongodb">Local MongoDB</h2>
<p>Follow the instructions available <a href="https://docs.mongodb.com/manual/administration/install-community/">here</a> to install MongoDB Community Edition and start up a local MongoDB server.</p>
<h1 id="step-2-install-vapor-toolbox">Step 2: Install Vapor Toolbox</h1>
<p>Vapor Toolbox is a helpful CLI tool for setting up new projects using Vapor. Follow the installation instructions <a href="https://docs.vapor.codes/4.0/install/macos/#install-toolbox">here</a> for macOS or <a href="https://docs.vapor.codes/4.0/install/linux/#install-toolbox">here</a> for Linux.</p>
<h1 id="step-3-create-a-new-project">Step 3: Create a new project</h1>
<p>This project will utilize <a href="https://github.com/mongodb/mongodb-vapor">MongoDBVapor</a>, a small library which integrates <a href="https://github.com/mongodb/mongo-swift-driver">the official MongoDB Swift driver</a> with Vapor.</p>
<p>You can use Vapor Toolbox from your terminal to instantiate a new project using MongoDBVapor from a template repository, as follows:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vapor new MyProject --template https://github.com/mongodb/mongodb-vapor-template/
</code></pre></div></div>
<p>This command will prompt you to select whether or not you’d like to use <a href="https://github.com/vapor/leaf">Leaf</a>, a library that allows you to create front-end templates for dynamically generating web pages in your Vapor application.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Would you like to use Leaf? (--leaf/--no-leaf)
y/n>
</code></pre></div></div>
<p>The rest of this post will assume you entered <code class="language-plaintext highlighter-rouge">y</code>, but to create a backend-only application you can enter <code class="language-plaintext highlighter-rouge">n</code>.</p>
<h1 id="step-4-build-and-run-your-application">Step 4: Build and run your application</h1>
<p>You can build and run your application either <a href="#from-the-command-line">from the command line</a> or <a href="#or-from-xcode">using Xcode</a>.</p>
<h2 id="from-the-command-line">From the command line</h2>
<p>First, move into the project directory:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd MyProject
</code></pre></div></div>
<p>Then, if you used MongoDB Atlas to start up a cluster above, specify the MongoDB connection string you saved via an environment variable:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export MONGODB_URI="your-connection-string-here"
</code></pre></div></div>
<p>if you started up a local MongoDB instance via the instructions above, your MongoDB instance should be running on the default MongoDB host and port and so you can skip setting the environment variable, as the driver will default to trying to connect to that.</p>
<p>Next, build your application (this will take a while the first time, as all of the dependencies must be downloaded and built themselves):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>swift build
</code></pre></div></div>
<p>Finally, run your application:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>swift run
</code></pre></div></div>
<p>The terminal should then print:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ NOTICE ] Server starting on http://127.0.0.1:8080
</code></pre></div></div>
<h2 id="or-from-xcode">Or, from Xcode</h2>
<p>First, move into the project directory:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd MyProject
</code></pre></div></div>
<p>Then, open the project in Xcode:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xed .
</code></pre></div></div>
<p>Xcode should then automatically resolve and download your project’s dependencies.</p>
<p>Then, if you used MongoDB Atlas to start up a cluster above, specify the MongoDB connection string you saved via an environment variable (if you started up a local MongoDB instance via the instructions above, your MongoDB instance should be running on the default MongoDB host and port and so you can skip this step.)</p>
<p>To set the environment variable, click on the package name <code class="language-plaintext highlighter-rouge">VaporExample</code> in the top bar of Xcode, then “Edit Scheme”:</p>
<p><img src="/files/vapor-post/vapor_4.png" alt="" /><br /><br /></p>
<p>Then select “Run” in the menu on the left side, and the “Arguments” tab:</p>
<p><img src="/files/vapor-post/vapor_5.png" alt="" /><br /><br /></p>
<p>Then, click the + sign under “Environment Variables” and add your connection string under the variable <code class="language-plaintext highlighter-rouge">MONGODB_URI</code>.</p>
<p><img src="/files/vapor-post/vapor_6.png" alt="" /><br /><br /></p>
<p>You will also need to set a custom working directory for your project to enable Xcode to locate the Leaf template. To do this, go to the “Options” tab, check the box “Use custom working directory”, and enter the path to your project:</p>
<p><img src="/files/vapor-post/vapor_7.png" alt="" /><br /><br /></p>
<p>You can then close the scheme editor, and build and run your project by clicking the play symbol button in the top left corner of Xcode.</p>
<p>Once your application is running, the following should display in the Xcode console (you can open this by going to View > Debug Area > Activate Console):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ NOTICE ] Server starting on http://127.0.0.1:8080
</code></pre></div></div>
<h1 id="step-5-test-out-the-application">Step 5: Test out the application</h1>
<p>Your application should now be running locally and connected to MongoDB. To test it out, navigate to <a href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> in your browser! You should see a page that looks like this:</p>
<p><img src="/files/vapor-post/vapor_8.png" alt="" /><br /><br /></p>
<p>In the terminal, you should see a corresponding GET request to load the main page:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ INFO ] GET / [request-id: FD46E644-EC23-4184-86EB-DE6FF601E4CA]
</code></pre></div></div>
<p>You should be able to add kittens to the list and see the list stay updated in real-time:</p>
<p><img src="/files/vapor-post/vapor_9.png" alt="" /><br /><br /></p>
<p>After adding a kitten, you should see a POST request logged to create the kitten, followed by a GET request to reload the list of kittens:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ INFO ] POST / [request-id: C8885B95-BC75-4EED-A022-5592A4073B76]
[ INFO ] GET / [request-id: A7001840-94C6-4DC1-9D2F-FD4C283C5023]
</code></pre></div></div>
<h1 id="step-6-explore-the-code-and-modify-to-your-liking">Step 6: Explore the code and modify to your liking</h1>
<p>This section will cover the structure of the project and explain the various ways MongoDBVapor is used throughout it.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├── Resources
│ ├── Views
│ ├── index.leaf
├── Sources
│ ├── App
│ │ ├── configure.swift
│ │ └── routes.swift
│ └── Run
│ └── main.swift
├── Tests
│ └── AppTests
│ └── AppTests.swift
└── Package.swift
</code></pre></div></div>
<h2 id="packageswift"><code class="language-plaintext highlighter-rouge">Package.swift</code></h2>
<p><code class="language-plaintext highlighter-rouge">Package.swift</code> is the Swift Package Manager manifest file, which defines the dependencies and structure of the project, including the dependency on MongoDBVapor. You can read more about SwiftPM and manifest files <a href="https://swift.org/package-manager/">here on swift.org</a>.</p>
<h2 id="resources"><code class="language-plaintext highlighter-rouge">Resources</code></h2>
<p>The <code class="language-plaintext highlighter-rouge">Resources/Views</code> directory contains the Leaf template for our app’s main page, <code class="language-plaintext highlighter-rouge">index.leaf</code>. By default, this folder is the location where Leaf will look for template files to live.</p>
<h2 id="sources"><code class="language-plaintext highlighter-rouge">Sources</code></h2>
<p><code class="language-plaintext highlighter-rouge">Sources</code> contains two directories, <code class="language-plaintext highlighter-rouge">App</code> and <code class="language-plaintext highlighter-rouge">Run</code>, each corresponding to a target defined in <code class="language-plaintext highlighter-rouge">Package.swift</code>.</p>
<h3 id="sourcesapp"><code class="language-plaintext highlighter-rouge">Sources/App</code></h3>
<p>The <code class="language-plaintext highlighter-rouge">App</code> target is where all of the logic for the application itself lives, including configuration code, model types, and request handlers.</p>
<p><code class="language-plaintext highlighter-rouge">configure.swift</code> contains a <code class="language-plaintext highlighter-rouge">configure(:_)</code> method that performs one-time configuration code run before the application is started. This includes a line of code to configure a global MongoDB client for the application to use:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">try</span> <span class="n">app</span><span class="o">.</span><span class="n">mongoDB</span><span class="o">.</span><span class="nf">configure</span><span class="p">(</span><span class="kt">Environment</span><span class="o">.</span><span class="nf">get</span><span class="p">(</span><span class="s">"MONGODB_URI"</span><span class="p">)</span> <span class="p">??</span> <span class="s">"mongodb://localhost:27017"</span><span class="p">)</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">routes.swift</code> contains:</p>
<p>1) A custom Swift type that matches the structure of documents in the MongoDB collection:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// A type matching the structure of documents in the corresponding MongoDB collection.</span>
<span class="kd">struct</span> <span class="kt">Kitten</span><span class="p">:</span> <span class="kt">Content</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">_id</span><span class="p">:</span> <span class="kt">BSONObjectID</span><span class="p">?</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">color</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">createdAt</span><span class="p">:</span> <span class="kt">Date</span><span class="p">?</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This type conforms to Vapor’s <a href="https://docs.vapor.codes/4.0/content/"><code class="language-plaintext highlighter-rouge">Content</code></a> protocol, which inherits from Swift’s <a href="https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types"><code class="language-plaintext highlighter-rouge">Codable</code></a> protocol, and allows you to convert it to and initialize it from data in external formats, including HTTP requests/responses and BSON documents, the format MongoDB stores data in.</p>
<p>2) An extension to <code class="language-plaintext highlighter-rouge">Request</code>, adding a computed property to access the MongoDB collection we’re working with:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Request</span> <span class="p">{</span>
<span class="c1">/// Convenience accessor for the home.kittens collection.</span>
<span class="k">var</span> <span class="nv">kittenCollection</span><span class="p">:</span> <span class="kt">MongoCollection</span><span class="o"><</span><span class="kt">Kitten</span><span class="o">></span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">mongoDB</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="nf">db</span><span class="p">(</span><span class="s">"home"</span><span class="p">)</span><span class="o">.</span><span class="nf">collection</span><span class="p">(</span><span class="s">"kittens"</span><span class="p">,</span> <span class="nv">withType</span><span class="p">:</span> <span class="kt">Kitten</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Associating the <code class="language-plaintext highlighter-rouge">Kitten</code> type with the collection allows you to use your custom type directly with driver API methods such as <code class="language-plaintext highlighter-rouge">insertOne</code> and <code class="language-plaintext highlighter-rouge">find()</code>. The driver will then automatically handle converting back and forth between BSON documents and your data type.</p>
<p>Any MongoDB types accessed via <code class="language-plaintext highlighter-rouge">Request.mongoDB</code> will automatically return <code class="language-plaintext highlighter-rouge">EventLoopFuture</code>s on the same <code class="language-plaintext highlighter-rouge">EventLoop</code> the <code class="language-plaintext highlighter-rouge">Request</code> is executing on. This simplifies thread safety concerns and improves performance by removing the need to hop the returned futures.</p>
<p>3) a <code class="language-plaintext highlighter-rouge">routes(_:)</code> function which registers request handlers on the <code class="language-plaintext highlighter-rouge">Application</code> and utilizes the extension and <code class="language-plaintext highlighter-rouge">Kitten</code> type defined above:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">routes</span><span class="p">(</span><span class="n">_</span> <span class="nv">app</span><span class="p">:</span> <span class="kt">Application</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
<span class="c1">// A GET request will return a list of all kittens in the database.</span>
<span class="n">app</span><span class="o">.</span><span class="k">get</span> <span class="p">{</span> <span class="n">req</span> <span class="o">-></span> <span class="kt">EventLoopFuture</span><span class="o"><</span><span class="kt">View</span><span class="o">></span> <span class="k">in</span>
<span class="n">req</span><span class="o">.</span><span class="n">kittenCollection</span><span class="o">.</span><span class="nf">find</span><span class="p">()</span><span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">cursor</span> <span class="k">in</span>
<span class="n">cursor</span><span class="o">.</span><span class="nf">toArray</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">kittens</span> <span class="k">in</span>
<span class="n">req</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="nf">render</span><span class="p">(</span><span class="s">"index.leaf"</span><span class="p">,</span> <span class="p">[</span><span class="s">"kittens"</span><span class="p">:</span> <span class="n">kittens</span><span class="p">])</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// A POST request will create a new kitten in the database.</span>
<span class="n">app</span><span class="o">.</span><span class="n">post</span> <span class="p">{</span> <span class="n">req</span> <span class="o">-></span> <span class="kt">EventLoopFuture</span><span class="o"><</span><span class="kt">Response</span><span class="o">></span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">newKitten</span> <span class="o">=</span> <span class="k">try</span> <span class="n">req</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="nf">decode</span><span class="p">(</span><span class="kt">Kitten</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
<span class="n">newKitten</span><span class="o">.</span><span class="n">createdAt</span> <span class="o">=</span> <span class="kt">Date</span><span class="p">()</span>
<span class="k">return</span> <span class="n">req</span><span class="o">.</span><span class="n">kittenCollection</span><span class="o">.</span><span class="nf">insertOne</span><span class="p">(</span><span class="n">newKitten</span><span class="p">)</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="kt">Response</span><span class="p">(</span><span class="nv">status</span><span class="p">:</span> <span class="o">.</span><span class="n">created</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="sourcesrun"><code class="language-plaintext highlighter-rouge">Sources/Run</code></h3>
<p>The <code class="language-plaintext highlighter-rouge">Run</code> target is the main executable target and contains a single file <code class="language-plaintext highlighter-rouge">main.swift</code> with a minimal amount of code to start the application running as well as to clean it up properly on shutdown.</p>
<p>This includes a call to <code class="language-plaintext highlighter-rouge">app.mongoDB.cleanup()</code>, which ensures your application’s MongoDB client is properly cleaned up and all connections to the database are closed before the application shuts down.</p>
<h2 id="tests"><code class="language-plaintext highlighter-rouge">Tests</code></h2>
<p>This directory contains the unit tests for your application. For now, there is just a simple test in there which confirms the application can start up, connect to a local MongoDB instance, and query for a list of kittens. You can run the test yourself from the command line via <code class="language-plaintext highlighter-rouge">swift test</code>, or using the Xcode UI.</p>
<h1 id="step-7-now-what">Step 7: Now what?</h1>
<p>To read more about the available features and see more examples, check out the <a href="https://github.com/mongodb/mongodb-vapor#example-usage">Example Usage</a> section from the library’s README.</p>
<p>A more complex example application with examples of how to perform various types of MongoDB queries is also available <a href="https://github.com/mongodb/mongo-swift-driver/tree/main/Examples/VaporExample">here</a>, with a <a href="https://github.com/mongodb/mongo-swift-driver/blob/main/Examples/VaporExample/README.md">README</a> which further discusses recommended usage of the library.</p>
<p>You can find the API documentation for MongoDBVapor <a href="https://mongodb.github.io/mongodb-vapor/">here</a>, and the documentation for Vapor <a href="https://docs.vapor.codes/4.0/">here</a>.</p>
<p>If you have any questions or feedback on the MongoDB driver or MongoDBVapor we’d love to hear from you, either via GitHub <a href="https://github.com/mongodb/mongodb-vapor/issues">issues</a> or our <a href="https://jira.mongodb.org/projects/SWIFT">SWIFT Jira project</a>!</p>
<h1 id="references">References</h1>
<ul>
<li><a href="https://github.com/mongodb/mongodb-vapor">MongoDBVapor GitHub repository</a></li>
<li><a href="https://mongodb.github.io/mongodb-vapor/">MongoDBVapor API documentation</a></li>
<li><a href="https://github.com/mongodb/mongo-swift-driver/tree/main/Examples/VaporExample">Example project using MongoDBVapor</a></li>
<li><a href="https://github.com/mongodb/mongodb-vapor-template/">Vapor Toolbox template using MongoDBVapor</a></li>
<li><a href="https://docs.vapor.codes/4.0/">Vapor documentation</a></li>
<li><a href="https://docs.mongodb.com/manual/">MongoDB documentation</a></li>
<li><a href="https://docs.atlas.mongodb.com/">MongoDB Atlas documentation</a></li>
<li><a href="https://github.com/mongodb/mongo-swift-driver">MongoDB Swift Driver GitHub repository</a></li>
<li><a href="https://jira.mongodb.org/projects/SWIFT">MongoDB Swift Driver Jira project</a></li>
<li><a href="https://swift.org/package-manager/">Swift Package Manager</a></li>
<li><a href="https://swift.org/getting-started/">Swift Getting Started Guide</a></li>
</ul>Tutorials not your style? You can find a complete example project using MongoDB with Vapor on GitHub.Announcing the 1.0 Release of the Official MongoDB Swift Driver2020-06-08T12:00:00+00:002020-06-08T12:00:00+00:00https://kaitlin.dev/2020/06/08/mongodb-swift-driver<p><em>This is a cross-post from the MongoDB blog. You can find the original article <a href="https://www.mongodb.com/blog/post/announcing-release-official-swift-driver">here</a>.</em></p>
<hr />
<p>I’m very excited to announce the 1.0 release of the official <a href="https://github.com/mongodb/mongo-swift-driver">MongoDB driver for Swift</a>! The driver is now GA, ready for use in production, and fully supported by MongoDB.</p>
<p>In this blog post, I’ll cover the history of the driver, features and use cases we support, how you can get started using and contributing to the driver, and our future plans for the project.</p>
<h1 id="evolution-of-the-driver">Evolution of the Driver</h1>
<p>My team and I initially began developing this library in 2018 to support the use of MongoDB Mobile on iOS. MongoDB’s mobile efforts were refocused with the acquisition of <a href="https://www.mongodb.com/realm">Realm</a> in 2019, but the year we spent working with the Swift community gave us valuable insight nevertheless. Once we better understood Swift’s potential beyond iOS as a great general-purpose programming language, we started to think about how we could best support Swift developers writing server-side applications with MongoDB.</p>
<p>With new use cases for the driver in mind, we turned to the community and the <a href="https://swift.org/server/">Swift Server Work Group</a> (SSWG), a steering team for server-side Swift efforts. After a number of conversations, both on the <a href="https://forums.swift.org/">Swift Forums</a> and at conferences like <a href="https://www.serversideswift.info/">ServerSide.swift</a>, we submitted a <a href="https://github.com/swift-server/sswg/blob/main/proposals/0010-mongo-swift-driver.md">proposal</a> for the driver to take part in the SSWG <a href="https://github.com/swift-server/sswg/blob/main/process/incubation.md">incubation process</a> for libraries. All of this generated invaluable feedback from both members of the workgroup and the Swift community at large and helped us refine our API to integrate better with the rest of the server-side ecosystem.</p>
<p>Now that our proposal has been accepted, we look forward to taking part in the incubation process and iterating through feedback and direction from the SSWG, and we hope to ultimately reach the process’s graduation stage.</p>
<h1 id="supported-features-and-use-cases">Supported Features and Use Cases</h1>
<p>The server-side Swift ecosystem is decidedly asynchronous first, so we are releasing a fully asynchronous 1.0 driver based on Apple’s <a href="https://github.com/apple/swift-nio">SwiftNIO</a>. This means you can expect our APIs to work seamlessly with popular asynchronous libraries and frameworks like <a href="https://vapor.codes/">Vapor</a>.</p>
<p>We also strongly believe in the potential of Swift as a language for synchronous use cases, such as scripts and command line utilities, so we provide a synchronous API as well.</p>
<p>The driver has Swifty APIs for everything from <a href="https://docs.mongodb.com/manual/reference/bson-types/">BSON</a> to <a href="https://docs.mongodb.com/manual/changeStreams/">change streams</a> to <a href="https://docs.mongodb.com/manual/aggregation/">aggregation</a> and supports connection pooling, application performance monitoring, and automatically retrying failed commands.</p>
<p>In addition to providing a flexible BSON document type, the driver leverages Swift’s <a href="https://developer.apple.com/documentation/swift/codable"><code class="language-plaintext highlighter-rouge">Codable</code></a> types, enabling use of custom types directly with MongoDB collections without needing a separate ODM (object-document mapper) library.</p>
<p>The minimum Swift version the driver supports is 5.1. The driver can be used with all MongoDB versions 3.6 and higher, including 4.4, though there are some newer server features we haven’t exposed APIs for yet. You can expect to see those added in upcoming minor versions.</p>
<p>You can view our full API, along with guides on how to use various aspects of the API, on our <a href="https://mongodb.github.io/mongo-swift-driver/docs/current/MongoSwift/index.html">documentation website</a>.</p>
<h1 id="getting-started">Getting Started</h1>
<p>You can include the driver as a dependency in your application via <a href="https://swift.org/package-manager/">Swift Package Manager</a>:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// swift-tools-version:5.1</span>
<span class="kd">import</span> <span class="kt">PackageDescription</span>
<span class="k">let</span> <span class="nv">package</span> <span class="o">=</span> <span class="kt">Package</span><span class="p">(</span>
<span class="nv">name</span><span class="p">:</span> <span class="s">"MyPackage"</span><span class="p">,</span>
<span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="s">"https://github.com/mongodb/mongo-swift-driver"</span><span class="p">,</span> <span class="o">.</span><span class="nf">upToNextMajor</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="s">"1.0.0"</span><span class="p">)),</span>
<span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="s">"https://github.com/apple/swift-nio"</span><span class="p">,</span> <span class="o">.</span><span class="nf">upToNextMajor</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="s">"2.0.0"</span><span class="p">))</span>
<span class="p">],</span>
<span class="nv">targets</span><span class="p">:</span> <span class="p">[</span>
<span class="o">.</span><span class="nf">target</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"MyTarget"</span><span class="p">,</span> <span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span><span class="s">"MongoSwift"</span><span class="p">,</span> <span class="s">"NIO"</span><span class="p">])</span>
<span class="p">]</span>
<span class="p">)</span>
</code></pre></div></div>
<p>In order to use the asynchronous API, you’ll need a SwiftNIO <a href="https://apple.github.io/swift-nio/docs/current/NIO/Protocols/EventLoopGroup.html"><code class="language-plaintext highlighter-rouge">EventLoopGroup</code></a>, so you’ll need to include SwiftNIO as a dependency, too. (For synchronous usage, you can omit that dependency and depend on the <code class="language-plaintext highlighter-rouge">MongoSwiftSync</code> module.)</p>
<p>You can then run <code class="language-plaintext highlighter-rouge">swift build</code> to download your dependencies and compile your application.</p>
<p>To create a client and perform some basic operations:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">MongoSwift</span>
<span class="kd">import</span> <span class="kt">NIO</span>
<span class="c1">// Create an EventLoopGroup and client</span>
<span class="k">let</span> <span class="nv">elg</span> <span class="o">=</span> <span class="kt">MultiThreadedEventLoopGroup</span><span class="p">(</span><span class="nv">numberOfThreads</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">client</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">MongoClient</span><span class="p">(</span><span class="s">"mongodb://localhost:27017"</span><span class="p">,</span> <span class="nv">using</span><span class="p">:</span> <span class="n">elg</span><span class="p">)</span>
<span class="c1">// Application cleanup code</span>
<span class="k">defer</span> <span class="p">{</span>
<span class="k">try</span><span class="p">?</span> <span class="n">client</span><span class="o">.</span><span class="nf">syncClose</span><span class="p">()</span>
<span class="nf">cleanupMongoSwift</span><span class="p">()</span>
<span class="k">try</span><span class="p">?</span> <span class="n">elg</span><span class="o">.</span><span class="nf">syncShutdownGracefully</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">// Define a struct that matches the data in our collection</span>
<span class="kd">struct</span> <span class="kt">Cat</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</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">color</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">let</span> <span class="nv">_id</span><span class="p">:</span> <span class="kt">BSONObjectID</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">db</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="nf">db</span><span class="p">(</span><span class="s">"test"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">cats</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="nf">collection</span><span class="p">(</span><span class="s">"cats"</span><span class="p">,</span> <span class="nv">withType</span><span class="p">:</span> <span class="kt">Cat</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="p">[</span>
<span class="kt">Cat</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Chester"</span><span class="p">,</span> <span class="nv">color</span><span class="p">:</span> <span class="s">"tan"</span><span class="p">,</span> <span class="nv">_id</span><span class="p">:</span> <span class="kt">BSONObjectID</span><span class="p">()),</span>
<span class="kt">Cat</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Roscoe"</span><span class="p">,</span> <span class="nv">color</span><span class="p">:</span> <span class="s">"orange"</span><span class="p">,</span> <span class="nv">_id</span><span class="p">:</span> <span class="kt">BSONObjectID</span><span class="p">())</span>
<span class="p">]</span>
<span class="c1">// insert values into the collection</span>
<span class="k">let</span> <span class="nv">insert</span> <span class="o">=</span> <span class="n">cats</span><span class="o">.</span><span class="nf">insertMany</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">insert</span><span class="o">.</span><span class="n">whenSuccess</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">result</span><span class="p">?</span><span class="o">.</span><span class="n">insertedCount</span><span class="p">)</span> <span class="c1">// prints 2</span>
<span class="p">}</span>
<span class="c1">// create a cursor over the collection to read back the documents</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">insert</span><span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
<span class="n">cats</span><span class="o">.</span><span class="nf">find</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">cursor</span> <span class="k">in</span>
<span class="n">cursor</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="n">cat</span> <span class="k">in</span>
<span class="nf">print</span><span class="p">(</span><span class="n">cat</span><span class="p">)</span> <span class="c1">// prints out a `Cat` struct</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Please see our <a href="https://github.com/mongodb/mongo-swift-driver/blob/main/README.md">README</a> for more details. Our GitHub repository also contains <a href="https://github.com/mongodb/mongo-swift-driver/tree/main/Examples">example applications</a> demonstrating how to integrate the driver with server-side Swift frameworks like <a href="https://vapor.codes/">Vapor</a>.</p>
<h1 id="next-steps-for-the-team">Next Steps for the Team</h1>
<p>Currently, the driver uses the <a href="https://github.com/mongodb/mongo-c-driver">MongoDB C Driver</a> (also known as “libmongoc”) underneath the hood. <code class="language-plaintext highlighter-rouge">libmongoc</code> has provided a solid, reliable core, and depending on it allowed us to focus on building a great API. Now that we’ve achieved API stability, we will shift our focus to writing driver internals in Swift. We expect this to enhance driver performance and to speed up our own development process. Meanwhile, we’ll continue adding support to the driver for the latest MongoDB versions.</p>
<p>We are grateful for the opportunity we have to give back to the server-side Swift community. We plan to make as much of our work as possible useful to the ecosystem and want to collaborate with the SSWG and the community at large whenever we can on our upcoming projects. As an example, we’ll need to write a SwiftNIO-based connection pool for the driver to use, based on our connection pooling <a href="https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst">specification</a>. We’d like to implement that as a standalone library which can serve as a reference implementation for other Swift developers.</p>
<h1 id="getting-involved">Getting Involved</h1>
<p>If you have any questions, feedback, or ideas you’d like to discuss with us, you can contact us either through our <a href="https://jira.mongodb.org/projects/SWIFT/issues">JIRA project</a> or through a <a href="https://github.com/mongodb/mongo-swift-driver">GitHub</a> issue.</p>
<p>We’re happy to accept GitHub pull requests, too – check out our <a href="https://github.com/mongodb/mongo-swift-driver/blob/main/Guides/Development.md">Development Guide</a> for information on how to build and test the driver.</p>
<p>We’d love for you to try out the driver and let us know what you think. Thanks so much for reading!</p>This is a cross-post from the MongoDB blog. You can find the original article here.Why doesn’t JSONEncoder conform to the Encoder protocol? 🤔2019-01-07T16:00:00+00:002019-01-07T16:00:00+00:00https://kaitlin.dev/2019/01/07/jsonencoder-encoder<p>A common point of confusion in the Swift encoding and decoding ecosystem is that there is an <a href="https://developer.apple.com/documentation/swift/encoder"><code class="language-plaintext highlighter-rouge">Encoder</code> protocol</a>, but standard library types like <a href="https://developer.apple.com/documentation/foundation/jsonencoder"><code class="language-plaintext highlighter-rouge">JSONEncoder</code></a> and <a href="https://developer.apple.com/documentation/foundation/propertylistencoder"><code class="language-plaintext highlighter-rouge">PropertyListEncoder</code></a> don’t conform to it. We encounter the <code class="language-plaintext highlighter-rouge">Encoder</code> protocol when we make our types conform to <a href="https://developer.apple.com/documentation/swift/encodable"><code class="language-plaintext highlighter-rouge">Encodable</code></a>:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// A type that can encode itself to an external representation.</span>
<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">Encodable</span> <span class="p">{</span>
<span class="c1">/// Encodes this value into the given encoder. </span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">encode</span><span class="p">(</span><span class="n">to</span> <span class="nv">encoder</span><span class="p">:</span> <span class="kt">Encoder</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The protocol requires us to implement an <code class="language-plaintext highlighter-rouge">encode(to:)</code> method, which takes in an <code class="language-plaintext highlighter-rouge">Encoder</code>.</p>
<p>What’s going on here? 🤔🤔🤔</p>
<p>The answer lies in the architecture of <code class="language-plaintext highlighter-rouge">JSONEncoder</code>.</p>
<p>Inspecting <a href="https://github.com/apple/swift/blob/master/stdlib/public/Darwin/Foundation/JSONEncoder.swift">the source code</a> for <code class="language-plaintext highlighter-rouge">JSONEncoder</code>, we see it’s a <a href="https://kaitlinmahar.com/2018/12/29/open-swift.html"><code class="language-plaintext highlighter-rouge">open</code> type</a> that internally uses a <code class="language-plaintext highlighter-rouge">private</code> type <code class="language-plaintext highlighter-rouge">_JSONEncoder</code>, which <em>does</em> conform to <code class="language-plaintext highlighter-rouge">Encoder</code>.</p>
<div align="center">
<img src="/files/encoder-decoder/encoder-structure.png" width="400" align="center" /><br />
<i>JSONEncoder uses a private _JSONEncoder.</i>
</div>
<p><br /><br /></p>
<p>The actual code is rather complicated, but it boils down to something like this:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">JSONEncoder</span> <span class="p">{</span>
<span class="kd">func</span> <span class="n">encode</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Encodable</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Data</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">privateEncoder</span> <span class="o">=</span> <span class="nf">_JSONEncoder</span><span class="p">()</span>
<span class="k">try</span> <span class="n">value</span><span class="o">.</span><span class="nf">encode</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">privateEncoder</span><span class="p">)</span>
<span class="c1">// do some processing to convert `privateEncoder`'s contents</span>
<span class="c1">// to `Data`, and return it</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>So that <code class="language-plaintext highlighter-rouge">encode(to:)</code> method you wrote for your <code class="language-plaintext highlighter-rouge">Encodable</code> type <em>is</em> being used – it’s just that a <code class="language-plaintext highlighter-rouge">_JSONEncoder</code> is passed in, and <em>not</em> a <code class="language-plaintext highlighter-rouge">JSONEncoder</code>.</p>
<p>The same is true on the decoding side - there is a <a href="https://developer.apple.com/documentation/swift/decoder"><code class="language-plaintext highlighter-rouge">Decoder</code></a> protocol, which we see in the <a href="https://developer.apple.com/documentation/swift/decodable"><code class="language-plaintext highlighter-rouge">Decodable</code></a> protocol:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// A type that can decode itself from an external representation.</span>
<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">Decodable</span> <span class="p">{</span>
<span class="c1">/// Creates a new instance by decoding from the given decoder.</span>
<span class="nf">init</span><span class="p">(</span><span class="n">from</span> <span class="nv">decoder</span><span class="p">:</span> <span class="kt">Decoder</span><span class="p">)</span> <span class="k">throws</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But <a href="https://developer.apple.com/documentation/foundation/jsondecoder"><code class="language-plaintext highlighter-rouge">JSONDecoder</code></a> and <a href="https://developer.apple.com/documentation/foundation/propertylistdecoder"><code class="language-plaintext highlighter-rouge">PropertyListDecoder</code></a> don’t conform to <code class="language-plaintext highlighter-rouge">Decoder</code>, and instead internally use private <code class="language-plaintext highlighter-rouge">Decoder</code> types.</p>
<div align="center">
<img src="/files/encoder-decoder/decoder-structure.png" width="400" align="center" /><br />
<i>JSONDecoder uses a private _JSONDecoder.</i>
</div>
<p><br /><br /></p>
<p>In code, it plays out similarly to the encoding case:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">JSONDecoder</span> <span class="p">{</span>
<span class="kd">func</span> <span class="n">decode</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Decodable</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="k">Type</span><span class="p">,</span> <span class="n">from</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">Data</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">T</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">privateDecoder</span> <span class="o">=</span> <span class="nf">_JSONDecoder</span><span class="p">()</span>
<span class="c1">// do some processing to store `data` in `privateDecoder`'s storage</span>
<span class="k">return</span> <span class="k">try</span> <span class="kt">T</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">privateDecoder</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="ok-but-why">Ok… but why?</h2>
<p>Ok, so now we see how the <code class="language-plaintext highlighter-rouge">Encodable</code> and <code class="language-plaintext highlighter-rouge">Decodable</code> protocols fit into <code class="language-plaintext highlighter-rouge">JSONEncoder</code> and <code class="language-plaintext highlighter-rouge">JSONDecoder</code>. But why were they designed that way? Why not just make <code class="language-plaintext highlighter-rouge">JSONEncoder</code> an <code class="language-plaintext highlighter-rouge">Encoder</code> too?</p>
<p>In short, the answer is that they provide very different APIs. The <code class="language-plaintext highlighter-rouge">JSONEncoder</code> API is designed to provide a single, simple entry point into encoding, and the <code class="language-plaintext highlighter-rouge">Encoder</code> protocol provides a completely different API for customizing how types are encoded.</p>
<p>Consider <code class="language-plaintext highlighter-rouge">JSONEncoder</code>. All we really get is this one simple method:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">encode</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Encodable</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Data</span>
</code></pre></div></div>
<p>And we know exactly what to do with it:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">s</span> <span class="o">=</span> <span class="kt">MyType</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">sData</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">JSONEncoder</span><span class="p">()</span><span class="o">.</span><span class="nf">encode</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
</code></pre></div></div>
<p>There are also some configuration options available on <code class="language-plaintext highlighter-rouge">JSONEncoder</code>, such as <a href="https://developer.apple.com/documentation/foundation/jsonencoder/2895284-outputformatting"><code class="language-plaintext highlighter-rouge">outputFormatting</code></a> and <a href="https://developer.apple.com/documentation/foundation/jsonencoder/2949141-keyencodingstrategy"><code class="language-plaintext highlighter-rouge">keyEncodingStrategy</code></a>. But that’s about it.</p>
<p>On the other hand, the <code class="language-plaintext highlighter-rouge">Encoder</code> protocol requires a number of methods that provide <em>containers</em> for encoding values in:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">container</span><span class="o"><</span><span class="kt">Key</span><span class="p">:</span> <span class="kt">CodingKey</span><span class="o">></span><span class="p">(</span><span class="n">keyedBy</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">Key</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="o">-></span> <span class="kt">KeyedEncodingContainer</span><span class="o"><</span><span class="kt">Key</span><span class="o">></span>
<span class="kd">func</span> <span class="nf">singleValueContainer</span><span class="p">()</span> <span class="o">-></span> <span class="kt">SingleValueEncodingContainer</span>
<span class="kd">func</span> <span class="nf">unkeyedContainer</span><span class="p">()</span> <span class="o">-></span> <span class="kt">UnkeyedEncodingContainer</span>
</code></pre></div></div>
<p>And those container types are themselves protocols, which provide their own various methods for storing values in them, like this:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">SingleValueEncodingContainer</span> <span class="p">{</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">encode</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">UInt16</span><span class="p">)</span> <span class="k">throws</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">encode</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int64</span><span class="p">)</span> <span class="k">throws</span>
<span class="c1">// ... repeat for several primitive Swift types</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The methods on <code class="language-plaintext highlighter-rouge">Encoder</code> and <a href="https://developer.apple.com/documentation/swift/singlevalueencodingcontainer"><code class="language-plaintext highlighter-rouge">SingleValueEncodingContainer</code></a> (and the other container types, too) are meant to be used by <code class="language-plaintext highlighter-rouge">Encodable</code> types in their <code class="language-plaintext highlighter-rouge">encode(to:)</code> implementations to customize how exactly they’re encoded. I’ll talk about that more in a future blog post, but for now you can read about it <a href="https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types">in the Swift documentation</a> (see “Encoding and Decoding Manually”).</p>
<p>One of the authors of <code class="language-plaintext highlighter-rouge">Codable</code>, <a href="https://itaiferber.net/">Itai Ferber</a>, confirmed the reasoning behind the design:
<a class="embedly-card" href="https://www.reddit.com/r/swift/comments/a8jden/why_dont_jsonencoder_and_jsondecoder_conform_to/ecblk1e">Card</a>
<script async="" src="//embed.redditmedia.com/widgets/platform.js" charset="UTF-8"></script></p>
<h2 id="conclusion">Conclusion</h2>
<p>In short, <code class="language-plaintext highlighter-rouge">JSONEncoder</code> and <code class="language-plaintext highlighter-rouge">JSONDecoder</code> use internal types conforming to <code class="language-plaintext highlighter-rouge">Encoder</code> and <code class="language-plaintext highlighter-rouge">Decoder</code> in order to split up portions of the encoding/decoding APIs, based on how and where you use them.</p>
<p>I hope you found this post helpful! Please feel free to ask questions or give feedback via <a href="https://www.twitter.com/k__mahar">Twitter</a> or <a href="mailto:kaitlinmahar@gmail.com">email</a>.</p>A common point of confusion in the Swift encoding and decoding ecosystem is that there is an Encoder protocol, but standard library types like JSONEncoder and PropertyListEncoder don’t conform to it. We encounter the Encoder protocol when we make our types conform to Encodable: /// A type that can encode itself to an external representation. public protocol Encodable { /// Encodes this value into the given encoder. public func encode(to encoder: Encoder) }What does open mean in Swift?2018-12-29T03:00:00+00:002018-12-29T03:00:00+00:00https://kaitlin.dev/2018/12/29/open-swift<p>You may have encountered an <code class="language-plaintext highlighter-rouge">open class</code> in a library you use, or maybe you’ve considered using one within your own code. <code class="language-plaintext highlighter-rouge">open</code> provides an additional access control level that’s even less restrictive than <code class="language-plaintext highlighter-rouge">public</code>. But what exactly does <code class="language-plaintext highlighter-rouge">open</code> mean, and how is it different from <code class="language-plaintext highlighter-rouge">public</code>?</p>
<p>Here’s what you need to know about <code class="language-plaintext highlighter-rouge">open</code>:</p>
<h4 id="1-open-classes-can-be-subclassed-within-modules-that-import-them">1. <code class="language-plaintext highlighter-rouge">open</code> classes can be subclassed within modules that import them.</h4>
<p>In contrast, <code class="language-plaintext highlighter-rouge">public</code> classes <em>cannot</em> be subclassed in modules that import them.</p>
<p>So if I have <code class="language-plaintext highlighter-rouge">ModuleA</code> with the following classes defined:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">open</span> <span class="kd">class</span> <span class="kt">ClassA</span> <span class="p">{}</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="kt">ClassB</span> <span class="p">{}</span>
</code></pre></div></div>
<p>And I try to subclass them in my own module <code class="language-plaintext highlighter-rouge">ModuleB</code>:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">ModuleA</span>
<span class="c1">// this compiles just fine.</span>
<span class="kd">class</span> <span class="kt">SubclassA</span><span class="p">:</span> <span class="kt">ClassA</span> <span class="p">{}</span>
<span class="c1">// this errors.</span>
<span class="kd">class</span> <span class="kt">SubclassB</span><span class="p">:</span> <span class="kt">ClassB</span> <span class="p">{}</span>
</code></pre></div></div>
<p>My subclass of the <code class="language-plaintext highlighter-rouge">open class</code> works just fine - but when I try to subclass the <code class="language-plaintext highlighter-rouge">public class</code>, I get <code class="language-plaintext highlighter-rouge">error: cannot inherit from non-open class 'ClassB' outside of its defining module</code>.</p>
<h4 id="2-open-class-members-can-be-overridden-within-modules-that-import-their-classes">2. <code class="language-plaintext highlighter-rouge">open</code> class members can be overridden within modules that import their classes.</h4>
<p>(By “class members”, I mean <code class="language-plaintext highlighter-rouge">class</code> properties and methods.)</p>
<p>In contrast, <code class="language-plaintext highlighter-rouge">public</code> class members <em>cannot</em> be subclassed in modules that import their classes.</p>
<p>Let’s return to our previous <code class="language-plaintext highlighter-rouge">open class</code> and add a method to it:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">open</span> <span class="kd">class</span> <span class="kt">ClassA</span> <span class="p">{</span>
<span class="kd">open</span> <span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"foo in base class"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, when we import <code class="language-plaintext highlighter-rouge">ModuleA</code> into our own module, we can override <code class="language-plaintext highlighter-rouge">foo</code> in our subclass:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">ModuleA</span>
<span class="kd">class</span> <span class="kt">SubclassA</span><span class="p">:</span> <span class="kt">ClassA</span> <span class="p">{</span>
<span class="k">override</span> <span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"foo in subclass"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="3-class-members-of-an-open-class-dont-have-to-be-open-too-and-theyre-internal-by-default">3. Class members of an <code class="language-plaintext highlighter-rouge">open</code> class don’t have to be <code class="language-plaintext highlighter-rouge">open</code> too, and they’re <code class="language-plaintext highlighter-rouge">internal</code> by default.</h4>
<p>Just like with members of <code class="language-plaintext highlighter-rouge">public</code> classes, if you don’t specify the access control level for a member of an <code class="language-plaintext highlighter-rouge">open</code> class, it will default to <code class="language-plaintext highlighter-rouge">internal</code>.</p>
<p>If <code class="language-plaintext highlighter-rouge">ClassA</code> looked like this:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">open</span> <span class="kd">class</span> <span class="kt">ClassA</span> <span class="p">{</span>
<span class="kd">open</span> <span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"foo in base class"</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"bar in base class"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I could override <code class="language-plaintext highlighter-rouge">foo</code> within <code class="language-plaintext highlighter-rouge">ModuleB</code> just fine like before, but trying to override <code class="language-plaintext highlighter-rouge">bar</code> would give me <code class="language-plaintext highlighter-rouge">error: method does not override any method from its superclass</code>, since there is no public or open method <code class="language-plaintext highlighter-rouge">bar</code>that we know about from within <code class="language-plaintext highlighter-rouge">ModuleB</code>.</p>
<p>We also could’ve marked <code class="language-plaintext highlighter-rouge">bar</code> with an explicit level that wasn’t <code class="language-plaintext highlighter-rouge">open</code>, like <code class="language-plaintext highlighter-rouge">private</code> or <code class="language-plaintext highlighter-rouge">public</code>, and the typical rules for those access levels would apply.</p>
<h4 id="4-open-only-applies-to-classes-and-class-members">4. <code class="language-plaintext highlighter-rouge">open</code> only applies to classes and class members.</h4>
<p>Since <code class="language-plaintext highlighter-rouge">open</code> describes behavior related to inheritance, it only makes sense to use it with types that support inheritance - in Swift, that’s just classes!</p>
<h4 id="5-think-carefully-before-marking-anything-in-your-library-open">5. Think carefully before marking anything in your library open.</h4>
<p>There’s a reason that <code class="language-plaintext highlighter-rouge">open</code> is not the default - it should only be used when you’re <em>sure</em> that you want your users to be able to do what it allows.</p>
<p>The Swift language guide states:</p>
<blockquote>
<p>Marking a class as open explicitly indicates that you’ve considered the impact of code from other modules using that class as a superclass, and that you’ve designed your class’s code accordingly.</p>
</blockquote>
<p>Basically, unless if there is a good, concrete reason for users to subclass your class, don’t allow it! Once you make a library class <code class="language-plaintext highlighter-rouge">open</code>, changing it back to <code class="language-plaintext highlighter-rouge">public</code> becomes a breaking API change, as users may have already implemented subclasses that would no longer be allowed.</p>
<p>It’s worth noting that it’s used very sparingly in the Swift standard library – I only count <a href="https://github.com/search?l=Swift&q=%22open+class%22+repo%3Aapple%2Fswift+path%3Astdlib%2F&type=Code">6 occurrences</a> of <code class="language-plaintext highlighter-rouge">open class</code>.</p>
<h4 id="in-conclusion">In conclusion</h4>
<p>Making a class and its members <code class="language-plaintext highlighter-rouge">open</code> can be a useful tool if you want the ability to subclass/override outside of the module where the class is defined, but use it carefully!</p>
<p>For more information, I recommend reading the Swift language guide <a href="https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html">chapter on access control</a>.</p>
<p>Please let me know any thoughts or questions you have via Twitter or email!</p>You may have encountered an open class in a library you use, or maybe you’ve considered using one within your own code. open provides an additional access control level that’s even less restrictive than public. But what exactly does open mean, and how is it different from public?Custom error messages in Swift 42018-05-09T14:00:00+00:002018-05-09T14:00:00+00:00https://kaitlin.dev/2018/05/09/custom-errors<p>I always took error types with custom messages for granted in other languages, so I was a bit surprised at the amount of work I had to do to implement them in Swift. I had to spend a while digging through outdated Stack Overflow posts before coming upon this solution, so hopefully this will be useful to you!</p>
<p>The <a href="https://developer.apple.com/documentation/swift/error">Swift documentation</a> on the <a href="https://developer.apple.com/documentation/swift/error"><code class="language-plaintext highlighter-rouge">Error</code> protocol</a> spends a while discussing how to add associated values to error <code class="language-plaintext highlighter-rouge">enum</code>s, and even lists</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">localizedDescription</span><span class="p">:</span> <span class="kt">String</span>
</code></pre></div></div>
<p>as part of the protocol. Yet, neither the associated values nor the <code class="language-plaintext highlighter-rouge">localizedDescription</code> are actually printed out when we throw an error! See the following example:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">XCTest</span>
<span class="kd">enum</span> <span class="kt">MyError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">first</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">second</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">localizedDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">return</span> <span class="s">"Some description here!"</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">ErrorTest</span><span class="p">:</span> <span class="kt">XCTestCase</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">var</span> <span class="nv">allTests</span><span class="p">:</span> <span class="p">[(</span><span class="kt">String</span><span class="p">,</span> <span class="p">(</span><span class="kt">ErrorTest</span><span class="p">)</span> <span class="o">-></span> <span class="p">()</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)]</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="p">(</span><span class="s">"testMyError"</span><span class="p">,</span> <span class="n">testMyError</span><span class="p">)</span>
<span class="p">]</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">testMyError</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span>
<span class="k">throw</span> <span class="kt">MyError</span><span class="o">.</span><span class="nf">first</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="s">"first error"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This looks like it should print the associated messages, or maybe the <code class="language-plaintext highlighter-rouge">localizedDescription</code>, when we throw the error. And yet, my output, running the test from the command line:</p>
<p><code class="language-plaintext highlighter-rouge">failed: caught error: The operation couldn’t be completed.(MyTests.MyError error 0.) </code></p>
<p>While it does tell me that the first error case was thrown (<code class="language-plaintext highlighter-rouge">error 0</code>), I don’t get any of the useful information I worked so hard to store with my error!</p>
<p>The solution I found for this was that, rather than implementing <code class="language-plaintext highlighter-rouge">Error</code>, you can implement the <a href="https://developer.apple.com/documentation/foundation/localizederror"><code class="language-plaintext highlighter-rouge">LocalizedError</code></a> protocol instead (which inherits from <code class="language-plaintext highlighter-rouge">Error</code>.) This protocol specifies a property:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">errorDescription</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
</code></pre></div></div>
<p>which is printed out when the error is thrown.</p>
<p>Modifying the previous example to implement <code class="language-plaintext highlighter-rouge">LocalizedError</code>, we get:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">MyError</span><span class="p">:</span> <span class="kt">LocalizedError</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">first</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">second</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">errorDescription</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span> <span class="k">return</span> <span class="s">"Some description here!"</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>When we run the same test as before, the output is now:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>failed: caught error: Some description here!
</code></pre></div></div>
<p>Finally, what if we want to take advantage of our error’s associated values to give better error messages? We can get fancier with the description, and do something like:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">MyError</span><span class="p">:</span> <span class="kt">LocalizedError</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">first</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">second</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">errorDescription</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
<span class="k">switch</span> <span class="k">self</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">first</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">second</span><span class="p">(</span><span class="n">message</span><span class="p">):</span>
<span class="k">return</span> <span class="n">message</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, when we run the test, we get:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>failed: caught error: first error
</code></pre></div></div>
<p>Exactly the message we specified when we originally threw the error!</p>
<p>Thanks for reading, and let me know any questions or feedback you have via Twitter or email 🙂</p>I always took error types with custom messages for granted in other languages, so I was a bit surprised at the amount of work I had to do to implement them in Swift. I had to spend a while digging through outdated Stack Overflow posts before coming upon this solution, so hopefully this will be useful to you!