<?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: Lee Jarvis</title>
    <description>The latest articles on Forem by Lee Jarvis (@leejarvis).</description>
    <link>https://forem.com/leejarvis</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%2F145505%2F576e8f2f-f78f-4076-83bf-8c4e2fb7fb7a.jpeg</url>
      <title>Forem: Lee Jarvis</title>
      <link>https://forem.com/leejarvis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/leejarvis"/>
    <language>en</language>
    <item>
      <title>Interactive Shells in Capistrano</title>
      <dc:creator>Lee Jarvis</dc:creator>
      <pubDate>Wed, 16 Oct 2019 15:57:41 +0000</pubDate>
      <link>https://forem.com/leejarvis/interactive-shells-in-capistrano-5a7b</link>
      <guid>https://forem.com/leejarvis/interactive-shells-in-capistrano-5a7b</guid>
      <description>&lt;p&gt;&lt;a href="https://capistranorb.com"&gt;Capistrano&lt;/a&gt; is a very popular deployment tool written in Ruby. If you've deployed Ruby or Rails applications, chances are you've heard of it even if you haven't had the chance to use it.&lt;/p&gt;

&lt;p&gt;Capistrano was a crucial part of our deployment process at &lt;a href="https://loco2.com"&gt;Loco2&lt;/a&gt; long before &lt;a href="https://www.jonathanleighton.com/articles/2016/loco2-infrastructure-overhaul/"&gt;overhauling our hosting infrastructure&lt;/a&gt; with Terraform and Docker.&lt;/p&gt;

&lt;p&gt;Having &lt;a href="https://leejarvis.me/posts/2019/leaving-loco2"&gt;recently left Loco2&lt;/a&gt;, I found myself back on a project that uses Capistrano. As powerful as it is, it frustratingly doesn't include support for interactive shells. Whilst this is &lt;a href="https://capistranorb.com/documentation/advanced-features/ptys/"&gt;by design&lt;/a&gt;, it's a bit annoying if you want to boot a rails console or run another command that allows input from the user.&lt;/p&gt;

&lt;p&gt;After doing some Googling I managed to piece together something that calls &lt;code&gt;ssh&lt;/code&gt; directly, allowing us to run a command in an interactive shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:rails&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Start a rails console"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:console&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;exec_interactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"rails console"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Start a rails dbconsole"&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:dbconsole&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;exec_interactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"rails dbconsole"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exec_interactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:web&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hostname&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"RAILS_ENV=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rails_env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# add other ENV variables&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;release_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bundle exec &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Running command on &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="sx"&gt;%(ssh #{host} -t "sh -c '#{command}'")&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;primary(:web).hostname&lt;/code&gt; just fetches the hostname for the first/primary web host. It's worth mentioning this because it &lt;em&gt;might&lt;/em&gt; not be desirable. You could quite easily fetch all of the remote hosts and provide a menu that allows the user to select the host they want to connect to. For my purposes though, I just wanted to run a rails console without having a custom script that fetched our hostname via the AWS command line tools.&lt;/p&gt;

&lt;p&gt;You may want to consider safeguarding production access by adding a warning message when &lt;code&gt;fetch(:rails_env) == "production"&lt;/code&gt;, or perhaps add the &lt;code&gt;--sandbox&lt;/code&gt; option to protect your data.&lt;/p&gt;

&lt;p&gt;I believe developers should be trusted to have direct access to all live&lt;br&gt;
environments, including production. These sorts of small convenience scripts really help to avoid friction when troubleshooting or debugging data issues on non-local environments.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>capistrano</category>
    </item>
    <item>
      <title>Clean Ruby Codebases</title>
      <dc:creator>Lee Jarvis</dc:creator>
      <pubDate>Tue, 02 Jul 2019 15:51:00 +0000</pubDate>
      <link>https://forem.com/leejarvis/clean-ruby-codebases-1dhe</link>
      <guid>https://forem.com/leejarvis/clean-ruby-codebases-1dhe</guid>
      <description>&lt;p&gt;&lt;em&gt;original article: &lt;a href="https://leejarvis.me/posts/2019/clean-ruby-codebases"&gt;https://leejarvis.me/posts/2019/clean-ruby-codebases&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I enjoy reading code. Even the smallest snippet from a distant corner of&lt;br&gt;
