<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Hugh Jeremy</title>
    <description>The latest articles on Forem by Hugh Jeremy (@hugh_jeremy).</description>
    <link>https://forem.com/hugh_jeremy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F84305%2F57447351-32ec-4836-ae80-6e0ddd509c9c.jpeg</url>
      <title>Forem: Hugh Jeremy</title>
      <link>https://forem.com/hugh_jeremy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hugh_jeremy"/>
    <language>en</language>
    <item>
      <title>Time-Independent Dates in Swift</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Thu, 07 Dec 2023 00:23:26 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/time-independent-dates-in-swift-26j5</link>
      <guid>https://forem.com/hugh_jeremy/time-independent-dates-in-swift-26j5</guid>
      <description>&lt;p&gt;Most often, it is important that a date also near-precisely represent a point in time. In Swift, for that, we have &lt;code&gt;Foundation.Date&lt;/code&gt;. &lt;code&gt;TimeIndependentDate&lt;/code&gt; fills a gap where, in limited circumstances, we need to deal with a date independent of time. This is particularly useful when interfacing with third party systems which supply dates independent of time.&lt;/p&gt;

&lt;p&gt;I often find myself needing a time-independent date in Swift. So I've open-sourced a &lt;code&gt;TimeIndependentDate&lt;/code&gt; type for anyone else that might need one: &lt;a href="https://github.com/hwjeremy/time-independent-date-swift"&gt;https://github.com/hwjeremy/time-independent-date-swift&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Initialise a date, throwing a `TimeIndependentDate` error if the supplied&lt;/span&gt;
&lt;span class="c1"&gt;// literal values are invalid.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;timeIndependentDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="kt"&gt;TimeIndependentDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;january&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Parse a date from a string. Adjust encoding order as desired. A&lt;/span&gt;
&lt;span class="c1"&gt;// `TimeIndependentDateError` is thrown if the supplied string&lt;/span&gt;
&lt;span class="c1"&gt;// is not a valid date.&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;timeIndependentDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="kt"&gt;TimeIndependentDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"2021-01-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;encodingOrder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dayFirst&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>swift</category>
      <category>time</category>
      <category>opensource</category>
    </item>
    <item>
      <title>iOS from zero to App Store at Procuret</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Sun, 28 May 2023 21:03:41 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/ios-from-zero-to-app-store-at-procuret-o63</link>
      <guid>https://forem.com/hugh_jeremy/ios-from-zero-to-app-store-at-procuret-o63</guid>
      <description>&lt;p&gt;&lt;a href="https://procuret.com"&gt;Procuret&lt;/a&gt; is a business-to-business payments platform. Like many fintech startups, we bootstrapped with a web app. Web apps offer near-instant deployment, tight iteration loops, and a permission-less development environment. Perfect for finding product-market-fit.&lt;/p&gt;

&lt;p&gt;Native iOS apps offer great accessibility, blazing performance, and a type-safe development environment. They are ideal for offering a higher quality experience when product-market-fit has been achieved. Given Procuret's growth, it is natural for us to move focus from web to native.&lt;/p&gt;

&lt;p&gt;I got started writing apps in &lt;a href="https://developer.apple.com/xcode/swiftui/"&gt;SwiftUI&lt;/a&gt; when it was first released. It was not production ready. Four years has made a huge difference. SwiftUI is now in a great spot. We found almost everything just works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zuj-3t8l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jktondluoiinmb8fqcbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zuj-3t8l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jktondluoiinmb8fqcbk.png" alt="Procuret iOS in Xcode" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One senior (myself) and one junior (&lt;a href="https://www.linkedin.com/in/khoyet/"&gt;Kayla Hoyet&lt;/a&gt;) worked on the app. Combined we spent about 6 months "full-time-equivalent" to build it, spread out over one calendar year. We wrote 24,000 lines of code.&lt;/p&gt;

&lt;p&gt;Of that, 8,000 lines is the &lt;a href="https://github.com/procuret/procuret-swift"&gt;open-source API library&lt;/a&gt;. Take a look to see how we structure our data.&lt;/p&gt;

&lt;p&gt;We were thrilled to discover how easy it is to deploy to iPad as well as iPhone. The iOS app for both platforms is a single build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G2qbkPyH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lu98hgoh1hcm2u2kbopf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G2qbkPyH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lu98hgoh1hcm2u2kbopf.jpg" alt="Procuret on iPad" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At runtime, we query the &lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/horizontalsizeclass"&gt;&lt;code&gt;horizontalSideClass&lt;/code&gt;&lt;/a&gt; to determine how much data to display. The Instalment Schedule is a great example. In the screenshot below, note that we display their bare minimum amount of data for iPhone, but robust data on iPad.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GrdAj5Od--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4kexzhirtg0q4vvfppyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GrdAj5Od--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4kexzhirtg0q4vvfppyr.png" alt="Comparison of Procuret iOS running on iPad &amp;amp; iPhone" width="800" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With 20/20 hindsight, I wish we had trusted &lt;a href="https://developer.apple.com/documentation/swiftui/navigationpath"&gt;&lt;code&gt;NavigationPath&lt;/code&gt;&lt;/a&gt; from the start. It was introduced in iOS 16 and works very well. Initially I was wary of it, but we adopted it aggressively late in the development cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A very common pattern that `NavigationPath` makes easy:&lt;/span&gt;
&lt;span class="c1"&gt;// Popping back to the root view from deep in a `NavigationStack`.&lt;/span&gt;

&lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;navigationPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Destination&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;shouldPopBackToRootView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="c1"&gt;// later on...&lt;/span&gt;

&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shouldPopBackToRootView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;navigationPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We found the App Store Review process navigable, fast, and clear. Our Test Flight builds were typically approved within 18 hours. App Store approval came within 36 hours.&lt;/p&gt;

&lt;p&gt;SwiftUI is very good. It puts native app deployment within reach for small teams. It is fast to write, resistant to bugs, and a pleasant working environment for the developer.&lt;/p&gt;

&lt;p&gt;If you have questions about iOS app development, ask here in the comments or &lt;a href="https://twitter.com/hugh_jeremy"&gt;@hugh_jeremy&lt;/a&gt; on Twitter. Check out &lt;a href="https://apps.apple.com/au/app/procuret/id1634745293"&gt;Procuret on the App Store&lt;/a&gt; to access our flexible payment options. It is available to businesses in Australia and New Zealand. &lt;/p&gt;

</description>
      <category>swift</category>
      <category>swiftui</category>
      <category>ios</category>
    </item>
    <item>
      <title>Apprenticeships in software development</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Mon, 19 Oct 2020 07:08:27 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/apprenticeships-in-software-development-5197</link>
      <guid>https://forem.com/hugh_jeremy/apprenticeships-in-software-development-5197</guid>
      <description>&lt;p&gt;Here in Australia, if I want to be a sparky, chippy, bricky, or dunny diver, I would start out as an apprentice. As a first year apprentice electrician I would get paid about $40k AUD per year to work and learn on the job.&lt;/p&gt;

&lt;p&gt;If I stuck with my learning, I would be looking at an average of about $85k per year, and well north of $130k if I gunned it into senior electron-pushing.&lt;/p&gt;

