<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Don Mowry</title>
    <description>© Don Mowry</description>
    <link>https://www.donmowry.com/</link>
    <atom:link href="https://www.donmowry.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Thu, 07 Nov 2019 06:37:02 +0000</pubDate>
    <lastBuildDate>Thu, 07 Nov 2019 06:37:02 +0000</lastBuildDate>
    <generator>Jekyll v3.8.5</generator>
    
      <item>
        <title>Edutudu Part III: Vapor API</title>
        <description>&lt;p&gt;In the &lt;a href=&quot;/edutudu-part-ii-vapor-data&quot;&gt;last post&lt;/a&gt; I created a simple model of data and compiled the app, but I commented out a portion of the templated code to do so. This portion dealt with persistence of the data and routing the data through a controller. My original intent was to hook into a MySQL database, but I think for the purposes of this exercise, I am going to keep the SQLite database. The principle is the same, and I may return to enhance the persistence later.  For now, though, I’m going to continue by fleshing out the web API.&lt;/p&gt;

&lt;p&gt;From a cursory reading, the routing and controllers in Vapor seem similar in concept to Rails. Rails has &lt;a href=&quot;https://guides.rubyonrails.org/active_record_basics.html&quot;&gt;Active Record&lt;/a&gt; to save, load, and query data, while Vapor has Fluent.  Fluent doesn’t seem to abstract the database driver away completely as we have to use specific protocols for specific drivers.  So, for instance, to persist and instance of a class in SQLite, the model must declare conformance to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SQLiteModel&lt;/code&gt;.  I’d rather have a backing store-agnostic interface, but that’s not what Fluent provides.&lt;/p&gt;

&lt;p&gt;I had created the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo&lt;/code&gt; struct with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt; id property, thinking I’d use a UUID and convert it to a string. However, the Fluent SQLite package provides a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SQLiteUUIDModel&lt;/code&gt; protocol that expects a UUID id field.  This is great, and I’ll change the struct to match.&lt;/p&gt;

&lt;p&gt;According to &lt;a href=&quot;https://docs.vapor.codes/3.0/fluent/models/&quot;&gt;the documentation&lt;/a&gt;, “[b]oth structs and classes can conform to Model, however you must pay special attention to Fluent’s return types if you use a struct.”  This got me to thinking: I want a common definition for the data, but perhaps the individual apps should decide the best way to provide it? Should my common &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct&lt;/code&gt;s be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protocol&lt;/code&gt;s instead?&lt;/p&gt;

&lt;p&gt;In designing an architecture for an app at work, I did exactly that. I created a library of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protocol&lt;/code&gt;s to decouple the interface from the implementation and allow a plug-in type of component-based architecture. However, here, I believe that sticking to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct&lt;/code&gt; is better.  A big reason is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Codable&lt;/code&gt; interface. It is simple to translate from JSON and back. This being an educational side project, I don’t need additional complexity, for the same reasons I’m going with SQLite.&lt;/p&gt;

&lt;p&gt;In a real application, I would spend more time designing this layer and do things differently.  One way might be to use protocols, and have a field that held the version of the data structure. This would allow encoding and decoding by switching on that version. Or perhaps the encoders and decoders are provided by the library, and the JSON only goes through there.  Designing for migration is essential in a production application.  For this project, not so much.&lt;/p&gt;

&lt;p&gt;Thus, my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo.swift&lt;/code&gt; becomes:&lt;/p&gt;
&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;createdOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;updatedOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TodoStatus&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Priority&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dueDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo+Vapor.swift&lt;/code&gt; becomes:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Vapor&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FluentSQLite&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// Allows `Todo` to be encoded to and decoded from HTTP messages.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// A single entry of a Todo list.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;SQLiteUUIDModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// Allows `Todo` to be used as a dynamic migration.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Migration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// Allows `Todo` to be used as a dynamic parameter in route definitions.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Parameter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m going to create a &lt;a href=&quot;https://restfulapi.net&quot;&gt;RESTful API&lt;/a&gt;.  Like Rails and other web app frameworks, Vapor makes this very simple. In fact, last time I commented out a bunch of generated code that supported the CRUD operations on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo&lt;/code&gt;s in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TodoController.swift&lt;/code&gt;.  I can uncomment that to get started, along with support in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;routes.swift&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point I run the app to see how things are going. I’m hit with a somewhat cryptic error:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⚠️ DecodingError: Cannot initialize TodoStatus from invalid String value 1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Huh?&lt;/p&gt;

&lt;p&gt;Fortunately, &lt;a href=&quot;https://github.com/vapor/vapor/issues/1736&quot;&gt;Google provides&lt;/a&gt;. The enum &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TodoStatus&lt;/code&gt; needs to conform to &lt;a href=&quot;https://api.vapor.codes/core/latest/Core/Protocols/ReflectionDecodable.html&quot;&gt;ReflectionDecodable&lt;/a&gt;.  Of course in a certain way, this makes sense. An ORM needs to use reflection to inspect and understand objects so that it can create storage and map the object to the backing data store.  In another way, though, I’m still confused: the documentation explicitly states that there are types that are already made to conform to ReflectionDecodable, and that one is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt;.  And yet an enum of Strings needs explicit conformance. Well, if it must be, it must be:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Vapor&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TodoStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;ReflectionDecodable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reflectDecoded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;TodoStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TodoStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another change that I’ve made is to store the SQLite file rather than keep it in-memory.  This will allow us to keep the data between application runs.  It’s a simple change in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configure.swift&lt;/code&gt; to provide a path to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SQLiteDatabase&lt;/code&gt; initializer.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let sqlite = try SQLiteDatabase(storage: .file(path: &quot;edutudu.sqlite&quot;))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Running again, and going to &lt;a href=&quot;http://localhost:8080/todos&quot;&gt;http://localhost:8080/todos&lt;/a&gt; shows us a list of all none of our Todos. Let’s try to POST one to see it show up in our list.&lt;/p&gt;