an obscure library or plugin could be enough to spark a new idea, and&lt;br&gt;
at the very least it might evoke interest, repulsion (usually when reading my old code), or simply teach me something new.&lt;/p&gt;

&lt;p&gt;As the saying goes, code is read much more often than it is written. It takes many years of experience to write great code, and nobody gets there without having read a lot of it.&lt;/p&gt;

&lt;p&gt;I started writing code before JavaScript became popular, when learning&lt;br&gt;
HTML meant copying the contents of your favourite website's source code&lt;br&gt;
and tweaking it to fit your purpose. New websites introduce new HTML tags and CSS rules, long before the prominence of w3schools.&lt;/p&gt;

&lt;p&gt;Naturally I became a bit stuck when I started learning server-side &lt;br&gt;
languages. At the time I couldn't really afford the books; I really&lt;br&gt;
wanted to sample everything and see what sticks.&lt;/p&gt;

&lt;p&gt;Reading code I couldn't understand became regular, even nuking several&lt;br&gt;
OS installs during my frivolous experimentations didn't stop me. I learned to appreciate clean, well-written code before I could really write it myself. GitHub eventually came along to make this experience effortless.&lt;/p&gt;

&lt;p&gt;When speaking to new developers I have a few examples I use to &lt;br&gt;
demonstrate what I appreciate as well-readable Ruby code. There's&lt;br&gt;
a level of subjectivity to this of course, but for me, clean code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is straightforward and obvious&lt;/li&gt;
&lt;li&gt;Is easy to digest&lt;/li&gt;
&lt;li&gt;Generally follows common style best-practices&lt;/li&gt;
&lt;li&gt;Is well structured&lt;/li&gt;
&lt;li&gt;Avoids too much magic/metaprogramming&lt;/li&gt;
&lt;li&gt;Contains comments only where they are helpful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, without further ado, here are a few of my favourites:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/mperham/sidekiq/"&gt;Sidekiq&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sidekiq is a library for processing background jobs. It's pretty much &lt;br&gt;
considered the go-to tool for Ruby and Rails developers who want to store&lt;br&gt;
their queue data in Redis. What is a fairly complex piece of software&lt;br&gt;
is written so well that it basically holds your hand along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/jeremyevans/sequel"&gt;Sequel&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sequel is a database toolkit for Ruby. It's a strong replacement for&lt;br&gt;
ActiveRecord and is my ORM of choice for non-Rails projects. It's 12&lt;br&gt;
years old and was one of the first Ruby libraries I ever encountered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gitlab.com/yorickpeterse/oga"&gt;Oga&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Oga is an XML/HTML parser written in Ruby (crazy, right?). Whilst&lt;br&gt;
Oga is no longer maintained, it remains a good example of nice-to-read&lt;br&gt;
code. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/hanami/hanami"&gt;Hanami&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hanami is a young Ruby web framework. It's designed to combat the extreme&lt;br&gt;
bloat that Ruby on Rails introduces with its "everything including the &lt;br&gt;
kitchen sink" approach. The project is split into smaller pieces making&lt;br&gt;
it easy to digest and poke around. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/httprb/http"&gt;HTTP&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;HTTP is my go-to Ruby library for making HTTP requests. It's fast, easy&lt;br&gt;
to use (who on earth remembers the &lt;code&gt;net/http&lt;/code&gt; syntax?), and with a lack&lt;br&gt;
of "magic" code, it's really easy to understand what's happening under&lt;br&gt;
the hood.&lt;/p&gt;




&lt;p&gt;Do you have any favourite codebases?&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>discuss</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What's your ideal software stack?</title>
      <dc:creator>Lee Jarvis</dc:creator>
      <pubDate>Thu, 20 Jun 2019 18:52:15 +0000</pubDate>
      <link>https://forem.com/leejarvis/what-s-your-ideal-software-stack-1je8</link>
      <guid>https://forem.com/leejarvis/what-s-your-ideal-software-stack-1je8</guid>
      <description>&lt;p&gt;With technology moving as quickly as it does, this question rarely fails to elicit interesting conversation.&lt;/p&gt;