&lt;p&gt;Apprenticeships mean mentorship, training, pathways and practical experience. They exist in society because they are great way to turn fresh-faced kids into seasoned professionals in a safe, fair manner.&lt;/p&gt;

&lt;p&gt;So where are all the software developer apprenticeships? If I search for "apprentice software developer", I get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;advertisements for bootcamps&lt;/li&gt;
&lt;li&gt;"entry" level jobs requiring "previous XX experience"&lt;/li&gt;
&lt;li&gt;aggregator listings for trainee jobs in non-software fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I started out writing, I had zero knowledge and an extreme propensity to stick my typing fingers in the proverbial wall socket, just to see what would happen. Fortunately I had some great mentors around me to nudge me towards good practice.&lt;/p&gt;

&lt;p&gt;Now I get to build entire tech stacks for startups. How did I pull that off? How do I help more people pull the same trick? I'm not sure, but it sure didn't involve bootcamps or applying to trainee jobs demanding "3+ years in PHP/Wordpress". I'd like to...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pay forward the help I received&lt;/li&gt;
&lt;li&gt;Spark discussion about apprenticeships in programming&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, I've posted my &lt;a href="https://procuret.com/jobs/apprentice-engineer"&gt;&lt;del&gt;first job ad for an apprentice&lt;/del&gt;&lt;/a&gt; (please note - applications for this position are now &lt;em&gt;closed&lt;/em&gt;). If like me you are fortunate enough to have risen from the fresh-faced to the battle-hardened ranks of keyboard-warriors, I encourage you to do the same.&lt;/p&gt;

</description>
      <category>career</category>
      <category>webdev</category>
      <category>learning</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Guiding principles for a new engineering team</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Sat, 10 Oct 2020 22:25:45 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/guiding-principles-for-a-new-engineering-team-438c</link>
      <guid>https://forem.com/hugh_jeremy/guiding-principles-for-a-new-engineering-team-438c</guid>
      <description>&lt;p&gt;What principles should guide engineering decisions? At &lt;a href="https://procuret.com"&gt;Procuret&lt;/a&gt; we are starting to scale up. It's time for us to grow our engineering team. I want to ensure that we can do so while retaining a principled engineering culture.&lt;/p&gt;

&lt;p&gt;To that end, I've drafted up the following &lt;a href="https://procuret.com/engineering/principles"&gt;principles&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  1. User data are sacrosanct
&lt;/h2&gt;

&lt;p&gt;Anyone that interacts with our software provides us with data. These data might be deeply and obviously personal, like identity documents. They might be less obvious personal, like browser data, approximate location, or usage patterns.&lt;/p&gt;

&lt;p&gt;All of these data are sacrosanct. Protecting these data from inappropriate access, by parties internal or external, with intentions malcious or careless, are our primary concern at all times.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. There is no such thing as "User error"
&lt;/h2&gt;

&lt;p&gt;There's an old joke that some errors have the code PEBKAC: Problem Exists Between Keyboard and Chair. While funny, if used in seriousness such a joke reveals a dangerous arrogance. If someone using our software is confused, we have done something wrong. Our software is the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Speed is quality
&lt;/h2&gt;

&lt;p&gt;How often do we release updates, how fast do we build new features, how quickly does our test suite run, how rapidly do we fix reported bugs, and how short is our page load time on customer devices?&lt;/p&gt;

&lt;p&gt;The faster we can do these and other things is a measure of our performance. What we do in a day is less important than how fast we do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. There are no stupid questions
&lt;/h2&gt;

&lt;p&gt;If you don't understand what someone else is saying, there is a very strong chance they don't understand either. Ask questions for your sake and the sake of everyone around you.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Readable code is good code
&lt;/h2&gt;

&lt;p&gt;Our code should be at least somewhat comprehensible to non-technical observers. Declarative, natural language code is fast, maintainable code.&lt;/p&gt;

&lt;p&gt;--//--&lt;/p&gt;

&lt;p&gt;What do you think? Have I got it right? What would you add or change?&lt;/p&gt;

</description>
      <category>culture</category>
      <category>discuss</category>
      <category>jobs</category>
      <category>career</category>
    </item>
    <item>
      <title>Practical applications of protocols in Python</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Wed, 24 Jun 2020 03:54:15 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/practical-applications-of-protocols-in-python-20i0</link>
      <guid>https://forem.com/hugh_jeremy/practical-applications-of-protocols-in-python-20i0</guid>
      <description>&lt;p&gt;There's no such thing as a free lunch. In object-oriented code, &lt;em&gt;protocols&lt;/em&gt; offer a very close approximation. In this context, a protocol is a set of behaviours that, if exhibited by a type, guarantee certain other behaviours.&lt;/p&gt;

&lt;p&gt;For example, consider a hypothetical class, &lt;code&gt;Human&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unique_id&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unique_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class has an initialiser and a single class method, which we will assume is used to decode deserialised representations of Humans. For example, when they are retrieved from a database.&lt;/p&gt;

&lt;p&gt;Suppose we want to be able to decode many &lt;code&gt;Human&lt;/code&gt; instances at once. Perhaps our database returns lists of &lt;code&gt;Human&lt;/code&gt;. We could add a new class method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# continues `Human` definition from above
&lt;/span&gt;
    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy enough. We can now call &lt;code&gt;Human.decode()&lt;/code&gt; on single instances, and &lt;code&gt;Human.decode_many()&lt;/code&gt; on lists.&lt;/p&gt;

&lt;p&gt;Consider that nothing in the &lt;code&gt;.decode_many()&lt;/code&gt; function body depends on the actual implementation of &lt;code&gt;Human&lt;/code&gt;. The &lt;code&gt;.decode()&lt;/code&gt; method is completely opaque to &lt;code&gt;.decode_many()&lt;/code&gt;, and &lt;code&gt;.decode_many()&lt;/code&gt; does not need to be aware of the &lt;code&gt;.decode()&lt;/code&gt; implementation in order to do its job perfectly.&lt;/p&gt;

&lt;p&gt;With that in mind, consider a second class, &lt;code&gt;Email&lt;/code&gt;. Suppose it is defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirmed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;confirmed&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;confirmed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;confirmed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should be noticed that &lt;code&gt;Email.decode_many()&lt;/code&gt; is identical to &lt;code&gt;Human.decode_many()&lt;/code&gt;. We could write &lt;code&gt;.decode_many()&lt;/code&gt; in a generic form as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ATypeWithDecodeMethod&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ATypeWithDecodeMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In english: "Take any type having a &lt;code&gt;.decode()&lt;/code&gt; method, and call that method with all instances of data in this array of data, and return the resulting array"&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Having a &lt;code&gt;.decode()&lt;/code&gt; method&lt;/em&gt; is a behaviour which, if exhibited by a type, &lt;em&gt;guarantees another behaviour&lt;/em&gt;: The ability to be fed to &lt;code&gt;.decode_many()&lt;/code&gt;. We can use the word "protocol" to describe the requirement to &lt;em&gt;have a &lt;code&gt;.decode()&lt;/code&gt; method&lt;/em&gt;, and a class exhibiting that requirement is said to &lt;em&gt;conform to the protocol&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We can write such a protocol like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Decodable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;NotImplementedError&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our original &lt;code&gt;Human&lt;/code&gt; definition already has a &lt;code&gt;.decode()&lt;/code&gt; method. As such, in practice, it already conforms to the &lt;code&gt;Decodable&lt;/code&gt; protocol. We can identify is as conformant, such that it gains access to &lt;code&gt;.decode_many()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Decodable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# definition remains otherwise unchanged from that
&lt;/span&gt;    &lt;span class="c1"&gt;# presented earlier in this article.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can call &lt;code&gt;Human.decode_many()&lt;/code&gt; and receive a list of &lt;code&gt;Human&lt;/code&gt; instances in return, despite the definition of &lt;code&gt;Human&lt;/code&gt; not including any &lt;code&gt;.decode_many()&lt;/code&gt; method. We can do the same with &lt;code&gt;Email&lt;/code&gt;, such that we are not duplicating code.&lt;/p&gt;