&lt;p&gt;There’s no basic form generated in our scaffolding.  Vapor uses its own templating language, &lt;a href=&quot;https://docs.vapor.codes/3.0/leaf/getting-started/&quot;&gt;Leaf&lt;/a&gt; for this, but I’m going to defer that for now.  We’ll just POST using cURL.&lt;/p&gt;

&lt;p&gt;Again, I’ll mention a difference between this exploratory exercise and a real application.  This app is wide open, and in the real world we’d have some protection around it. Vapor provides &lt;a href=&quot;https://docs.vapor.codes/3.0/auth/getting-started/&quot;&gt;Auth&lt;/a&gt; for user authentication, which would be a must.&lt;/p&gt;

&lt;p&gt;To create a Todo, we can just POST JSON using cURL to our local address.  As I type it out, I’m wondering why I added so many fields 🤓. Also, I could handle different date formats, but ISO 8601 is fine by me.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{&quot;id&quot;: &quot;cdefa388-581f-4dec-b1ea-6ad3019d6271&quot;, &quot;title&quot;: &quot;My first ToDo, by Fisher Price&quot;, &quot;createdOn&quot;: &quot;2019-11-07T05:50:39Z&quot;, &quot;updatedOn&quot;: &quot;2019-11-07T05:50:39Z&quot;, &quot;status&quot;: &quot;open&quot;, &quot;priority&quot;: &quot;high&quot;}'&lt;/span&gt; http://localhost:8080/todos
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we get a response!&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;open&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;CDEFA388-581F-4DEC-B1EA-6AD3019D6271&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;title&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;My first ToDo, by Fisher Price&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;createdOn&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;2019-11-07T05:50:39Z&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;priority&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;high&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;updatedOn&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;2019-11-07T05:50:39Z&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Going back to &lt;a href=&quot;http://localhost:8080/todos&quot;&gt;http://localhost:8080/todos&lt;/a&gt;, we still see no Todos, though. Opening the database and querying, we see nothing.  The reason is that I’ve supplied an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt;, which of course is not necessary as the ORM will be handling the creation of that on insertion.  It’s strange that we don’t get an error, but instead get the JSON mirrored back to us.  Anyway, let’s try again:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{&quot;title&quot;: &quot;My first ToDo, by Fisher Price&quot;, &quot;createdOn&quot;: &quot;2019-11-07T05:50:39Z&quot;, &quot;updatedOn&quot;: &quot;2019-11-07T05:50:39Z&quot;, &quot;status&quot;: &quot;open&quot;, &quot;priority&quot;: &quot;high&quot;}'&lt;/span&gt; http://localhost:8080/todos
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The response:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;open&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;0A927E2B-C003-4FEB-8884-B459213B8623&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;title&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;My first ToDo, by Fisher Price&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;createdOn&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;2019-11-07T05:50:39Z&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;priority&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;high&quot;&lt;/span&gt;,&lt;span class=&quot;s2&quot;&gt;&quot;updatedOn&quot;&lt;/span&gt;:&lt;span class=&quot;s2&quot;&gt;&quot;2019-11-07T05:50:39Z&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now refreshing the page we see all that glorious plain HTML:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[{&quot;status&quot;:&quot;open&quot;,&quot;id&quot;:&quot;0A927E2B-C003-4FEB-8884-B459213B8623&quot;,&quot;title&quot;:&quot;My first ToDo, by Fisher Price&quot;,&quot;createdOn&quot;:&quot;2019-11-07T05:50:39Z&quot;,&quot;priority&quot;:&quot;high&quot;,&quot;updatedOn&quot;:&quot;2019-11-07T05:50:39Z&quot;}]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s all for now.  Next time we’ll look at pulling some code into a Swift Package.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/donmowry/edutudu/tree/vapor-iii&quot;&gt;Code for this post&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 07 Nov 2019 05:07:10 +0000</pubDate>
        <link>https://www.donmowry.com/edutudu-part-iii-vapor-persistence</link>
        <guid isPermaLink="true">https://www.donmowry.com/edutudu-part-iii-vapor-persistence</guid>
        
        
        <category>programming</category>
        
        <category>ios</category>
        
        <category>edutudu</category>
        
      </item>
    
      <item>
        <title>Edutudu Part II: Vapor Data</title>
        <description>&lt;p&gt;I love data.  Data is the lifeblood of any application. Table views? Full of data.  Email? Nothing but data. What is Instagram the app but a shell to hold the data of pictures and posts? I love a great UI. Native platforms have the best ability to create smooth, interactive, natural experiences and fluid animations that can be just plain gorgeous. Developing these UIs is deeply satisfying. But that UI needs to be filled with data, or it’s just a lifeless picture frame.&lt;/p&gt;

&lt;p&gt;The To Do app needs some data definition.  The Todo class that was created by the Vapor template needs to be expanded on, and some relations build around it. We need to step back and think about the final app, what structures we need, and the properties of those structures.&lt;/p&gt;

&lt;p&gt;I want to keep this application minimalist, not get overloaded with details at any one level, so that I can go full stack.  Once I’ve achieved that, I can go back in and start adding things, areas that I want to learn more about (in keeping with the purpose of the project) or just areas that are interesting. To that end, I’m going to keep the number and complexity of the objects simple.&lt;/p&gt;

&lt;p&gt;At the very least, a To Do app is going to need something to represent the item to be done. It should also have a person that has things To Do, a User.&lt;/p&gt;

&lt;p&gt;Quick aside: I’ve grown to dislike the word “User” to describe a person using an application.  I’m fortunate in my day job that it is a teaching application, so we can talk about “Learners” in general, instead of Users.  However, even our data tables and objects use the term “User”, so I’m not quite sure there’s a better term.&lt;/p&gt;

&lt;p&gt;A Todo will need a properties to hold the text of the item, completion status, a creation date, update date, and let’s put in an optional due date and optional details for use in exercising a future UI.  Let’s also give it a &lt;a href=&quot;http://www.43folders.com/2009/04/28/priorities&quot;&gt;priority&lt;/a&gt; to torture ourselves.&lt;/p&gt;

&lt;p&gt;A User will have a name, creation date, update date, and will have a list of Todos. Both of these will need unique Ids to, well, uniquely identify them in the database.&lt;/p&gt;