&lt;p&gt;From frontend JavaScript frameworks to infrastructure; what's your ideal software stack and why?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>SwiftUI ScrollViews</title>
      <dc:creator>Lee Jarvis</dc:creator>
      <pubDate>Thu, 20 Jun 2019 08:08:55 +0000</pubDate>
      <link>https://forem.com/leejarvis/swiftui-scrollviews-1mp7</link>
      <guid>https://forem.com/leejarvis/swiftui-scrollviews-1mp7</guid>
      <description>&lt;p&gt;Original post: &lt;a href="https://leejarvis.me/posts/2019/swiftui-scrollviews" rel="noopener noreferrer"&gt;https://leejarvis.me/posts/2019/swiftui-scrollviews&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recently wrote about my &lt;a href="https://leejarvis.me/posts/2019/wwdc19" rel="noopener noreferrer"&gt;WWDC highlights&lt;/a&gt;, and how SwiftUI was a huge surprise announcement for me. I decided to have a play and build something small.&lt;/p&gt;

&lt;p&gt;A few years ago I created an iOS app that tracks gift ideas for family and friends. It's not something I ever published to the app store, but I do keep it for personal use.&lt;/p&gt;

&lt;p&gt;The app isn't particularly exciting. It stores gift ideas and keeps track of birthdays, anniversaries, etc. On the app homescreen I have a&lt;br&gt;
horizontal scroll view that shows some of my upcoming events; "Mum's birthday", "My Wedding Anniversary".&lt;/p&gt;

&lt;p&gt;These event "cards" were made up of a name and a custom emoji/background colour. To drastically simplify, they looked something like this (but better, I promise):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flga5h23usggenmcqha3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flga5h23usggenmcqha3c.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ScrollView wasn't especially difficult to build, but asking AutoLayout to&lt;br&gt;
play nicely across all devices in any orientation wasn't exactly my idea of fun. Not only was it tedious and time-consuming, it was really sucking&lt;br&gt;
the fun out of creating the app.&lt;/p&gt;

&lt;p&gt;With SwiftUI, I was able to re-create a similar ScrollView with just several lines of code:&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;struct&lt;/span&gt; &lt;span class="kt"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Identifiable&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;name&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;let&lt;/span&gt; &lt;span class="nv"&gt;emoji&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;let&lt;/span&gt; &lt;span class="nv"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Color&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;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="kt"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Jon's Birthday"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"🥳"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="kt"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&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="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Wedding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"👰"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="kt"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Aimee's Birthday"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"🎉"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="kt"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Christmas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"🎄"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;purple&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView&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;ScrollView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;HStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;ForEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;in&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="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emoji&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;50&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;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cornerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&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;This is the entire file. Go ahead, paste it into Xcode 11 beta.&lt;/p&gt;

&lt;p&gt;No AutoLayout constraints, no Storyboards, no testing across every device just to be sure I didn't break something. The UI exists right here in the code.&lt;/p&gt;

&lt;p&gt;The declarative syntax really makes the code easy to read and write, and&lt;br&gt;
using Xcode's powerful built-in documentation and suggestion tools,&lt;br&gt;
I found myself creating the user-interface is a very natural way.&lt;/p&gt;

&lt;p&gt;This was built on macOS Mojave, which doesn't have access to the live&lt;br&gt;
preview canvas and drag+drop behaviour. This meant I had to rebuild the&lt;br&gt;
app each time to preview my changes. No bother, I've been doing that&lt;br&gt;
for years. I can only imagine how much better this is on macOS Catalina.&lt;/p&gt;

&lt;p&gt;I've been watching some of the WWDC session videos too. Here's some good ones on SwiftUI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2019/204/" rel="noopener noreferrer"&gt;Introducing SwiftUI: Building Your First App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2019/216/" rel="noopener noreferrer"&gt;SwiftUI Essentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2019/238/" rel="noopener noreferrer"&gt;Accessibility in SwiftUI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All WWDC videos can be &lt;a href="https://developer.apple.com/videos/all-videos/" rel="noopener noreferrer"&gt;found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll be continuing to play with SwiftUI and may write a post or two in&lt;br&gt;
the coming weeks. I am incredibly excited for this technology; it makes&lt;br&gt;
building mobile applications much more accessible. Finally, for me, iOS&lt;br&gt;
development is fun again.&lt;/p&gt;

</description>
      <category>swiftui</category>
      <category>ios</category>
      <category>xcode</category>
    </item>
  </channel>
</rss>