&lt;h1&gt;
  
  
  A Note on Formality
&lt;/h1&gt;

&lt;p&gt;This is a good time to pause consider the elephant in the room: What makes a "protocol" different from regular old class inheritance? There's no formal definition of a protocol in Python, so how can &lt;code&gt;Decodable&lt;/code&gt; be said to be a protocol?&lt;/p&gt;

&lt;p&gt;The answer is that in this context, a "protocol" is what you as the programmer define it to be, in your own mind. It just so happens that Python's &lt;code&gt;class&lt;/code&gt; keyword provides the behaviours necessary to implement protocol relationships in Python code.&lt;/p&gt;

&lt;p&gt;Some languages give explicit protocol tools: Swift has &lt;code&gt;protocol&lt;/code&gt;, Java has &lt;code&gt;abstract&lt;/code&gt;, and C# has &lt;code&gt;interface&lt;/code&gt;. Each one differs in nuanced ways, but all seek to identify code which defines behaviours that, if exhibited by a type, guarantee certain other behaviours.&lt;/p&gt;

&lt;p&gt;A key to the effectiveness of protocols in Python is that the protocol &lt;em&gt;should not have an initialiser&lt;/em&gt;. Any class adopting a protocol should be able to do so while retaining full responsibility for and control over its own initialisation.&lt;/p&gt;

&lt;p&gt;Just like static type checking, property immutability, or any other Python paradigm, it is up to you whether you want to obey the rules you define for your protocols.&lt;/p&gt;

&lt;h1&gt;
  
  
  Extending protocols
&lt;/h1&gt;

&lt;p&gt;True joy arrives when we decide we want &lt;code&gt;Decodable&lt;/code&gt; objects to be able to &lt;em&gt;do more stuff&lt;/em&gt;. Any new capabilities we add to the &lt;code&gt;Decodable&lt;/code&gt; protocol that do not change the protocol requirements are essentially free.&lt;/p&gt;

&lt;p&gt;For example, suppose that our database may return optional data. That is, in response to a query we may receive some data or we may receive &lt;code&gt;None&lt;/code&gt;. Our &lt;code&gt;.decode()&lt;/code&gt; method definition does not make such allowances. If it is fed &lt;code&gt;None&lt;/code&gt; it will crash.&lt;/p&gt;

&lt;p&gt;We can add a new method like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Continues the `Decodable` definition from above
&lt;/span&gt;    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;optionally_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now both &lt;code&gt;Human&lt;/code&gt; and &lt;code&gt;Email&lt;/code&gt; may be optionally decoded, and we did not even have to touch their definitions. Let's go even further. Suppose we want to be able to load &lt;code&gt;Human&lt;/code&gt; and &lt;code&gt;Email&lt;/code&gt; from raw JSON-serialised data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Continues the `Decodable` definition from above
&lt;/span&gt;    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deserialise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, without touching &lt;code&gt;Email&lt;/code&gt; or &lt;code&gt;Human&lt;/code&gt;, we have added new capabilities to both. Not quite a free lunch, but so easy that we could call it one.&lt;/p&gt;

</description>
      <category>python</category>
      <category>protocols</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Versioning a Relational Data Model</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Wed, 17 Jun 2020 08:31:57 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/versioning-a-relational-data-model-5f74</link>
      <guid>https://forem.com/hugh_jeremy/versioning-a-relational-data-model-5f74</guid>
      <description>&lt;p&gt;Suppose we have an application dealing in Birds. We have a Bird class, which could be in any language, but which we'll consider in Python for examples sake.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;species&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Species&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Assume defined elsewhere
&lt;/span&gt;        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unique_id&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;species&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;species&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside our database, we might have created a birds table. Example SQL in this article will use Postgres syntax, but is mostly generic SQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;birds&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;unique_id&lt;/span&gt; &lt;span class="nb"&gt;serial&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;species&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;species&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose our application is terribly useful and popular, and many customers are very happy being able to uniquely identify and name their birds. However, some greedy, ungrateful customers would like to be able to &lt;em&gt;rename&lt;/em&gt; their birds.&lt;/p&gt;

&lt;p&gt;Inside the application, we might add a new method to our &lt;code&gt;Bird&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Continues on from `Bird` defined earlier
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;new_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="c1"&gt;# Some database adapter type
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 

        &lt;span class="c1"&gt;# Execute a database query changing the bird's name
&lt;/span&gt;        &lt;span class="c1"&gt;# and return the updated bird
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the database, we make the following changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;names&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bird&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;birds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="k"&gt;names&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt;
    &lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;at&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="s1"&gt;'utc'&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;   &lt;span class="n"&gt;birds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;birds&lt;/span&gt; &lt;span class="k"&gt;drop&lt;/span&gt; &lt;span class="k"&gt;column&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fantastic! We now have 5ish Normal Form renaming of birds. Everything is wonderful. Except... Some things are now pear shaped. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our database is now incompatible with prior versions of our application code&lt;/li&gt;
&lt;li&gt;We must ensure that any new database instance created for the bird application uses the newer data model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a very small system like this one, these problems are not really problems. It's easy to keep track of what database instance is using what data model, and make manual adjustments where necessary.&lt;/p&gt;

&lt;p&gt;What about when we have thousands of tables spread across dozens of schemas? These problems become very big problems.&lt;/p&gt;

&lt;p&gt;It's easy to keep application code versioned using Git. Could we do something similar for a relational data model? Yes indeed. The first step is to make the database "self-aware". We can do this by creating a new table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;meta_revisions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;executed&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;unique&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever we modify the data model, we write to this table. Every revision gets an associated version number. For example, the revision that created the initial &lt;code&gt;birds&lt;/code&gt; table might be &lt;code&gt;1&lt;/code&gt; and the revision adding the &lt;code&gt;names&lt;/code&gt; table might be &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You could put all your revisions in a folder, and then name each revision file with its version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/project/data_model_revisions/1.sql
/project/data_model_revisions/2.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git will naturally handle the process of versioning these files. So long as you never go back and &lt;em&gt;edit&lt;/em&gt; a revision file, the folder becomes a sequence of instructions that can build up your data model from scratch. Almost like an SQL dump file, except one that takes the meandering course of your application's development.&lt;/p&gt;

&lt;p&gt;I like to include a little tool in my codebases that handles data model revisions. In pseudo code, it goes something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;last_executed_revision = database.retrieve_last_revision();
sequence_number = integer(last_executed_revision)