&lt;p&gt;Vapor applications model data objects using a protocol: &lt;a href=&quot;https://docs.vapor.codes/3.0/getting-started/content/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content&lt;/code&gt;&lt;/a&gt;. This in turn conforms to three protocols: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Codable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RequestDecodable&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ResponseEncodable&lt;/code&gt;.  The first is the Swift protocol we already know and love.  The second two allow Vapor to send and receive objects in HTTP requests and responses.  However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content&lt;/code&gt; provides default implementations of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RequestDecodable&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ResponseEncodable&lt;/code&gt; functions.  This means that the only requirement for a data object to be marked as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Contnet&lt;/code&gt; is that it conforms to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Codable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since I eventually want to pull the data and any business logic into a Swift package, I’m going to begin with that in mind and create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct&lt;/code&gt;s in a group, and the Vapor conformance in another group:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/adding-todo-models.png&quot; alt=&quot;model creation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I can reuse the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo.swift&lt;/code&gt; file in the App &amp;gt; Models group.  This is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; by default, and I’m not sure I need the reference semantics here - could this be something that Vapor needs? I don’t know, but I’ll find out! Because I’m going to start with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;struct&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;createdOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;updatedOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;TodoStatus&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Priority&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dueDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then, I can rename the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo.swift&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo+Vapor.swift&lt;/code&gt; and replace the contents with this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Vapor&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;/// Allows `Todo` to be encoded to and decoded from HTTP messages.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;At this point, I’m also going to comment out all of the database, controller, and route information having to do with Todos and TodoControllers.  By default, the template is using an in-memory SQLite store, and I’m going to address that in a future post.&lt;/p&gt;

&lt;p&gt;I can run, it all compiles, and the home page displays “It works!”  Indeed it does, little vapor app.  It just doesn’t do much yet.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/donmowry/edutudu/tree/vapor-ii&quot;&gt;Code for this post&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 26 Sep 2019 00:27:10 +0000</pubDate>
        <link>https://www.donmowry.com/edutudu-part-ii-vapor-data</link>
        <guid isPermaLink="true">https://www.donmowry.com/edutudu-part-ii-vapor-data</guid>
        
        
        <category>programming</category>
        
        <category>ios</category>
        
        <category>edutudu</category>
        
      </item>
    
      <item>
        <title>Edutudu Part I: Vapor</title>
        <description>&lt;p&gt;In writing a full stack To Do app for my own education, an Edutudu, I am going to start with a Vapor web service.  This of course raises two questions: why start with a web service, and why Vapor?&lt;/p&gt;

&lt;p&gt;The most well known Swift web frameworks right now are &lt;a href=&quot;https://vapor.codes&quot;&gt;Vapor&lt;/a&gt; and &lt;a href=&quot;https://www.kitura.io&quot;&gt;Kitura&lt;/a&gt;.  There are &lt;a href=&quot;https://www.raywenderlich.com/1323930-vapor-vs-kitura-choosing-a-server-side-swift-framework&quot;&gt;good&lt;/a&gt; &lt;a href=&quot;https://www.hackingwithswift.com/articles/73/server-side-swift-kitura-vs-vapor&quot;&gt;comparisons&lt;/a&gt; around, and plenty of tutorials on each. Either is a good option.  What sold me on Vapor is mostly &lt;a href=&quot;https://docs.vapor.codes/3.0/fluent/getting-started/&quot;&gt;Fluent&lt;/a&gt;, Vapor’s ORM framework. Vapor also has a &lt;a href=&quot;https://docs.vapor.codes/3.0/getting-started/toolbox/&quot;&gt;command line tool&lt;/a&gt; that allows quick creation of an Xcode project to house the Vapor application.&lt;/p&gt;

&lt;p&gt;This is the second reason I am starting with Vapor, though it is minor. Rather than create projects and integrate them later, I’m starting with the project generated by the toolbox, and will base a workspace off of it.  There’s not a hugely compelling reason to do this, but I do need a place to start and this one option.&lt;/p&gt;

&lt;p&gt;I’m going to be following the &lt;a href=&quot;https://docs.vapor.codes/3.0/getting-started/hello-world/&quot;&gt;Vapor docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first step is installing the Vapor Toolbox via Homebrew.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;brew tap vapor/tap
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;vapor/tap/vapor&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we’ll create a new site:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vapor new EduTuDuWeb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And an Xcode project:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;EduTuDuWeb
vapor xcode
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the project, select the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt; schema on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;My Mac&lt;/code&gt;, open http://localhost:8080/hello and there we are!&lt;/p&gt;

&lt;p&gt;Hello, world!&lt;/p&gt;

&lt;p&gt;Now, this isn’t all that much of an accomplishment.  Following a recipe, using a tool, and having a template generate a project.  But it’s a good start, and a base to begin with.  Next time, we’ll start customizing.&lt;/p&gt;

&lt;p&gt;I’m not yet sure it’s accurate, but I think of Vapor like a younger kind of Rails or Django, a framework and scaffolding for creating web applications quickly. I’m familiar with Ruby on Rails, so I’m hoping this is an easy leap to make.&lt;/p&gt;

&lt;p&gt;Vapor has already given me a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;routes.swift&lt;/code&gt; with some routes defined, a model, and a controller. The funny thing is that the default model is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Todo&lt;/code&gt;.  Either this means “to-do” as in, here’s a placeholder for your model, or To Do apps are a common Hello World. Maybe both.  Maybe they knew I was on my way over.&lt;/p&gt;

&lt;p&gt;I don’t yet have a database set up, and one point of my exercise is to pull common data structures together.  So I’ll be adapting the Todo class in Part II.&lt;/p&gt;
</description>
        <pubDate>Wed, 18 Sep 2019 02:17:10 +0000</pubDate>
        <link>https://www.donmowry.com/edutudu-part-i-vapor</link>
        <guid isPermaLink="true">https://www.donmowry.com/edutudu-part-i-vapor</guid>
        
        
        <category>programming</category>
        
        <category>ios</category>
        
        <category>edutudu</category>
        
      </item>
    
      <item>
        <title>iOS 13 New Tech</title>
        <description>&lt;p&gt;This &lt;a href=&quot;/seismic_shift&quot;&gt;massive&lt;/a&gt; &lt;a href=&quot;https://developer.apple.com/videos/wwdc2019/&quot;&gt;WWDC&lt;/a&gt; introduced a number of new APIs and tools, such as &lt;a href=&quot;https://developer.apple.com/xcode/swiftui/&quot;&gt;SwiftUI&lt;/a&gt;, &lt;a href=&quot;https://developer.apple.com/documentation/combine&quot;&gt;Combine&lt;/a&gt;, and &lt;a href=&quot;https://developer.apple.com/documentation/swift_packages&quot;&gt;Swift Package Manager support in Xcode&lt;/a&gt;.  In addition, there are other community efforts like &lt;a href=&quot;https://vapor.codes&quot;&gt;Vapor&lt;/a&gt; and &lt;a href=&quot;https://www.kitura.io&quot;&gt;Kitura&lt;/a&gt; that I have not have time nor cause to use.&lt;/p&gt;

&lt;p&gt;I like to stay up to date on frameworks and APIs.  Although they may not have direct impact on my day to day job, developing that familiarity with them has indirect benefit in the form of stretching my thinking and sharpening my learning. Adding them to my toolkit means that when there is a point at which they are a good solution, I can recognize that and have at least some baseline knowledge to go off of.&lt;/p&gt;

&lt;p&gt;There are enough new things that are different, that this year I am going to take a different approach.  I’m going to bring as many of these new things as I can together in a way that has structure and purpose, a way to direct my learning.&lt;/p&gt;

&lt;p&gt;Yes, I’m writing a To Do App.&lt;/p&gt;

&lt;p&gt;Almost like a Hello World of projects, it seems like a to do app is a common way to have a simple project for a developer to learn with.  For mine, I’m going to have a Swift Package that contains the data model and logic, a Vapor web server, and macOS, iOS, and watchOS apps that all use the model package.  I’ll use SwiftUI for the front end, and share parts of the UI across targets.&lt;/p&gt;

&lt;p&gt;I have a lot of dreams and projects I believe I’ll get to, but which are overtaken by actual responsibilities.  I know I’m not alone in this regard.  And here is yet another.  But this is for me and my edification, so there is little pressure.  The difference is this time I decided to share this process as I go along.&lt;/p&gt;

&lt;p&gt;Introducing EduTuDu: the Series.&lt;/p&gt;

&lt;p&gt;A goofy name should help keep it light in my mind and clear this is for Edu-cation.  That’s not to say that I won’t be trying to include everything that a real product would include, but that’s not my purpose. I just want a structure to learn around.&lt;/p&gt;

&lt;p&gt;As a kind of Step 0, I’ve created a &lt;a href=&quot;https://github.com/donmowry/edutudu&quot;&gt;GitHub project&lt;/a&gt; to work with, trying to tag the work I do in each blog post.  This is to try to keep to the &lt;a href=&quot;/big-shop-process-for-the-small-shop&quot;&gt;principles&lt;/a&gt; laid out before. I’m using an MIT license, since I believe that is most fully inline with my goals and values.&lt;/p&gt;

&lt;p&gt;Next up, Part I.&lt;/p&gt;
</description>
        <pubDate>Tue, 17 Sep 2019 02:17:10 +0000</pubDate>
        <link>https://www.donmowry.com/ios-13-new-tech</link>
        <guid isPermaLink="true">https://www.donmowry.com/ios-13-new-tech</guid>
        
        
        <category>programming</category>
        
        <category>ios</category>
        
        <category>edutudu</category>
        
      </item>
    
      <item>
        <title>Big Shop Process for the Small Shop</title>
        <description>&lt;p&gt;While there’s a lot of discussion of programming tips and tricks, of the latest APIs and libraries, I wanted to discuss a little of the practice of software engineering, of the scaffolding around the procedure of writing code that helps to enable that code to go out into the world and help the people that use it.  There are number of processes in place in bigger development shops which are also of benefit to the small, or one-person, firm. Some of these are basic, others may seem overkill for one person.  You don’t have to do them all, or do them as I lay out here.  However, done well, they help the generation and smooth the delivery of quality code.&lt;/p&gt;

&lt;p&gt;Like a great many developers, I work full time but also do some programming at home. I do this to keep sharp, explore new techniques, use APIs that I may not get a chance to at work, and just to have something of my own to polish and refine. I have other hobbies, but programming is definitely one of them.  This article comes from my experience in applying the procedures I use in my day-to-day job to my own projects at home. I believe it would equally apply to a single-proprietor shop, a freelance contractor, or a small group.&lt;/p&gt;

&lt;p&gt;Let’s start with the basics.&lt;/p&gt;

&lt;h2 id=&quot;version-control&quot;&gt;Version Control&lt;/h2&gt;
&lt;p&gt;I’m just going to assume that you’re doing this. Version control, whether using Git, Mecurial, or even SVN or CVS, is a fundamental tool of the engineer. The main reason that I mention it is that I’ll be using Git throughout this discussion. You may be using another tool, and the topics still apply though the details may change.&lt;/p&gt;

&lt;p&gt;Make sure that code is committed regularly, with good comments. Don’t be the one that future-you is cursing out later because you can’t find a commit that caused a crash.&lt;/p&gt;

&lt;h3 id=&quot;off-site&quot;&gt;Off-site&lt;/h3&gt;

&lt;p&gt;Even for the small or one-person firm, off-site storage of code is important.  Like backing up your valued data on your computer, backing up your code off-site increases the safety and reliability of your storage. This doesn’t mean Dropbox (though that’s a good second line of defense). We’re talking about version control. There are many providers, such as &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;, &lt;a href=&quot;https://gitlab.com&quot;&gt;GitLab&lt;/a&gt;, and &lt;a href=&quot;https://beanstalkapp.com&quot;&gt;Beanstalk&lt;/a&gt;, that offer free or low price hosting for small projects.  You can see a larger list at &lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_of_source-code-hosting_facilities&quot;&gt;Wikipedia&lt;/a&gt;.  There’s no reason not to do this. I’m using &lt;a href=&quot;https://bitbucket.org/product/&quot;&gt;Bitbucket&lt;/a&gt;, for example, on a side project.&lt;/p&gt;