for file in revisions_folder:
    file_sequence = integer(file.name_without_extension)
    if file_sequence &amp;gt; last_executed_revision:
        for statement in file.body.split(';'):
            database.execute(statement)
        database.record_revision(file_sequence)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, in the real world, you can make this tool much more complicated and useful. It can automatically handle rollbacks on errors, do tricky query formatting, or some other task that eases management of your data model.&lt;/p&gt;

&lt;p&gt;However you do it, the end result is data model bliss. You can always be sure that your database schemas are in sync with your application code, and you can rebuild schemas to the specification of any prior version of your application code.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Adding a Unified `NSToolbar` to a SwiftUI window</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Mon, 30 Mar 2020 00:20:19 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/adding-a-unified-nstoolbar-to-a-swiftui-window-4ppp</link>
      <guid>https://forem.com/hugh_jeremy/adding-a-unified-nstoolbar-to-a-swiftui-window-4ppp</guid>
      <description>&lt;p&gt;Previously on "Adventures in SwiftUI", I &lt;a href="https://dev.to/hugh_jeremy/adding-an-nstableview-to-a-swiftui-view-212p"&gt;discussed adding an &lt;code&gt;NSTableView&lt;/code&gt; to a SwiftUI project&lt;/a&gt;. I received a follow up question from Axel:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1244403543584526341-853" src="https://platform.twitter.com/embed/Tweet.html?id=1244403543584526341"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1244403543584526341-853');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1244403543584526341&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;To be honest, I don't have the faintest idea how I did this. So, I'll dig through the code and blog the process. That way, if anyone else has the same question, Google will hopefully land them here.&lt;/p&gt;

&lt;p&gt;First stop, where do we create the &lt;code&gt;NSWindow&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@NSApplicationMain&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AppDelegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;NSApplicationDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSWindow&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;


    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;applicationDidFinishLaunching&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;aNotification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Snip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's declared in the AppDelegate. I can't recall if all SwiftUI apps depend on the AppDelegate pattern... If yours does not, then the following is probably still useful, but you will likely need to adapt to your circumstances.&lt;/p&gt;

&lt;p&gt;Here is the point of &lt;code&gt;NSWindow&lt;/code&gt; creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Enclosing code snipped&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;contentRect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nv"&gt;styleMask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;titled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unifiedTitleAndToolbar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;miniaturizable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resizable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fullSizeContentView&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nv"&gt;backing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buffered&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;center&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Draft Sport"&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;titleVisibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hidden&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFrameAutosaveName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Draft Sport"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSHostingView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;rootView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contentView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key thing to note here is the provision of the &lt;a href="https://developer.apple.com/documentation/appkit/nswindow/stylemask/1644619-unifiedtitleandtoolbar"&gt;&lt;code&gt;.unifiedTitleAndToolbar&lt;/code&gt;&lt;/a&gt; mask in the style mask array.&lt;/p&gt;

&lt;p&gt;Next, we need to create the actual toolbar content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Enclosing code snipped&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;toolbarButtons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSHostingView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;rootView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ToolbarButtons&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;toolbarButtons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toolbarButtons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fittingSize&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;titlebarAccessory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSTitlebarAccessoryViewController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;titlebarAccessory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toolbarButtons&lt;/span&gt;
&lt;span class="n"&gt;titlebarAccessory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layoutAttribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trailing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we add the &lt;code&gt;titlebarAccessory&lt;/code&gt; to the &lt;code&gt;NSWindow&lt;/code&gt; instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Enclosing code snipped&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toolbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NSToolbar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTitlebarAccessoryViewController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;titlebarAccessory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila, you've got a unified toolbar. Watch out though: I haven't gone any further with this. I imagine there are going to be all sorts of challenges developing the typical behaviour expected of a unified toolbar.&lt;/p&gt;

&lt;p&gt;I hope this helps you Axel, and anyone else enjoying SwiftUI!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/hugh_jeremy"&gt;Hugh&lt;/a&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>swiftui</category>
      <category>swift</category>
      <category>appkit</category>
    </item>
    <item>
      <title>Adding an NSTableView to a SwiftUI View</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Wed, 18 Mar 2020 22:38:52 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/adding-an-nstableview-to-a-swiftui-view-212p</link>
      <guid>https://forem.com/hugh_jeremy/adding-an-nstableview-to-a-swiftui-view-212p</guid>
      <description>&lt;p&gt;SwiftUI made a &lt;a href="https://dev.to/hugh_jeremy/swiftui-first-impressions-pain-analysis-5d6i"&gt;surprisingly good first impression&lt;/a&gt;. Of course, things get more complicated as soon as you want to build something actually useful. Tables are a fundamental part of many macOS applications. How can we build them in SwiftUI?&lt;/p&gt;

&lt;p&gt;Apple pushes the SwiftUI &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; pretty hard. It's great for iPhone GUIs. It is woefully inadequate for the desktop. Compared to the old AppKit &lt;code&gt;NSTableView&lt;/code&gt; it is a children's toy.&lt;/p&gt;

&lt;p&gt;You can certainly build tables out of &lt;code&gt;List&lt;/code&gt;, using rows of &lt;code&gt;HStack&lt;/code&gt; views. My test case was the Draft Rugby &lt;a href="https://draftrugby.com/player-stats"&gt;Player Stats table&lt;/a&gt;. Using &lt;a href="https://github.com/hwjeremy/draft-sport-swift"&gt;draft-sport-swift&lt;/a&gt; to source the data, &lt;code&gt;List&lt;/code&gt; gave me something approaching serviceability:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MEyIaqPt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/pb1c6djs4xvttb0cvz9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MEyIaqPt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/pb1c6djs4xvttb0cvz9g.png" alt="Alt Text" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In code, it's not pretty. Lots of manual column-width calculation. Manual row banding. Manual row formulation in a dedicated row view. In the end, it involved writing lots of code to get something that &lt;em&gt;approximated&lt;/em&gt; the aesthetic of an &lt;code&gt;NSTableView&lt;/code&gt;, but without all the functionality that &lt;code&gt;NSTableView&lt;/code&gt; includes.&lt;/p&gt;

&lt;p&gt;Creating an &lt;code&gt;NSTableView&lt;/code&gt; also involves writing lots of code. However, you get a lot of bang for your buck. Ideally, we could slap an &lt;code&gt;NSTableView&lt;/code&gt; straight into our SwiftUI app. Fortunately, we can!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create &lt;code&gt;NSViewController&lt;/code&gt; / &lt;code&gt;NSTableView&lt;/code&gt; as normal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;AppKit&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PlayerNSTableView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSTableView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define your NSTableView subclass as you would in an AppKit app&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;PlayerNSTableController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define your NSViewController subclass as you would in an AppKit app&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create an &lt;code&gt;NSViewControllerRepresentable&lt;/code&gt; wrapper
&lt;/h3&gt;