&lt;h2 id=&quot;git-flow&quot;&gt;Git Flow&lt;/h2&gt;

&lt;p&gt;In his &lt;a href=&quot;https://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;seminal post&lt;/a&gt;, &lt;a href=&quot;https://nvie.com&quot;&gt;Vincent Driessen&lt;/a&gt; laid out a model for git branching that came to be known as Git Flow.  It’s since been codified into a tool called git-flow that’s available on many platforms, such as on macOS through Homebrew.&lt;/p&gt;

&lt;p&gt;However even if you don’t install the tooling, the concepts are sound and can be used without it.  At its simplest core, the key points are that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;develop&lt;/code&gt; is the branch representing the current in-progress code, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; is only released code, and all work happens on branches merged to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;develop&lt;/code&gt;.  If nothing else, this is a minimum version control model to follow.&lt;/p&gt;

&lt;p&gt;What is most important is that there is a process, and that it is followed.  It’s very simple for a single developer to skip process since there is no one else to check that it is followed. Discipline is part of a successful single-person shop.&lt;/p&gt;

&lt;p&gt;Along with the previous two points, this forms a  process of committing often, with good comments, to a feature branch on an off-site server.  You will be happy with yourself that you do.&lt;/p&gt;

&lt;h2 id=&quot;issue-tracking&quot;&gt;Issue Tracking&lt;/h2&gt;

&lt;p&gt;If you are or have ever been a professional developer, you are familiar with issue tracking software.  You may have even written some yourself.&lt;/p&gt;

&lt;p&gt;Creating issues for yourself may seem like overkill.  You know what you need to do and can do it.  However, what issue tracking represents is a bit of Project Management.  If you create issues for everything that you plan to do, you can more easily track and complete things. You can add future enhancements.  You can track bugs that your users may tell you about or that you find, and determine how severe they are. Issue tracking is most basic way to manage your overall project.&lt;/p&gt;

&lt;p&gt;This can be as simple as a spreadsheet. But here, again, there are free or low cost tools. Bitbucket and Github have built-in issue tracking. &lt;a href=&quot;https://trello.com&quot;&gt;Trello&lt;/a&gt; is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Kanban_(development)&quot;&gt;Kanban&lt;/a&gt; board that is free at small scale. The important part is to get things written down so they can be ordered and you’ll know what to do next.&lt;/p&gt;

&lt;h2 id=&quot;pull-request&quot;&gt;Pull Request&lt;/h2&gt;

&lt;p&gt;When you push up a branch, you can make a pull request, asking a maintainer to merge into the main branch.  Using a Git Flow model, this would be a merge into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;develop&lt;/code&gt; branch. Github and Bitbucket and others provide helpful tools for seeing the diff of the code being proposed. At this point, it can be read, examined, and reviewed before being accepted.  The reviewer can make comments, the submitter update the branch, etc.&lt;/p&gt;

&lt;p&gt;So, what would be the point if there is only one person on the project? One person that has written the code, one person reviewing, one person merging? It’s helpful for a few reasons. One, it enforces the branching model above. Code must be branched before changing in order to use a PR process. It will help you stick to making small changes per PR so that they are readable, a good practice that goes hand-in-hand with the small commits with good comment mentioned above. Another is that it gives you a chance to see your changes in isolation, all in one place.  This will help you spot such things as errors, stray changes, and missing documentation. Finally, if you bring on a collaborator later, it will be good to have the process in place.&lt;/p&gt;

&lt;p&gt;Tooling makes this so simple it’s practically as easy to just do it as not.&lt;/p&gt;

&lt;h2 id=&quot;continuous-integration&quot;&gt;Continuous Integration&lt;/h2&gt;

&lt;p&gt;When you merge code, you want to make sure it works.  You want to make sure it builds from source on a different machine and you want to make sure all the tests pass.  If you don’t, you could be surprised that you’ve accidentally set up your project such that it depends on unidentified environment requirements, or as I once did, on a custom framework that could not be rebuilt. &lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_integration&quot;&gt;Continuous Integration&lt;/a&gt; can ensure this.&lt;/p&gt;

&lt;p&gt;For the small shop, frequent integration, periodically building and running the code and tests, can be sufficient. But there are tools available, for instance &lt;a href=&quot;https://travis-ci.org&quot;&gt;Travis CI&lt;/a&gt; has an option to build any open source project on Github, or built from source for a local server. &lt;a href=&quot;https://jenkins.io&quot;&gt;Jenkins&lt;/a&gt; and other tools can be built from source as well. These can give piece of mind and remove the need for manual integration.&lt;/p&gt;

&lt;h2 id=&quot;continuous-delivery&quot;&gt;Continuous Delivery&lt;/h2&gt;

&lt;p&gt;Just like the code can be integrated automatically, that built code can be distributed automatically.  You can use your CI server to automatically send binaries to services like HockeyApp or TestFlight. This is especially important when beta testing with a larger pool of users.&lt;/p&gt;

&lt;p&gt;Like CI, CD may be overkill for a one person project.  It’s simple to build and upload manually.  But even if this CI/CD is done with simple local scripts or tools such as &lt;a href=&quot;https://fastlane.tools&quot;&gt;Fastlane&lt;/a&gt;, automatic CI/CD saves time and thought. Scripted processes are automatic and repeatable.&lt;/p&gt;

&lt;h2 id=&quot;tag--release&quot;&gt;Tag &amp;amp; Release&lt;/h2&gt;

&lt;p&gt;This another place where where following the git-flow model helps. When you are ready, create a release branch. If you have a CI/CD process set up, at this point you can have the release branch automatically pushed up through your beta channel, whether that’s TestFlight, AppCenter, Google Play, etc.&lt;/p&gt;

&lt;p&gt;When you release live, merge the release branch to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;, delete the branch and tag master with the release version. Keeping consistent with this process is simple and will ensure that your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; branch always reflects what people who use your app are seeing.&lt;/p&gt;

&lt;h2 id=&quot;beta-testing&quot;&gt;Beta Testing&lt;/h2&gt;

&lt;p&gt;When you have a release branch as an app in your beta channel, it’s time to make sure that you know the bugs that you are shipping with. Note that you’ll likely be shipping with bugs. As software gets more complex, there are more ways for small errors or incorrect interactions between components to enter. There are great ways to minimize the number of bugs, which are out of the scope of this discussion, but you are likely to have bugs. It’s important to know what these may be and to ensure that you don’t ship with &lt;em&gt;major&lt;/em&gt; bugs, show-stoppers like user data loss or corruption, UI dead ends, deadlocks, memory mismanagement, etc.&lt;/p&gt;

&lt;p&gt;To help determine that the app is consistent enough for your users, and to shake out issues, beta testing is an important step. Getting enough outside eyes on your app, eyes that are sharp and trusted, will reveal problems that you may subconsciously gloss over since you are so familiar with your own product. Just as a copy editor can spot tpyos and grammatical errors that the author’s mind skips over, so too do beta testers spot the small things. Just as an editor may reveal structural problems that the author is too close to see, so too do beta testers give a report of how a clean mind perceives your app. Even subjective things like the duration or direction of an animation are important to get a reading on. If your testers are saying a fancy animation is actually getting in the way of accomplishing a task, then it doesn’t matter how enamored you are with it. That’s an issue.&lt;/p&gt;

&lt;p&gt;For each issue raised, you must assess: does this need to be fixed before shipping? Can it wait until a subsequent release, a fast-follow release or next major version? The severity of the issue and complication of a fix sometimes makes this clear, but other times it’s a judgement call.&lt;/p&gt;

&lt;p&gt;There are services to help with this, but for those projects with limited resources, recruiting people you know or those from social networks or forums may be sufficient. It’s more helpful if they are a part of the target audience.&lt;/p&gt;

&lt;p&gt;Beta testers can help with the correctness, perception, and usability of your app. They can tell you exactly what they were doing then they encountered something. Getting a report of a problem from a beta tester is far better than getting one from the field.&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;This list may be more aspirational for me than actual. I certainly miss steps or cut corners at times.  But there is a right way to do things, and just like I would try to never knowingly commit a bad hack, I know what the right process is and strive to follow it. Hints of process from bigger development shops can help the one-person side project make the best use of the limited time there is.&lt;/p&gt;

</description>
        <pubDate>Sun, 21 Jul 2019 03:00:10 +0000</pubDate>
        <link>https://www.donmowry.com/big-shop-process-for-the-small-shop</link>
        <guid isPermaLink="true">https://www.donmowry.com/big-shop-process-for-the-small-shop</guid>
        
        
        <category>programming</category>
        
        <category>process</category>
        
      </item>
    
      <item>
        <title>A Seismic Shift</title>
        <description>&lt;p&gt;Did you feel that last week? If you are a developer on an Apple platform, did you feel that shift during WWDC?&lt;/p&gt;

&lt;p&gt;Every so often, there is a shift so impactful that it’s almost a physical feeling.  I remember the moment Swift was announced, and it was immediately clear that this was a huge change.  Maybe not right away, maybe not that year or the next, but these big changes reach out and are felt throughout the industry.  Java.  C++. .Net.  All of these had lasting impact.&lt;/p&gt;

&lt;p&gt;Last week was another monumental shift.  SwiftUI, not even hinted at in rumor blogs except in the barest of ways, landed with an audible gasp and collective shock.  Not the iOS-on-Mac technology announced last year, here was a declarative, reactive UI framework, with deep tooling and support on all their platforms, the basis of Apple UI development for the next decade and beyond.&lt;/p&gt;

&lt;p&gt;Through support in Swift 5.1, SwiftUI is a DSL for UI.  This hints at a greater power: to make this a domain-specific language, support for such a concept in Swift has been baked into the language. This means that DSLs can be created for other things. Can you image writing Swift code that actually expresses an HTML or CSS document? By creating SwiftUI, Apple has opened even more options, through Swift 5.1 and Combine.&lt;/p&gt;

&lt;p&gt;Functional Reactive Programming has been a hot ticket in Apple development for a while now.  Since its seeds in C# a decade ago, it has grown and spread to other languages and platforms.  There are implementations for the Apple platforms, several in fact. But it has sat on top of the platform, lacking deep integration.  It has a different syntax and mindset that is not common to all developers, a barrier to hiring. Combine, the new Apple framework, changes that. It is an official framework that can bee integrated with platform technologies.  It is a platform framework, something that can rightfully be considered for hiring developers, like Core Data, GCD, or Core Animation.  It brings FRP into the fold.&lt;/p&gt;

&lt;p&gt;Data flows one way in SwiftUI.  This is a well-known and well-regarded concept, but difficult to enforce, and tricky to always see the effects of.  But with FRP and SwiftUI, Data can be bound to views, and data flow enforced.&lt;/p&gt;

&lt;p&gt;Bindings are native on macOS, but have never been available to iOS. Key Value Observing or Notifications could approximate bindings, but required glue code and have gotchas to be aware of.  Now bindings are available on macOS, iOS, watchOS, iPadOS, and tvOS.  This brings up another huge advantage of SwiftUI.&lt;/p&gt;

&lt;p&gt;Cross-platform (where here, platform means Apple platforms) development remains an industry bugbear. Frameworks and tooling has lead mostly to lowest common denominator and alien UI. But here, there is no difference in platform tool. SwiftUI is &lt;em&gt;how&lt;/em&gt; you write UI code, from tvOS to watchOS and all computers in between.  And what is to stop Andorid, Windows, Linux, Web from joining in?  Not a conversion, not a lowest common denominator of support, but full platform support built right in?&lt;/p&gt;

&lt;p&gt;Make no mistake: what Apple introduced last week is the way we will be programming UI.  Maybe not this year, maybe not next, but like with Swift itself, growing more and more each year, until it is massive and undeniable.  Giant.&lt;/p&gt;