&lt;p&gt;SwiftUI is all about &lt;code&gt;View&lt;/code&gt; implementations. AppKit is all about &lt;code&gt;NSViewController&lt;/code&gt;, &lt;code&gt;NSView&lt;/code&gt;, the relationships between them, and a bunch of other crap. &lt;code&gt;NSViewControllerRepresentable&lt;/code&gt; is the bridge between the AppKit and SwiftUI worlds. It is itself a &lt;code&gt;View&lt;/code&gt; conforming protocol, and can be passed around in SwiftUI like any other &lt;code&gt;View&lt;/code&gt; struct.&lt;/p&gt;

&lt;p&gt;Create a struct conforming to &lt;code&gt;NSViewControllerRepresentable&lt;/code&gt;, and add the required protocol stubs. Observe the tangled mess that results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;AppKit&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;SwiftUI&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;DraftSport&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;PlayerNSTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSViewControllerRepresentable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;@Binding&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;players&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="kd"&gt;typealias&lt;/span&gt; &lt;span class="kt"&gt;NSViewControllerType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PlayerNSTableController&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;makeNSViewController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSViewControllerRepresentableContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;PlayerNSTable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PlayerNSTableController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;PlayerNSTableController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;updateNSViewController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;nsViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PlayerNSTableController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSViewControllerRepresentableContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;PlayerNSTable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;players&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;nsViewController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a lot to unpack here. Never fear, the only bit you really need to care about is the body of &lt;code&gt;makeNSViewController()&lt;/code&gt;. Inside the body, you will see &lt;code&gt;return PlayerNSTableController()&lt;/code&gt;. That's the key: Initialise the &lt;code&gt;NSViewController&lt;/code&gt; subclass you defined in Step 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Manipulate the wrapped &lt;code&gt;NSViewController&lt;/code&gt; from SwiftUI
&lt;/h3&gt;

&lt;p&gt;We're obviously going to want to manipulate the AppKit table data from SwiftUI. In the above &lt;code&gt;NSViewControllerRepresentable&lt;/code&gt; implementation, you will notice the line &lt;code&gt;@Binding var players: Array&amp;lt;Player&amp;gt;?&lt;/code&gt;. This is a bridge passing data between the SwiftUI and AppKit worlds.&lt;/p&gt;

&lt;p&gt;Like any other struct conforming to &lt;code&gt;View&lt;/code&gt;, &lt;code&gt;PlayerNSTable&lt;/code&gt; may observe changes to its properties and update the GUI in response.  The magic sauce is the &lt;code&gt;@Binding&lt;/code&gt; decorator. Now, when &lt;code&gt;.players&lt;/code&gt; is changed, the &lt;code&gt;.updateNSViewController()&lt;/code&gt; method of our &lt;code&gt;NSViewControllerRepresentable&lt;/code&gt; is called.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;.updateNSViewController()&lt;/code&gt; we can do whatever we like. I happen to expose a &lt;code&gt;.refresh(:Array&amp;lt;Player&amp;gt;)&lt;/code&gt; method on my &lt;code&gt;PlayerNSTableController&lt;/code&gt;, which ultimately calls &lt;code&gt;.reloadData()&lt;/code&gt; on the underlying &lt;code&gt;NSTableView&lt;/code&gt;. It's up to you how you want to feed new data into your &lt;code&gt;NSViewController&lt;/code&gt; / &lt;code&gt;NSTableView&lt;/code&gt; pair.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add your wrapped AppKit view to a SwiftUI view
&lt;/h3&gt;

&lt;p&gt;You can now add your &lt;code&gt;NSViewControllerRepresentable&lt;/code&gt; implementation to any SwiftUI &lt;code&gt;View&lt;/code&gt;. Here's my implementation, &lt;code&gt;PlayerNSTable&lt;/code&gt;, being added to an enclosing &lt;code&gt;View&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;SwiftUI&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;DraftSport&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;PlayerTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;players&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;PlayerNSTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;players&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$players&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;topLeading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onAppear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;retrievePlayers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;retrievePlayers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
        &lt;span class="kt"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieveMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;season&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Season&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2020"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;players&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;fatalError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No players"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that when &lt;code&gt;PlayerNSTable&lt;/code&gt; appears, &lt;code&gt;Player&lt;/code&gt; data are retrieved via the &lt;code&gt;retrievePlayers()&lt;/code&gt; method. As with any other SwiftUI &lt;code&gt;View&lt;/code&gt;, changes to &lt;code&gt;@State&lt;/code&gt; decorated properties are observed. When &lt;code&gt;Player.retrieveMany()&lt;/code&gt; executes its callback closure, the &lt;code&gt;.players&lt;/code&gt; property is updated. This update ultimately triggers the &lt;code&gt;updateNSViewController()&lt;/code&gt; method we discussed in Step 2.&lt;/p&gt;

&lt;p&gt;The end result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FPCofpye--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/ugtzcpr41gxwlc418p1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FPCofpye--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/ugtzcpr41gxwlc418p1u.png" alt="" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A fully functional &lt;code&gt;NSTableView&lt;/code&gt; inside a SwiftUI &lt;code&gt;View&lt;/code&gt; hierarchy. Phew! That was intense. If you have any questions, find me &lt;a href="https://twitter.com/hugh_jeremy"&gt;@hugh_jeremy&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>swiftui</category>
      <category>appkit</category>
      <category>swift</category>
      <category>macos</category>
    </item>
    <item>
      <title>SwiftUI first impressions / pain analysis</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Wed, 18 Mar 2020 22:38:49 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/swiftui-first-impressions-pain-analysis-5d6i</link>
      <guid>https://forem.com/hugh_jeremy/swiftui-first-impressions-pain-analysis-5d6i</guid>
      <description>&lt;p&gt;For years I've been occasionally dabbling in macOS native application development, and for years I've been resisting the urge to throw my MacBook out the window. Any delusions I've occasionally had about releasing native macOS apps have been flattened by the AppKit pain train.&lt;/p&gt;

&lt;p&gt;Where's my Delegate? Or was it the Controller, Model, or View that I needed? Was it in my Storyboard? Xib? Nib? Jib? Why is Xcode crashing? What is this non-deterministic compiler error? &lt;/p&gt;

&lt;p&gt;I can see how a dedicated developer could become a weapon with AppKit, but if you're multi-disciplinary the time input/reward ratio is just way off.&lt;/p&gt;

&lt;p&gt;Apple has presumably realised this, hence the new kid on the block: SwiftUI. I'd heard about this new-fangled "SwiftUI". It was only this week that my urge to self-flaggellate was strong enough to try it. So I fired up Xcode, and...&lt;/p&gt;

&lt;p&gt;Well. What a pleasant surprise. Everything, and I mean the whole damn production, is boiled down into one glorious concept: The &lt;code&gt;View&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This rustles my jimmies in all the right directions. The only way I ever made any progress with AppKit was to ignore its psychotic smorgasbord of concepts and build self-contained &lt;code&gt;NSView&lt;/code&gt; subclasses. Then, those subclasses could be composed into a UI.&lt;/p&gt;

&lt;p&gt;That's effectively SwiftUI in a nutshell. You create &lt;code&gt;View&lt;/code&gt; implementations, and then you add them to each other. That's it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;View&lt;/code&gt; is a protocol, not a class. No inheritance. You have full flexibility to initialise this bad boy however the hell you want. They're just fresh-off-the-stack structs with a &lt;code&gt;.body&lt;/code&gt; property requirement.&lt;/p&gt;