&lt;p&gt;When Apple dropped that giant news last week, did you feel that seismic shift?&lt;/p&gt;
</description>
        <pubDate>Mon, 10 Jun 2019 03:00:10 +0000</pubDate>
        <link>https://www.donmowry.com/seismic_shift</link>
        <guid isPermaLink="true">https://www.donmowry.com/seismic_shift</guid>
        
        
        <category>programming</category>
        
        <category>apple</category>
        
      </item>
    
      <item>
        <title>Solid Engineering - D is for Dependency Inversion</title>
        <description>&lt;p&gt;Despite sounding similar, this &lt;a href=&quot;/solid-engineering&quot;&gt;SOLID&lt;/a&gt; principle is Dependency Inversion, which is not Dependency Injection. Dependency Injection is simply the design of a class such that everything it needs is passed into it, which helps with such goals as single responsibility, unit testing, and removing hidden dependencies. &lt;a href=&quot;http://www.oodesign.com/dependency-inversion-principle.html&quot;&gt;Dependency Inversion&lt;/a&gt;, however, is an inversion in the way that some might think about software development.  It is two rules:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;High-level modules should not depend on low-level modules. Both should depend on abstractions.&lt;/li&gt;
    &lt;li&gt;Abstractions should not depend on details. Details should depend on abstractions.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can read the example in the article above, but the key is that if there is a piece of functionality that couples two class together too tightly, abstract that out. You want to make sure that the interface between the two classes or systems are not talking to each other directly, but rather through a well-known interface. Similar thinking led to the USB spec, in that with the bus interface abstracted, different systems could plug into each other without having to be hard-wired together.&lt;/p&gt;

&lt;p&gt;You want to make sure that things aren’t coupled tightly where they should not be. This isn’t to say that it should be abstractions all the way down, but especially between systems, there should be an abstraction to allow implementations to vary without changes needed on the other side. You want to stay nice and flexible.&lt;/p&gt;

&lt;p&gt;Plus, you want to keep &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2015/408/&quot;&gt;Crusty&lt;/a&gt; happy.&lt;/p&gt;
</description>
        <pubDate>Fri, 16 Oct 2015 02:32:10 +0000</pubDate>
        <link>https://www.donmowry.com/d-is-for-dependency-inversion</link>
        <guid isPermaLink="true">https://www.donmowry.com/d-is-for-dependency-inversion</guid>
        
        
        <category>programming</category>
        
        <category>solid</category>
        
      </item>
    
      <item>
        <title>Solid Engineering - I is for Interface Segregation</title>
        <description>&lt;p&gt;Keeping the &lt;a href=&quot;http://www.seriouseats.com/2011/02/blast-from-the-past-the-mcdlt-mcdonalds-1980s-jason-alexander.html&quot;&gt;hot side hot and the cold side cold&lt;/a&gt; may have been a marketing or ecological failure, but keeping dissimilar responsibilities apart is just good object-oriented programming, and is today’s &lt;a href=&quot;/solid-engineering&quot;&gt;SOLID&lt;/a&gt; principle: the Interface Separation principle.&lt;/p&gt;

&lt;p&gt;At its core, this principle is about introducing high cohesion to our objects. This means that an interface should consist of methods and properties that are tightly replaced to each other. If a class suffers from low cohesion, it will be larger, more unwieldy, and more difficult to change and maintain. Clients of a class should respectfully be presented with a small, focused interface.&lt;/p&gt;

&lt;p&gt;On iOS we have a common example of this principle. When you use a Table View Controller, there are two protocols you implement as well: a UITableViewDataSource and a UITableViewDelegate. The table view uses both of these in its operation. Yet, functions related to how a table view gets its data are not related to communication from the table view to its delegate. Thus, it would not make sense to combine these interfaces. In your own code, you could have one class that implements both protocols, but there is no reason that one object must implement both.&lt;/p&gt;

&lt;p&gt;When designing class interfaces, you are creating an API, even if the only client will be your internal code. ISP is about treating the clients of this API with respect. By decoupling disparate methods, clients are presented with a slimmer, clearer surface because the implementation is not forced to implement methods it does not need. That’s just common courtesy right there.&lt;/p&gt;
</description>
        <pubDate>Mon, 12 Oct 2015 02:32:10 +0000</pubDate>
        <link>https://www.donmowry.com/i-is-for-interface-segregation</link>
        <guid isPermaLink="true">https://www.donmowry.com/i-is-for-interface-segregation</guid>
        
        
        <category>programming</category>
        
        <category>solid</category>
        
      </item>
    
      <item>
        <title>Solid Engineering - L is for Liskov Substitution</title>
        <description>&lt;p&gt;Halfway through &lt;a href=&quot;/solid-engineering&quot;&gt;SOLID&lt;/a&gt; we find the letter L.  This stands for the Liskov Substitution Principle.  Since it’s self-evident what this means, we can move on.&lt;/p&gt;

&lt;p&gt;Um.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.indiebound.org/book/9780136291558&quot;&gt;Bertrand Meyer&lt;/a&gt; also discussed this principle, but Barbara Liskov introduced and formalized it in a stricter way, thus this principle bears her name.  This principle lays out a strong behavioral relationship of subtypes such that a class may be replaced with a subtype of a class without altering the correctness of a system.&lt;/p&gt;

&lt;p&gt;This has &lt;a href=&quot;http://en.wikipedia.org/wiki/Design_by_contract&quot;&gt;design by contract&lt;/a&gt; implications.  Subtypes may not have stricter preconditions or looser postconditions than their parent type. So what does this mean?&lt;/p&gt;

&lt;p&gt;A common example given is a Square class.  A Square is a type of Rectangle, so it makes sense that Square inherits from Rectangle. However, a Square has an additional, stricter precondition: the height and width must be identical.  Therefore, if we have setter methods for one or the other, it would need to mutate both dimensions.  If a Square is used where a Rectangle is expected, this stricter precondition may cause unexpected consequences, as code that mutates a Rectangle’s height is not expected to affect its width.&lt;/p&gt;

&lt;p&gt;This is clear, though probably less applicable explanation of a violation of the principle. This also allows a finer point about the LSP: the stricter condition being introduced is that &lt;em&gt;when the height or width is changed, the other is changed as well&lt;/em&gt;. However, here there is a simple correction to this: remove the &lt;em&gt;change&lt;/em&gt; condition. Make the object immutable.  The calling code then can have no expectation that changing one dimension does not change the other, simply because it can have no expectation of change at all.&lt;/p&gt;