&lt;p&gt;Could it get better? Yes it could get better. Some people are imperative people. The want to tell you &lt;em&gt;how&lt;/em&gt; to do something, rather than &lt;em&gt;what&lt;/em&gt; they want done. These people have titles like "[insert JavaScript framework] Evangelist" and you should socially distance yourself from these people.&lt;/p&gt;

&lt;p&gt;Better people are declarative people. They tell you what the objective is, and then leave you alone to achieve it. SwiftUI is a declarative person. The difference in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Imperative Python
&lt;/span&gt;&lt;span class="n"&gt;relevant_heights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;forest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;relevant_heights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevant_heights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Declarative Python
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;forest&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first example, we told Python how to find the right tree heights. In the second example, we told Python what we wanted and let it work out the implementation details itself.&lt;/p&gt;

&lt;p&gt;If you've ever worked in SQL, you will be familiar with the declarative style, because SQL is entirely declarative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Declarative SQL */&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;   &lt;span class="n"&gt;trees&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;  &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The declarative attitude is the one required to grok the following:&lt;br&gt;
&lt;/p&gt;

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

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Top row text"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bottom row text"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;    
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We declare that we would like a vertical stack of views (&lt;code&gt;VStack&lt;/code&gt;), inside of which we would like two pieces of text (&lt;code&gt;Text&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XXgVgye_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dmmrbb90gcaifbp2cxlr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XXgVgye_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dmmrbb90gcaifbp2cxlr.png" alt="Alt Text" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I first saw this, I was frustrated. Where exactly do we return from inside the &lt;code&gt;VStack&lt;/code&gt;? And what is returned? How can two &lt;code&gt;Text&lt;/code&gt; declarations magically turn into two lines of text?&lt;/p&gt;

&lt;p&gt;Being an absolute SQL fanatic, I found peace by accepting that we are declaring &lt;em&gt;what&lt;/em&gt; we want rather than &lt;em&gt;how&lt;/em&gt; we want it done. Writing Swift code against the SwiftUI API is more like writing SQL than say, imperative AppKit code.&lt;/p&gt;

&lt;p&gt;We can mix and match views willy-nilly. Say we declare an &lt;code&gt;H1&lt;/code&gt; view, which in our application will be roughly analogous to &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; in HTML:&lt;br&gt;
&lt;/p&gt;

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

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;headingText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headingText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;design&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now use &lt;code&gt;H1&lt;/code&gt; inside our earlier &lt;code&gt;Header&lt;/code&gt; example:&lt;br&gt;
&lt;/p&gt;

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

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;headingText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Top row text"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bottom row text"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;    
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Producing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YN7GACS1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/jzqj39q5v0hab3mbo8s8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YN7GACS1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/jzqj39q5v0hab3mbo8s8.png" alt="Alt Text" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That really is it, kids. Everything else is a variation on the them of defining &lt;code&gt;View&lt;/code&gt; implementations and composing them together. Nested hierarchies of &lt;code&gt;View&lt;/code&gt; become your application user interface.&lt;/p&gt;

&lt;p&gt;What a pleasant surprise. SwiftUI might be a keeper. Some quick summary thoughts:&lt;/p&gt;

&lt;h2&gt;
  
  
  Good
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Everything above&lt;/li&gt;
&lt;li&gt;Xcode doesn't crash constantly anymore&lt;/li&gt;
&lt;li&gt;Swift package manager is great now&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Bad
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Not many built in controls, re-using AppKit classes (e.g. NSTableView) quickly becomes necessary&lt;/li&gt;
&lt;li&gt;AppKit / SwiftUI interaction is poorly documented&lt;/li&gt;
&lt;li&gt;All the Apple docs are (surprise) focused on UIKit people, &amp;amp; iPhone GUI paradigms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to dive a little deeper? In a follow up, I discuss &lt;a href="https://dev.to/hugh_jeremy/adding-an-nstableview-to-a-swiftui-view-212p"&gt;how to integrate an AppKit NSTableView into a SwiftUI View hierarchy&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/hugh_jeremy"&gt;Hugh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>swift</category>
      <category>swiftui</category>
      <category>macos</category>
      <category>xcode</category>
    </item>
    <item>
      <title>Mixing synchronous and asynchronous requests for serious speed</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Sat, 14 Mar 2020 05:32:05 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/mixing-synchronous-and-asynchronous-requests-for-serious-speed-29gn</link>
      <guid>https://forem.com/hugh_jeremy/mixing-synchronous-and-asynchronous-requests-for-serious-speed-29gn</guid>
      <description>&lt;p&gt;You know what I'm talking about. Those infuriating websites that present animated grey boxes while they fetch their content asynchronously. For &lt;em&gt;seconds&lt;/em&gt;. No one has seconds. Give me the content now!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://draftrugby.com"&gt;Draft Rugby&lt;/a&gt; is a fantasy Rugby app. It's in early development. Our main feature is the &lt;a href="https://draftrugby.com/player-stats"&gt;Player Stats page&lt;/a&gt;. This page is effectively a glorified table: It allows rapid search and sorting of the ~800 players in the Super Rugby season.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3knFKrWJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6o3k1hyskif72vmem7cb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3knFKrWJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6o3k1hyskif72vmem7cb.png" alt="Alt Text" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before today, it loaded pretty quickly. Here's the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A browser makes a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;/player-stats&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Draft Rugby replies with a bunch of HTML, CSS, and JS&lt;/li&gt;
&lt;li&gt;The browser runs the JS, which includes an immediate &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;/api/fantasy/player/list&lt;/code&gt; vis the &lt;a href="https://github.com/hwjeremy/draft-sport-js"&gt;Draft Sport JS library&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Draft Rugby replies with a bunch of JSON&lt;/li&gt;
&lt;li&gt;The browser eats the JSON and fills the player table&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 3 is an asynchronous javascript request leading to document manipulation, commonly known as "AJAX". That's nice, because the user can now sort and search the table. Each time they do, more asynchronous requests are made to get them the data they want, and refill the table.&lt;/p&gt;

&lt;p&gt;Except it's not &lt;em&gt;always&lt;/em&gt; nice, because of the speed of light. In an ideal case, with a client device say, 30 kilometres from the datacenter, there might be 50 milliseconds between the start of step 1 and the start of step 3. In Draft Sport's case it was taking a whopping ~270ms to finish the whole sequence and begin animating the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0AMlRoLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/lsfouiyz4ed0ag06strc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0AMlRoLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/lsfouiyz4ed0ag06strc.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No one has time for 270ms! A user will notice this delay, without question. And it get worse: Your user's visual processing system needs to parse your fancy loading animation while the async request is happening. Then it needs to dump that information and re-parse the actual page content.&lt;/p&gt;

&lt;p&gt;Don't do this! It sucks! Let's shift the initial table load back onto the server. Now the sequence looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A browser makes a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;/player-stats&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Draft Rugby replies with a bunch of HTML, CSS, and JS, including the content of the player stats table retrieved via &lt;a href="https://github.com/hwjeremy/draft-sport-py"&gt;Draft Sport Py&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The browser paints everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From 5 steps to 3. Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No double round-trip to the datacenter to fetch the initial data&lt;/li&gt;
&lt;li&gt;No loading animations for the user to parse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's the tradeoff? It depends on the disposition of your API. Draft Sport API is not the fastest thing in the world, yet - It takes about 50ms to retrieve the player table. That request now blocks the time-to-first-byte, slowing the page delivery down by 50ms.&lt;/p&gt;

&lt;p&gt;The synchronous result is still way better. The time until content is fully presented drops from about ~450ms to ~200ms. As Draft Sport API matures and gets faster, that time will drop further, whereas the speed of light isn't going anywhere. And in the real world, your user is not going to be 20ms from your datacenter. The further away they are, faster the synchronous request becomes. Your framework can't outrun the speed of light!&lt;/p&gt;

&lt;p&gt;Conclusion? Don't be afraid to hold up returning your first byte with a server-side API request. If you know what data the client wants, your overall time to displayed content will probably be significantly lower than if you return it asynchronously via an AJAX request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/hugh_jeremy"&gt;-Hugh&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>python</category>
      <category>asynchronous</category>
      <category>performance</category>
    </item>
    <item>
      <title>Cognition-oriented Programming</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Mon, 02 Dec 2019 21:17:51 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/cognition-oriented-programming-3359</link>
      <guid>https://forem.com/hugh_jeremy/cognition-oriented-programming-3359</guid>
      <description>&lt;p&gt;Programming is addictive. As programmers we have immense power. We can create machines of practically unlimited scale and scope. Our creations may yield immense joy and utility. In code anything seems possible.&lt;/p&gt;

&lt;p&gt;We programmers are still human. We are delicate, non-deterministic machines with squishy bodies and even squishier minds. Our capacity to build software is a function of our capacity to direct the limited resources of our bodies and minds.&lt;/p&gt;

&lt;p&gt;Programming paradigms provide loose rulesets to help us direct those resources. We adopt functional-oriented, object-oriented, data-oriented, or some other programming orientation. These paradigms are well established and serve wonderfully.&lt;/p&gt;

&lt;p&gt;They are top-level distinctions that sit on top of a huge stack of programming techniques, patterns, fads, and principles. Much joy comes from exploring new ways to write code, and finding out what allows each of us to most effectively reach our goals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Science gets us some of the way
&lt;/h2&gt;

&lt;p&gt;What should drive that exploration? How can we know whether we should adopt a data-oriented approach to our latest project? The decision is often made out to be one of computer science. What paradigm best fits the requirements of the system?&lt;/p&gt;

&lt;p&gt;A game programmer might generally require their program to respond to user input in 16 milliseconds or less. They might be well served in trading some readability for intensely optimised hot-loops that keep as much data in L1 cache as possible. Perhaps data-oriented C++?&lt;/p&gt;

&lt;p&gt;A web programmer might have bit more room to move, client-side progress animations and common acceptance of network latency yielding an acceptable response time of 20 - 150 milliseconds. They might be well served optimising for readability. Perhaps object-oriented Ruby?&lt;/p&gt;

&lt;p&gt;Yet the choice is rarely so clear cut. The latest fads, frameworks, and celebrity-programmer proclamations might influence us in different directions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think first, paradigm later
&lt;/h2&gt;

&lt;p&gt;Our guide should be the disposition of our own cognition. Each of us has a different mind. Your brain has developed differently to mine. Different experiences, training, and genetic-predispositions conspire to influence the manner in which we reason about code.&lt;/p&gt;

&lt;p&gt;This is quite abstract. Let us examine a practical example. Consider a single class definition. How long is too long? 200 lines? 1,000 lines? What if we mix multiple classes into a single file?&lt;/p&gt;

&lt;p&gt;You might be comfortable working with files thousands of lines long. Having witnessed such capability, I initially worked in this manner as well. Over many years I realised that I was not capable of maintaining a mental map of so much data. Now, I aim for a maximum of 150 lines per file, with strictly one class definition per file.&lt;/p&gt;

&lt;p&gt;If a definition becomes longer than 150 lines, I actively ponder breaking it into smaller composable parts. As I move through my programs, I operate on a maximum of two files at a time, working on the flow of data from to another. In this way, I am always operating on a maximum of approximately 300 lines of code.&lt;/p&gt;

&lt;p&gt;To you, this might sound absurd. You might have huge files and be perfectly comfortable surfing through them. This is exactly the point: We are all different, and we must seek to write code in a manner that supports our own cognition. Call it “cognition-oriented programming”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cognition for Teams
&lt;/h2&gt;

&lt;p&gt;In a team, this might appear to present problems. What if your CTO likes massive files and you don’t? I suggest that in a well-gel’d team, leadership is setting objectives and standards, not methods.&lt;/p&gt;

&lt;p&gt;That is, you are ideally told what you need to do and the bounds within which you need to do it, not how to do it. In such an environment, you might adopt a team-wide code-style guide. You might have rules such as “no global variables” and “no mutable state”.&lt;/p&gt;

&lt;p&gt;Then, within these bounds, you might focus on providing maintainable, reasonable, and clear public interfaces for your code. Behind those interfaces, and while always maintaining compliance with the set bounds, you may lean towards optimising for your own cognition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Optimised Mental Model
&lt;/h2&gt;

&lt;p&gt;The concept of being “in flow” is well studied. Cognition-oriented programming might be viewed as a subset of the requirements for being “in flow”. At the highest level, it may be viewed as the search for your own hyper-optimised mental model.&lt;/p&gt;

&lt;p&gt;Your brain has some strict finite limits. Your short-term working memory is likely limited to storing around seven “objects” at once. Your long-term memory might store pointers to every corner of your 200,000 line program, but at any instant, you can only reason with reference to some small subset of that code.&lt;/p&gt;

&lt;p&gt;How you structure that limited reference is crucial. Think of your working memory as your own L1 cache. In paradise, your program is crunching cache data at warp speed, never needing to reach out to comparatively glacial RAM. In maximum flow, your mind is crunching on concepts stored in your working memory set. You reason about them at immense speed and with great precision.&lt;/p&gt;

&lt;p&gt;In reality, our real programs need to go to RAM. Worse, they might need to access persistent storage. Heaven-forbid they need to take the long voyage out through the network stack. This is true for our mental models as well. In our metaphor, RAM might be some other piece of our codebase. Storage might be referencing some documentation. Network I/O might be talking to another human being.&lt;/p&gt;

&lt;p&gt;All of those operations are crucial and important. Optimising them will yield more time in nirvana, with your optimised mental model facilitating a flow of performant, reliable, and reasonable code. The feeling, in my experience, is close to that of playing a musical instrument. The dance of fingers across the keys is unending, rhythmic, and perhaps even spiritual.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seek Nirvana
&lt;/h2&gt;

&lt;p&gt;Finding your cognition-oriented model takes time. Try new patterns. Read widely. Observe what works for some very successful programmers. Not because you should imitate them, but because their models might provide inspiration for some part of your own.&lt;/p&gt;

&lt;p&gt;Engage in trial and error. Construct, destruct, and modify systems. All the while, observe yourself. Do you feel transcendent? Or do you feel as though you are straining to keep a grip on the program? Do you instantly see the path to a new feature, fixed bug, or performance improvement? Or do you strain to comprehend how to proceed?&lt;/p&gt;

&lt;p&gt;These questions will enable you to find the patterns that map to your own cognition. Do not force your patterns on others. Do not presume others’ patterns will work for you. Code is the tool that enables you, as a squishy mammal, to dent the universe. Seek to make it your own.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>productivity</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Feedback for junior developers</title>
      <dc:creator>Hugh Jeremy</dc:creator>
      <pubDate>Wed, 29 May 2019 21:09:31 +0000</pubDate>
      <link>https://forem.com/hugh_jeremy/feedback-for-junior-developers-2mab</link>
      <guid>https://forem.com/hugh_jeremy/feedback-for-junior-developers-2mab</guid>
      <description>&lt;p&gt;When I was younger and dumber, I charged pretty hard. Sometimes I would upset people by pushing too hard for an end result. I didn't consider how my actions would affect people around me. Back then the damage I did was usually a small, though still unacceptable, as I was junior cog in the machine.&lt;/p&gt;

&lt;p&gt;These days, I am in senior positions with responsibility for the output of junior developers. The team's collective output depends on everyone having high morale and the confidence to take risks, make mistakes, and learn continuously.&lt;/p&gt;

&lt;p&gt;Young Hugh's hard-charging ways weren't acceptable then, and they would be devastating if applied from a senior position.&lt;/p&gt;

&lt;p&gt;Of course, I often copped it as good as I gave it. I have painful memories of senior people shitting on my best efforts. It's an awful feeling to have tried hard to find a creative solution to a problem, only to be brutally shut down by someone in a position of power.&lt;/p&gt;

&lt;p&gt;This effect is amplified by people's relative tolerance for feedback. Some people have high self-confidence, and are generally better able to recover from the morale hit of harsh feedback. Others are gentler souls, very sensitive to criticism and vulnerable to retreating from creative development in the face of harsh feedback.&lt;/p&gt;

&lt;p&gt;You won't necessarily know who's who. Some of your developers might look like they can roll with the punches, but are quietly de-motivated to work with you. Personally I think I give off a confident persona: I will get up on stage to give a speech, &lt;a href="https://twitter.com/hugh_jeremy/status/1133739308169359368"&gt;post stupid videos&lt;/a&gt;, and put my hand up first to take on a big challenge. Yet I have cried openly in front of colleagues, on multiple occasions.&lt;/p&gt;

&lt;p&gt;Conclusion: We should take great care in how we offer feedback to our colleagues. In particular, junior developers who are at a formative stage in their code-crushing adventure. I don't have an all-conquering strategy about how to best give feedback.&lt;/p&gt;

&lt;p&gt;To provoke discussion, I present an email I sent earlier today. The content is esoteric to the particular project, so we'll have to ignore that and instead focus on the style of the feedback. How does this make the recipient feel?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hi [Junior Colleague]!&lt;/p&gt;

&lt;p&gt;I’ve made some adjustments to the FAQ page, and I’ve got some feedback on your code. This is not because you did anything wrong. You did great! Instead, it’s about me applying a consistent set of code rules to the [App] codebase, that allow me to keep the whole thing straight in my head.&lt;/p&gt;

&lt;p&gt;What I’m doing here is not necessarily better or worse than what you did. Instead, it’s just my style. Because I need to maintain responsibility for the whole app, I need to enforce my style or I my mental map of the whole app can break down.&lt;/p&gt;

&lt;p&gt;Moral of the story: Do what you need to do to get the job done. Sometimes I might come in with feedback like this, but it’s not because you did anything wrong. In fact, you did right because you got the required feature working! I will always be thrilled when you add functionality, regardless of whether or not it adheres to these rules.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CSS Namespacing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Good job creating &lt;code&gt;accordion.css&lt;/code&gt;. I’ve plopped it in &lt;code&gt;shared/accordian.css&lt;/code&gt; simply for organisational purposes. Inside, ensure all your classes are namespaced: &lt;code&gt;.accordion-active&lt;/code&gt; instead of &lt;code&gt;.active&lt;/code&gt;, &lt;code&gt;.accordian-panel&lt;/code&gt; instead of &lt;code&gt;.panel&lt;/code&gt;. Doing so means we never have to worry about conflicts between different stylesheets, we can mix and match them safely. You did this for almost all the classes, nice work!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Inline Javascript&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ensure all Javascript is added via the View superclass, rather than inline in an HTML template. I’ve moved things into a &lt;code&gt;scripts/other/faq.js&lt;/code&gt; script and &lt;code&gt;classes/shared/accordian.js&lt;/code&gt; class. The View superclass does a whole bunch of Javascript management, and it can’t work its magic if Javascript is inline in an HTML template.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Javascript Variables&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Never use &lt;code&gt;var&lt;/code&gt; - It puts the variable in global scope. Using &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; means that we can safely mix and match classes and scripts without them ever stepping on each others toes. Generally everything should be &lt;code&gt;const&lt;/code&gt; unless there is an extremely compelling reason to use a &lt;code&gt;let&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Javascript Objects&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Err on the side of object-oriented code, rather than procedural code. Procedural code is generally identifiable as a series of steps manipulating low-level datatypes like DOM elements and strings. Object oriented code usually (if done well!) looks a little more like English, with phrases composed of high-level types like ‘Accordian’.&lt;/p&gt;

&lt;p&gt;Generating object-oriented code might involve creating a mental map of the nouns you want in play, and the verbs that will act on those nouns. Nouns become classes and verbs become methods. &lt;/p&gt;

&lt;p&gt;E.g. “I want to toggle the open state of a bunch of accordions”. Our key noun is “accordian”: That will become our class. It has an open state that it will stored as an internally/private property. The key verb is is “toggle”: That will become our class method. Because we want a “bunch” of them, we will create a static class method that allows us to build a collection of Accordians.&lt;/p&gt;

&lt;p&gt;This means you can compose code that sounds more like English, e.g. “accordian.toggle();” or “const accordions = Accordian.fromElementsWithClassName(’accordian');". I find this easier to maintain when I inevitably come to look at it again months later.&lt;/p&gt;

&lt;p&gt;Doesn’t make any sense? Don’t fret! I’m not necessarily a very good teacher! You can see how your accordian code was translated by looking at classes/shared/accordian.js. Please feel free to charge some time to [Client] while looking through this stuff. Learning is part of the job!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conclusion&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You did nothing wrong, and everything right. This is all about the personal style I’m enforcing so I can execute my responsibility to advance the entire application. Other developers will have other styles, you will have your own style. I will always be thrilled with you create new functionality, regardless of what style you use.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

&lt;p&gt;Hugh&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How was that? What would you change? I think this email follows a few feedback patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Acknowledge successes&lt;/li&gt;
&lt;li&gt;Encourage future experimentation&lt;/li&gt;
&lt;li&gt;Give reasoning behind desired changes&lt;/li&gt;
&lt;li&gt;Provide helpful hints for gaining knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These patterns are self developed, and self identified. I wonder if you know of any great guides on how to give good feedback. Do you have any stories you can share about when giving or receiving feedback went well, or badly?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/hugh_jeremy"&gt;Hugh&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>beginners</category>
      <category>happiness</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