&lt;p&gt;This shows that the principle is about contracts, expectations, and behavior.  It is the expected behavior that cannot change from type to subtype.&lt;/p&gt;

&lt;p&gt;This is a fairly subtle point, and one that is probably rarely violated, but when it is, it can cause unexpected side effects and crashes.&lt;/p&gt;

&lt;p&gt;Next up: I.&lt;/p&gt;

</description>
        <pubDate>Fri, 02 Oct 2015 02:32:10 +0000</pubDate>
        <link>https://www.donmowry.com/l-is-for-liskov-sustitution</link>
        <guid isPermaLink="true">https://www.donmowry.com/l-is-for-liskov-sustitution</guid>
        
        
        <category>programming</category>
        
        <category>solid</category>
        
      </item>
    
      <item>
        <title>Designing an Apple Watch App</title>
        <description>&lt;p&gt;Similar to &lt;a href=&quot;http://www.marco.org/2015/05/08/overcast-apple-watch-redesign&quot;&gt;others&lt;/a&gt;, I found that when the concepts of a watch app meet actual hardware, the ideas give way to reality.&lt;/p&gt;

&lt;p&gt;Given that this app hasn’t shipped, I’m going to be speaking a bit in generalities.  The app has a hierarchy, a drill-down table view style, let’s call it Level 1, Level 2, and Activity.  There are several kinds of Activities.  This should be sufficient to discuss my experience.&lt;/p&gt;

&lt;p&gt;Last fall, after the Watch SDK shipped, I thought about what part of the app would benefit from the small, quick interactions that the watch would enable.  Right away, one of the Activities, one that had low interaction and value in short bursts, stood out as something that a user might want to do if they had a minute or less of time. This activity included an image downloaded from the Internet and a few pieces of text. For simplicity’s sake, let’s say the actions are thumbs up and thumbs down.&lt;/p&gt;

&lt;p&gt;After moving the data to &lt;a href=&quot;https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW6&quot;&gt;be accessible to both the app and the watch extension&lt;/a&gt;, I created four branches: the first started with a list of Level 2 so that you had a sense of where you were.  The second started only with the activity, and used a long press to complete the action.  The third split the activity into 3 views to be swiped through, with the actions at the end.  The fourth had the actions on the first view with a little bit of data, and 2 more views.&lt;/p&gt;

&lt;p&gt;So which one did I use?  It’s probably apparent.&lt;/p&gt;

&lt;p&gt;Immediately (and predictably), the first branch was out.  I had included this in order to prove that interacting with a long scrolling list was not ideal. And indeed, holding up your arm to scroll through a long list was instantly tiring. Introducing the extra step of deciding where to go slowed down the quick and direct access I was going for. &lt;em&gt;Lesson one: prioritize quick interactions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A quick side note: I had feared that it would take a long time to send the image over to the watch. Surprisingly, this was nearly instantaneous. I had queued up some ideas about how to make this faster, but it turned out it was not needed right away, and I could move that optimization to a time when I had longer access to the hardware.&lt;/p&gt;

&lt;p&gt;The second branch started in a (essentially) random activity. You could scroll up and down to see the image and the text. Then, using a force touch, you would see the thumbs up / thumbs down options. This worked fine — and was clearly wrong. Bringing up the menu items included the taptic engine buzzing your wrist. You knew you were using a force touch. For what was to be many, quick interactions, this would have very soon become excessively annoying. As clearly demonstrated by the built in apps, the force touch was not a common interaction, but a way to change settings and the like. &lt;em&gt;Lesson two: use force touch for menu options only.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the third branch, I split the activity over three screens. The intention here was to make sure the user did not need to scroll, but instead would swipe through one screen of information at a time. This was a terrible idea. The Watch has a digital crown for the explicit purpose of scrolling. Swiping is more work, and should be used for things screens that are very different, like Glances, or different options like watch faces. Strongly related content like the activity that I was working on belongs together on one screen. &lt;em&gt;Lesson three: keep related content together.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, this was terrible for a different reason as well. I had the data loading very fast, and had the next activity ready to go. To display it, I reloaded the view controllers of the pages. But when I did this, there was a very noticeable lag. What I had feared for the images was now true for the data. This was similar to Today Extensions that might show old data for a moment. &lt;em&gt;Lesson four: keep view reuse to a minimum.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had come with four branches with varying differences, and none of them would be usable as they were. At this point, the interaction designer I was with and I had a few more hours with the hardware. So, I chose the only clear path.&lt;/p&gt;

&lt;p&gt;Tear it all down.&lt;/p&gt;

&lt;p&gt;Since I had written the app to completely separate the business logic and data fetching from the UI, it was a simple matter to swap out the front end.  Through experimentation, we’d learned that our app was best suited for a single screen, with normal text buttons for input.  To refresh the data quickly, I used a &lt;a href=&quot;https://developer.apple.com/library/ios/documentation/WatchKit/Reference/WKInterfaceTable_class/&quot;&gt;WKInterfaceTable&lt;/a&gt;.  When you select an action, I can swap out rows quickly.  While my designer partner got to work on two animation sequences for success and failure, I reorganized the interface: a header, a table, and a group with two actions.  Our refreshed app felt intuitive, worked quickly, and had fun animation.  We left feeling buoyed by our accomplishment.&lt;/p&gt;

&lt;p&gt;None of these lessons are surprising, and all are clear with hindsight.  But I think they exemplify the final lesson, which is also unsurprising, but needed to be done within the context of this specific app to see what worked best:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Experiment.&lt;/em&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 27 May 2015 02:32:10 +0000</pubDate>
        <link>https://www.donmowry.com/designing-an-apple-watch-app</link>
        <guid isPermaLink="true">https://www.donmowry.com/designing-an-apple-watch-app</guid>
        
        
        <category>apple</category>
        
        <category>watch</category>
        
        <category>programming</category>
        
        <category>design</category>
        
      </item>
    
  </channel>
</rss>
