<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://davidbeck.co/feed.xml" rel="self" type="application/atom+xml" /><link href="https://davidbeck.co/" rel="alternate" type="text/html" /><updated>2026-04-15T22:22:48+00:00</updated><id>https://davidbeck.co/feed.xml</id><title type="html">David Beck</title><subtitle>Personal website for David Beck: husband, father, software developer, geek, fun guy.</subtitle><entry><title type="html">Deep Dish Swift 2026 - Surviving in Low Connectivity</title><link href="https://davidbeck.co/posts/2026-04-14-deepdishswift" rel="alternate" type="text/html" title="Deep Dish Swift 2026 - Surviving in Low Connectivity" /><published>2026-04-14T00:00:00+00:00</published><updated>2026-04-14T00:00:00+00:00</updated><id>https://davidbeck.co/posts/deepdishswift</id><content type="html" xml:base="https://davidbeck.co/posts/2026-04-14-deepdishswift"><![CDATA[<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;">
  <iframe src="https://www.youtube.com/embed/-m5h2jXPp54?start=4145&amp;end=6369" style="position:absolute;top:0;left:0;width:100%;height:100%;" frameborder="0" allowfullscreen=""></iframe>
</div>

<p>I was lucky enough to be selected to talk at Deep Dish Swift 2026. The following is the contents from my slides.</p>

<h2 id="cellular">Cellular</h2>

<p>A bad connection is not the same as a slow connection</p>

<ul>
  <li>Lossy &amp; out-of-order — dropped and misordered packets stall progress</li>
  <li>Highly variable — bands change, signal fluctuates, device moves</li>
  <li>Upload-constrained — upstream bandwidth is the bottleneck</li>
</ul>

<h2 id="ip-tcp--udp">IP (TCP &amp; UDP)</h2>

<p>Reliability comes at a cost</p>

<ul>
  <li>UDP — extremely simple, but gives you no guarantees about delivery or order</li>
  <li>TCP — guarantees in order delivery</li>
</ul>

<h3 id="tcp">TCP</h3>

<p>Guarantees cause bottlenecks</p>

<ul>
  <li>Lost packets block everything behind them — even if newer data has arrived</li>
  <li>Delivery validation is time based — the connection will wait for confirmation before resending packets</li>
  <li>High latency + packet loss leads to the connection intentionally slowing itself down because TCP can’t distinguish between a flaky network and an unresponsive server</li>
</ul>

<p>Connections aren’t free</p>

<ul>
  <li>Multiple round trips — especially when using TLS you need to wait for multiple responses from the server before sending any data</li>
  <li>Every connection has overhead — the os has to keep track of every connection to keep it alive</li>
</ul>

<h2 id="http">HTTP</h2>

<h3 id="http1">HTTP/1</h3>

<p>1 TCP connection per request</p>

<p><img src="/images/2026-04-14-deepdishswift/Slides/DeepDish.008.png" alt="HTTP 1 Diagram" /></p>

<h3 id="http11">HTTP/1.1</h3>

<p>Re-use connections for new requests</p>

<p><img src="/images/2026-04-14-deepdishswift/Slides/DeepDish.009.png" alt="HTTP 1 Diagram" /></p>

<h3 id="http2">HTTP/2</h3>

<p>Multiplexed over TCP</p>

<p><img src="/images/2026-04-14-deepdishswift/Slides/DeepDish.010.png" alt="HTTP 1 Diagram" /></p>

<h3 id="http3">HTTP/3</h3>

<p>Multiplexed over QUIC</p>

<p><img src="/images/2026-04-14-deepdishswift/Slides/DeepDish.011.png" alt="HTTP 1 Diagram" /></p>

<h3 id="cancellation">Cancellation</h3>

<p>Doesn’t always work — you are racing against the request it’s trying to cancel</p>

<ul>
  <li>HTTP/1 can close the TCP connection</li>
  <li>HTTP/2 will send a cancel message but it will probably get trapped in the traffic jam</li>
  <li>HTTP/3 can prioritize stream cancellation, but it still might be too late</li>
  <li>Cancelling unused tasks is good practice, but won’t save you</li>
</ul>

<p><img src="/images/2026-04-14-deepdishswift/Cancellation.png" alt="HTTP Cancellation Diagram" /></p>

<h3 id="timeouts">Timeouts</h3>

<p>Just fancy cancellation</p>

<ul>
  <li>HTTP Timeouts are necessary — TCP and QUIC can retry for a very long time before giving up, and in some circumstances wait forever for a response</li>
  <li>Sometimes retrying fixes the connection — it gives the TCP connection a chance to reset, but sometimes it just needs more time</li>
  <li>URLSession timeout is based on response — large uploads need a longer timeout</li>
  <li>Lookout for retry congestion</li>
</ul>

<h2 id="measure-first">Measure first</h2>

<p>Know what to fix</p>

<ul>
  <li><a href="https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/TestingPerformance.html">Network link conditioner</a> — for everyday debugging</li>
  <li>Use your app in real world conditions — find what is the most critical</li>
  <li>Use a proxy to see everything that your app is doing — not just first party requests (<a href="https://proxyman.com/">Proxyman</a> is my recommendation)</li>
  <li>Have tracing in place (<a href="https://sentry.io/welcome/">Sentry</a>, <a href="https://telemetrydeck.com/">TelemetryDeck</a> etc.) but be careful</li>
  <li>Use <a href="https://developer.apple.com/documentation/foundation/urlsessiontaskmetrics">URLSessionTaskMetrics</a> for accurate measurements</li>
</ul>

<h2 id="send-less-stuff">Send less stuff</h2>

<p><img src="/images/2026-04-14-deepdishswift/Speedtest.png" alt="Speed Test" class="side" style="width: 200px; height: 412px" /></p>

<p>It adds up</p>

<p>While saving 1KB of data on a normal connection is imperceptible, if you can save 1KB on every request it could be the difference between your app working or not in low connectivity.</p>

<p>A pretty typical speed test will show a usable download speed, but the upload is the bottleneck. And latency and packet loss make the actual throughput of a TCP connection even worse. In this example from a neighborhood near my house, the upload speed shows 290 kbps (22 KB/s) but the actual throughput would only be 2.7 KB/s.</p>

<ul>
  <li>Focus on upload</li>
  <li>Look at headers — an empty request may carry 5KB of headers
    <ul>
      <li>Look out for cookies from web views</li>
      <li>Avoid large headers that change often</li>
    </ul>
  </li>
  <li>Compress request bodies if you send lots of JSON</li>
</ul>

<h2 id="prioritize">Prioritize</h2>

<p>Do it in advance</p>

<ul>
  <li>By the time you want to send a high priority request, it’s too late</li>
  <li><a href="https://developer.apple.com/documentation/foundation/urlsessiontask/priority">URLSessionTask.priority</a> doesn’t do much</li>
  <li>Consider artificially limiting low priority requests</li>
</ul>

<h2 id="detecting-low-connectivity">Detecting low connectivity</h2>

<p>Harder than you think</p>

<ul>
  <li>It is very hard to accurately detect low connectivity</li>
  <li>“Expensive” and “constrained” are user settings, not a signal of real world conditions</li>
  <li><a href="https://developer.apple.com/documentation/network/nwpath/linkquality-swift.enum">NWPath.LinkQuality</a> (iOS 26+) provides additional insight</li>
  <li>Round trip time of recent requests is the most reliable metric</li>
</ul>

<h2 id="polling">Polling</h2>

<p>Are we there yet?</p>

<ul>
  <li>Even an empty request can be a few KB</li>
  <li>Use SSE, WebSockets, Long polling if possible</li>
  <li>Space out requests based on response times</li>
</ul>

<h2 id="apns-is-a-gift"><a href="https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns">APNS</a> is a gift</h2>

<p>Use it wisely</p>

<p>Many backend engineers will resist adopting persistent connections like WebSockets because it makes web infrastructure difficult. But APNS is free and provides the persistent connection for you.</p>

<ul>
  <li>4KB limit</li>
  <li>Extremely resilient and efficient</li>
  <li>Rarely blocked — often works even on restricted networks (e.g. airplane Wi-Fi)</li>
  <li>You don’t need permission for data only notifications in the foreground</li>
  <li>But if you do use push alerts don’t ignore the content — an app can receive a notification but not be able to connect to its own server</li>
</ul>

<h2 id="idempotence">Idempotence</h2>

<p>Understand what can be retried</p>

<p>Idempotent (adj.) — Describing an operation that can be performed multiple times with the same input without changing the result beyond the first application.</p>

<p>URLSession will automatically resend GET, HEAD, PUT, DELETE</p>

<p><strong>Make everything idempotent</strong> — you never know what made it to the server</p>

<h2 id="adopt-http3">Adopt HTTP/3</h2>

<p>The time is now</p>

<ul>
  <li>Nearly every CDN now supports it — but you probably will need a CDN</li>
  <li>Networks may still downgrade to TCP — but getting better</li>
  <li>If you know your server uses HTTP/3, turn on <a href="https://developer.apple.com/documentation/foundation/urlrequest/assumeshttp3capable">assumesHTTP3Capable</a></li>
  <li>S3 still only supports HTTP/1</li>
</ul>

<h2 id="designing-for-low-connectivity">Designing for low connectivity</h2>

<p>Avoid blocking loading indicators</p>

<p>The user may want to look at or interact with other things in the app while something is loading, or they may have tapped on something by mistake. If you block the entire app while something is loading, you force them to wait for up to a minute.</p>

<p><img src="/images/2026-04-14-deepdishswift/Slides/DeepDish.028.png" alt="HTTP 1 Diagram" /></p>

<p>Showing actual progress indicators can be useful for large uploads and downloads, but for smaller requests you won’t see any incremental progress, because the request and response will be sent in a single packet.</p>]]></content><author><name></name></author><category term="Low Connectivity" /><category term="Deep Dish Swift" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Trunk Is Now ThinkSocial for Mastodon</title><link href="https://davidbeck.co/posts/2023-02-05-thinksocial" rel="alternate" type="text/html" title="Trunk Is Now ThinkSocial for Mastodon" /><published>2023-02-05T00:00:00+00:00</published><updated>2023-02-05T00:00:00+00:00</updated><id>https://davidbeck.co/posts/thinksocial</id><content type="html" xml:base="https://davidbeck.co/posts/2023-02-05-thinksocial"><![CDATA[<p><img alt="ThinkSocial App Icon" src="/images/2023-02-05-thinksocial/thinksocial-icon.svg" width="200" height="200" style="float: left; width: 200px; margin-right: 20px; margin-bottom: 20px;" /></p>

<p>Trunk, the Mastodon app for iOS with a unique horizontal timeline is now ThinkSocial for Mastodon.</p>

<p>When I was preparing for the original TestFlight launch of the app, I wasn’t even sure I was going to finish the app. I was mostly concerned with creating a proof of concept and getting some immediate feedback about the usefulness of a horizontal timeline.</p>

<p>But to upload an app, even a beta to TestFlight, you have to call it something. And “Untitled Mastodon App” was to big to fit on the homescreen. I searched for a Mastodon body part that wasn’t already taken, and eventually settled on “Trunk”. But everytime I use that name I think of either <a href="https://dragonball.fandom.com/wiki/Trunks">Dragon Ball Z</a>, or <a href="https://en.wikipedia.org/wiki/Swim_trunks">swimwear</a>. And it’s <a href="https://communitywiki.org/trunk">not that</a> <a href="https://mstdn.social/@trunksapp">unique</a>.</p>

<p>Why “ThinkSocial”? For years I have published apps under the “ThinkUltimate” business name<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. None of those apps have been very successful, but they have included “ThinkMessenger”, “ThinkDialer”, “ThinkScore” as well as several others that never shipped. I hope to eventually be able to have a sustainable business making indie apps, and having a consistent naming system seems useful.</p>

<p>If you’re interested in trying out ThinkSocial, you can <a href="https://testflight.apple.com/join/1ir4E70u">join the beta</a> and <a href="https://tnku.co/@david">follow me</a> for more updates. ThinkSocial uses a horizontal timeline that puts the full context of each post front and center which encourages a more focused, deeper experience.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>A few years ago I let <code class="language-plaintext highlighter-rouge">thinkultimate.com</code> expire. Someone is now squating on it and wants over $5,000 for it. When Mastodon first started to gain traction last Fall I almost bought <code class="language-plaintext highlighter-rouge">think.social</code> but passed on it and someone took that one as well (although there’s no price listed so maybe they are looking to do something with it). Let that be a lesson: hoard your domains. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="ThinkSocial" /><category term="Trunk" /><category term="Mastodon" /><category term="iOS" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Dev Notes #4: Image Caching, Custom Emoji, and a Dumb UIKit Bug</title><link href="https://davidbeck.co/posts/2023-02-04-dev-notes-4" rel="alternate" type="text/html" title="Dev Notes #4: Image Caching, Custom Emoji, and a Dumb UIKit Bug" /><published>2023-02-04T00:00:00+00:00</published><updated>2023-02-04T00:00:00+00:00</updated><id>https://davidbeck.co/posts/dev-notes-4</id><content type="html" xml:base="https://davidbeck.co/posts/2023-02-04-dev-notes-4"><![CDATA[<p>I started the week off addressing how the app caches images. The app uses <a href="https://github.com/davbeck/ImageIOSwift">my wrapper around ImageI/O</a>. The downloader that uses relies on <a href="https://developer.apple.com/documentation/foundation/urlcache">URLCache</a> as well as an in memory cache of the parsed images on top of that. But, URLCache can be unpredictable to say the least. I like that it uses the response headers to determine how long to cache items. But from my experience, it often ejects items to agressively.</p>

<p>For Mastodon, image urls mostly<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> change when they get updated. For instance if you update your avatar the url returned with your profile will be different. I can take adavantage of that to cache images by their URL and only ever remove them if I need to free up space. I am getting a little bit nervous thinking about Mastodon clones and other services in the fediverse. These kinds of assumptions could quickly fall apart, but that’s a problem for another day.</p>

<p>I do have a tendancy to overcomplicate things like this, but I wanted to support a few things.</p>

<p>First, I wanted to be able to handle background fetching, so I went with a really simple filename based caching system where all I need to do was move a download to the correct path based on it’s request url.</p>

<p>Second, I wanted to fallback to alternatives if one of the urls failed to download. Mastodon often provides multiple urls for any given images. Media attachments can include up to 4 urls: an original one, a cached one from your instance, and smaller preview sizes for each. The documentation says that the cached version is in case the original becomes unavailable because of load, but using mastodon.social, I’ve found the original to be much more reliable. Regardless, if the original fails, I want to try the cached url. If those both fail, I want to try the preview versions since a slow internet connection might cause the fullsize versions to timeout, but succeed at downloading a smaller version. This made the code a little bit ugly, but wasn’t too complicated to impliment.</p>

<p>And finally, I wanted to make sure that images were only available in memory once. So if 2 views request the same image (quit likely if we are talking about avatars in a conversation list) we don’t end up storing that image in memory more than we need. Swift Tasks make it pretty straightforward to return existing requests instead of starting new ones, but one extra complication was making sure existing images got reused. If you have a simple in memory cache (using NSCache or a Dictionary) that will avoid duplicate images being returned, but once you clear that cache, if an image gets requested again you will end up recreating it. The solution I found was to create a <a href="https://gist.github.com/davbeck/1a3ee5340823ba673c0c2c05a8db2645">weak wrapper</a> and store your returned items that way. Then if everything (including your cache) releases the image, it gets deallocated, but if anything is still holding onto it, you don’t create a duplicate.</p>

<p>Coupling that with preloading images (in the background when necessary), the experience paging through the timeline is much improved. Most things are already loaded when you get to them.</p>

<hr />

<p>Following that I was looking at a long list of bugs, and was feeling quit demotivated. So instead I bumped up something that seemed more fun: custom emoji support.</p>

<p>I’ve tried to be fairly agressive about prioritizing what is the most important work because I really want to get this app on the store by Summer. For review, Mastodon lets admins add custom emoji that user’s can include in their posts and username using a shortcode syntax like <code class="language-plaintext highlighter-rouge">ghost</code>. This isn’t necessarily a feature that is critical. Ivory shipped without it. If your app doesn’t suppor them they just show up as the shortcode. But it seemed like a fun challenge, especially to support animated ones.</p>

<p>Something I had to figure out first though was how I was going to render text. I was using SwiftUI.Text embeded in a UIKit view. But if I needed to use a UIKit view, that would make a big difference in how I implimented this.</p>

<p>UILabel doesn’t support links. You can use a tap gesture recognizer and compute the link location but I’ve done it before and it’s hard to get right. I briefly considered rendering my own label using TextKit, which would give me a lot of freedom. But at the end of the day, Text just did everything I wanted. The downside though is that the bridge between UIKit and SwiftUI can be a bit buggy. But I <em>think</em> I have that worked out 🤞🏻.</p>

<p>With that figured out, I just needed to get tiny images into Text. A little known feature of Text is that it has pretty simple and easy Image embeds out of the box. One of my favorite tricks is to use something like <code class="language-plaintext highlighter-rouge">Text("\(Image(systemSymbol: .arrow2Squarepath) \(name) boosted")</code>. Then if the text wraps it isn’t indented like it would be with an HStack.</p>

<p>In order to support partially loaded and animated images, I first parsed my AttributedStrings into parts, and then each time an image updates, stitch it back together with the current images.</p>

<p>Getting sizing right was a little tricky. On the web Mastodon shows custom emoji fairly large (20pt vs the text’s 15pt), but doing that pushed the alignment of the surrounding text off. It would be nice to use UIImage’s <a href="https://developer.apple.com/documentation/uikit/uiimage/1624100-withalignmentrectinsets">alignmentRect</a> to give the image a vertical overflow, but unfortunately SwiftUI ignores that, at least inside of Text. I was able to instead use <a href="https://developer.apple.com/documentation/swiftui/text/baselineoffset(_:)"><code class="language-plaintext highlighter-rouge">Text.baselineOffset()</code></a> to adjust it’s position so it was centered with the text. I still needed to limit their size more than I would have liked, but the end result looks good.</p>

<p><img src="/images/2023-02-04-dev-notes-4/emoji.png" alt="Screenshot of custom emoji in the app" /></p>

<hr />

<p>In the middle of working on emoji, there was a bug that popped up, that I had seen before, but was unable to reproduce. When I refreshed on status, the media and boost label would disappear on another, and then come back if I refreshed that post. And this was happening every time, even after relaunching the app. Some bugs you just have to drop everything when you catch them in the wild.</p>

<p>After poking at it a big, I found that when I was updating those views to display onscreen, I would set <code class="language-plaintext highlighter-rouge">isHidden = false</code> so they would appear in their UIStackView. But UIStackView would then just set it right back to true, keeping them from being displayed. Maybe this was because I was updating them when they were offscreen. Maybe it was something to do with animation. Maybe I could have found a workaround. But this was so incredibly frustrating. UIStackView is not a new API. It should “just work”.</p>

<p>So I ripped out UIStackView and setup constraints manually. More code, more boilerplate, but at least it does what I say now. And I have more control over the animations going forward.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>I have found that if you update a custom emoji, it will reuse the same URL. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="Trunk" /><category term="ThinkSocial" /><category term="iOS" /><summary type="html"><![CDATA[I started the week off addressing how the app caches images. The app uses my wrapper around ImageI/O. The downloader that uses relies on URLCache as well as an in memory cache of the parsed images on top of that. But, URLCache can be unpredictable to say the least. I like that it uses the response headers to determine how long to cache items. But from my experience, it often ejects items to agressively.]]></summary></entry><entry><title type="html">Trunk Dev Notes #3: CI Servers, Cleanup, and Fullscreen Images</title><link href="https://davidbeck.co/posts/2023-01-29-trunk-dev-notes-3" rel="alternate" type="text/html" title="Trunk Dev Notes #3: CI Servers, Cleanup, and Fullscreen Images" /><published>2023-01-29T00:00:00+00:00</published><updated>2023-01-29T00:00:00+00:00</updated><id>https://davidbeck.co/posts/trunk-dev-notes-3</id><content type="html" xml:base="https://davidbeck.co/posts/2023-01-29-trunk-dev-notes-3"><![CDATA[<p>At the beginning of last week I spent some time working on CI for Trunk. <a href="https://mastodon.social/@davbeck/109530813220759111">After making big claims about having to have CI on every project I work on</a>, my workflow has been broken for several weeks. It’s admittedly hard to take time away from actually building something to setup support tools.</p>

<p>I was using Xcode cloud, but tests started failing after I added <a href="https://github.com/pointfreeco/swift-snapshot-testing">snapshot tests</a>. Apparently the way Xcode cloud runs tests the snapshot images aren’t available.</p>

<p>So I went back to what I know. Going back to what I know seems to be a developing theme of this project. At my last company I had really good success with self hosted GitHub action runners. And I already have a “junk drawer” M1 Mac mini that could do the job well. It will probably help curb costs as well since I’m leaning pretty heavily into UI tests for this project and those eat up CI minutes quickly.</p>

<p>I’m leaving my TestFlight builds on Xcode Cloud though. That is pretty tricky to setup anywhere else because of code signing. To my knowledge, Xcode Cloud is the only CI that can use automatic provisioning.</p>

<p>I tested the full workflow out by putting out a fix for my Sentry logging. Apparently I had forgotten to paste in my api key. Oops.</p>

<hr />

<p>I then spent the evenings last week going through several small improvements.</p>

<p>I learned a new term recently, <a href="https://theoatmeal.com/comics/creativity_petting">“don’t pet your paintings”</a> and it’s really stuck with me when it comes to this kind of thing. I want to focus on making changes that are objective improvements, not just going around in circles. There’s a time to waffle on the exact font size of a label, but now isn’t it. So right now I only focus on things that stand out to me on a daily basis actually using the app.</p>

<p>But there were a handful of things that were pretty obvious. For instance on link previews, I include the description. For whatever reason I put the description above the title though. And every time I saw that it looked wrong. Those kind of things make sense to fix.</p>

<p>But anything that I’m not sure about, or think I might change back, goes straight to the icebox.</p>

<hr />

<p>At the start of this week I finally tackled something I’ve been putting off: fullscreen images. I’ve always been confused by the UIViewController transitions api. But when I think about what the core experience of the app is going to be, I consider excellent media handling to be a top priority.</p>

<p>My approach (and I’m not sure in hindesight this was the right approach) was to start with <a href="https://github.com/nytimes/NYTPhotoViewer">NYTPhotoViewer</a>. I’ve used it before and it worked well, but I wanted to make some significant changes to the design and I needed it to be able to support Mastodon’s GIFs (which are actually videos).</p>

<p>The first step to get it to work was to rewrite it in Swift. I used ObjC for almost a decade, but my brain doesn’t think that way anymore. From there I refactored it to use custom views instead of images, and finally made the UI changes I wanted, which mostly involved using a white background in light mode and a navigation bar to match what iOS usually does.</p>

<p>The hardest challenge with this whole process was supporting GIFs. The animation is suppose to imply a thumbnail zooming into place fullscreen. In reality there is more than 1 view involved in that transition. For NYTPhotoViewer, it actually uses 4 views during the animation: the thumbnail, the fullscreen view, and a snapshot of each for the animation transition. But animating snapshots of views won’t work for anything with movement. The view would appear to freeze while it animated into place. And for GIFs, you need to make sure their progress is kept in sync between the thumbnail and the fullscreen version.</p>

<p>This was made even more challenging because of limitations with <a href="https://developer.apple.com/documentation/avfoundation/avplayerlayer"><code class="language-plaintext highlighter-rouge">AVPlayerLayer</code></a>. It doesn’t animate changes to it’s transform property like a normal layer does. When you add it to the view hierarchy, even if you are sharing an AVPlayer, even if you wait for <a href="https://developer.apple.com/documentation/avfoundation/avplayerlayer/1389748-isreadyfordisplay"><code class="language-plaintext highlighter-rouge">isReadyForDisplay</code></a> to be <code class="language-plaintext highlighter-rouge">true</code>, it will stil be blank for a frame or 2.</p>

<p>This process had all the stages of programming. The overconfidence that it would be easier that in was. The despair that it was impossible. The despair that I would never get it to work right and this would be the end of the project. The resolve to leave it alone for a while but then continually “trying one more thing” to get it to work. The overwhelming joy when part of it finally does work, but 30 seconds later you move on to another component only to start the process all over again.</p>

<p>I was driven through this entire process by Ivory. I looked at every Mastodon app I know of and only Ivory handled this UI transition correctly. Many don’t even support fullscreen GIFs correctly, instead presenting them as videos. But that dang Ivory app did it perfectly, so it must be possible. I don’t know if <a href="https://tapbots.social/@paul">Paul</a> struggled with this as much as I did, or if he just threw it together effortlessly like the master he is, but if it was possible to get right, I was going to get it right.</p>

<p>I can’t emphasize enough just how happy I am with the end result. <a href="/images/2023-01-29-trunk-dev-notes-3/fullscreen-transition-slowmow.mp4">Take a look (in slow motion)</a>. Notice how the corner radius animates, rather than just fading away. Notice how the image doesn’t just jump behind the navigation bar but fades in. And <a href="/images/2023-01-29-trunk-dev-notes-3/fullscreen-transition-gif.mp4">animated GIFs look great too</a>. It continues to play while animating into place.</p>]]></content><author><name></name></author><category term="Trunk" /><category term="ThinkSocial" /><category term="iOS" /><summary type="html"><![CDATA[At the beginning of last week I spent some time working on CI for Trunk. After making big claims about having to have CI on every project I work on, my workflow has been broken for several weeks. It’s admittedly hard to take time away from actually building something to setup support tools.]]></summary></entry><entry><title type="html">Trunk Dev Notes #2</title><link href="https://davidbeck.co/posts/2023-01-17-trunk-dev-notes-2" rel="alternate" type="text/html" title="Trunk Dev Notes #2" /><published>2023-01-17T00:00:00+00:00</published><updated>2023-01-17T00:00:00+00:00</updated><id>https://davidbeck.co/posts/trunk-dev-notes-2</id><content type="html" xml:base="https://davidbeck.co/posts/2023-01-17-trunk-dev-notes-2"><![CDATA[<p>Over the weekend I was able to get the second beta of Trunk to a place where I was ready to release it. I wanted to get it to a point where the concept of the app was well represented, which meant a fully functional timeline.</p>

<p>I’ve been trying very hard to put off improvements to media in order to focus on the second beta release. I wanted that one to include a “feature complete” timeline experience, but I had bigger changes in mind for media that I didn’t want to get in the way.</p>

<p>But since switching to a UIKit based post page, video was completely broken. Videos just didn’t show up at all. So I needed to at least get it working, even if it wasn’t the best experience for now. But I also didn’t want to put a bunch of work into something that I knew would change quit a bit.</p>

<p>My first attempt at this was to replace VideoPlayer with AVPlayerViewController wrapped in UIViewControllerRepresentable (currently attachments are SwiftUI views embedded inside the UIKit views using UIHostingConfiguration). And… it had the exact same bug. I don’t know why this surprised me, that’s probably exactly how VideoPlayer itself is implemented. Seems like there is a bug with UIViewControllerRepresentable in general and UIHostingConfiguration.</p>

<p>The solution I landed on was to use AVPlayerLayer mostly the same way I do GIFv files (auto played silently) and present AVPlayerViewController when the user taps on it. It has some rough edges but it works.</p>

<p>With that fixed, and some improvements to the all caught up screen, I was ready to open up the beta to a few more people. When I released the first beta I limited it to 100 testers. I wanted to get feedback on the horizontal timeline, but I didn’t want to release it to too many people when it was barely usable. Shortly after I released it, <a href="https://mastodon.social/@jamesthomson">James Thomson</a> boosted the link and those spots filled up almost immediately, but I wasn’t sure what the demand would look like when I opened more spots.</p>

<p>After uploading the second beta, I increase the number of testers to 500 and posted the link again. To my surprise those all filled up overnight. I have never seen this much interest in a beta in my entire career. I’m sure most of that is due to the massive migration to Mastodon and everyone looking for new clients, but it’s still very encouraging. I don’t know how well that will translate into paid subscriptions, but it’s very promising.</p>

<p>Something else that was really promising: the #1 feature request I received following this beta was literally the top item on my backlog. I had originally hoped to include an option (defaulted to on) to hide replies that you have already seen, but it proved to be more difficult than I thought and I really wanted to get something out, so I punted it for the next release. I spent Monday getting my predicates in order to make this work. This feature actually makes me a little nervous. It’s pretty easy to hide too much.</p>

<p>Along with hiding previously viewed content, I also addressed a couple of bugs that popped up in the last beta release. On in particular was rather odd: casting to an Int32 was overflowing for someone’s follower count. Certainly there isn’t a user with billions of followers, but the crash logs don’t include any info on what the value actually was, so who knows what was going on there.</p>

<p>Finally before dropping the next beta, I wanted to add in some notices about missing functionality. Several people asked about how to reply to posts, or how to view a profile. To manage expectations I added some placeholder views where those feature will eventually live.</p>]]></content><author><name></name></author><category term="Trunk" /><category term="ThinkSocial" /><category term="iOS" /><summary type="html"><![CDATA[Over the weekend I was able to get the second beta of Trunk to a place where I was ready to release it. I wanted to get it to a point where the concept of the app was well represented, which meant a fully functional timeline.]]></summary></entry><entry><title type="html">Trunk Dev Notes #1</title><link href="https://davidbeck.co/posts/2023-01-14-trunk-dev-notes-1" rel="alternate" type="text/html" title="Trunk Dev Notes #1" /><published>2023-01-14T00:00:00+00:00</published><updated>2023-01-14T00:00:00+00:00</updated><id>https://davidbeck.co/posts/trunk-dev-notes-1</id><content type="html" xml:base="https://davidbeck.co/posts/2023-01-14-trunk-dev-notes-1"><![CDATA[<p>The last few weeks working on Trunk have been fairly frustrating, but I think it’s coming around the corner.</p>

<p>I had hoped to spend the holiday break knocking out a punch list of features before releasing a second beta to a wider audience. However as more features got added, 2 very big issues emerged that brought development to a stop.</p>

<p>First was a performance issue with my caching layer. I had gone with an approach that I had used before where caching was based on api endpoints. Views would first show the cached result and then if needed, refresh the endpoint to get the latest data. While that simple approach worked great for apps where the data is mostly isolated between each call, in Mastodon (and any social app) the same data can be returned by multiple api requests.</p>

<p>For instance, you might have a status that was loaded from the timeline. If the user favorites that, you want to update the cache to remember that change. But that same status may then come back as a reply to another status, or as a boosted post, etc. I was trying to update every cached value when something like that changed, but not only was it incredibly buggy, but it was also slowing the app down to a complete crawl.</p>

<p>After several false starts and alternatives considered, I eventually went back to CoreData. I’ve been trying to get away from using CoreData for the last several years (mostly because of its limitations with Swift, but also its tendency to crash the app), but this experience really showed me how much it really has to offer.</p>

<p>So, with the entire backend rewritten, I then turned my attention to the other showstopper: scrolling.</p>

<p>Trunks UI is centered around a horizontal paged interface where the user swipes between posts from their timeline. As they scroll to each post, the “context” (the conversation before and after the post) get loaded in. But it would be really weird if you went to view a post from your timeline and what you saw at the top of the screen was actually the original post that someone replied to. So instead I show the “primary” post (the one from the timeline) and you can scroll up to see older posts in the conversation.</p>

<p>The problem is that adding content to the top of a scroll view while maintaining the previous content’s position is actually really tricky. UIScrollView (and by extension SwiftUI.ScrollView) really wants to maintain the offset. That gets even more tricky to deal with when using views that don’t render their entire contents, like UITableView or LazyVStack. In those cases the position might not even be consistent, because visible content is placed based on estimated heights.</p>

<p>In the first beta I had some pretty simple logic that would scroll to the primary posts position when the context loaded. This worked at first, but as I implemented all the various content posts can have (like media and polls), the screen started flashing as the new content loaded in and then was scrolled out of the way.</p>

<p>I tried several approaches to get this to work. <a href="https://talk.objc.io/episodes/S01E336-scroll-view-with-tabs-part-2">A recent episode of Swift Talk</a> made me hopeful that I could implement something in SwiftUI that would work. But there just isn’t a direct enough connection to the layout process to get it to be reliable and performant.</p>

<p>Eventually I gave up and realized I would need to rewrite the post page in UIKit. While that was a hard decision to make, ultimately I had to ask myself if I wanted to make an app in pure SwiftUI, or the best app possible. I attempted to use the existing post view (using either UIHostingConfiguration or UIHostingController) but animating size changes with either was non functional. I was able to use a lot of SwiftUI views within each post row, but the overall container needed to be a UIView.</p>

<p>I concidered using UITableView or UICollectionView, but if something’s worth doing, it’s worth overdoing. Both of those can have their own challenges with maintaining content’s position. I think that’s why <a href="https://tapbots.social/@paul/109564775494812308">Ivory is using manual layout with pre-computed heights</a> and why <a href="https://mastodon.social/@libei/109597383746473808">Mastoot needs to scroll to content twice</a>. So instead I wrote my own UIScrollView subclass that handles reusing views itself. What this gives me is autolayout based views without any inconsistent positioning. So far it seems to be working really well.</p>

<p>With those changes, the next beta is getting really close. There are a few bugs to work out and one last feature that I want to include. The goal is to have a “feature complete” timeline. Meaning you won’t miss anything if you browse mastodon exclusively in the app.</p>]]></content><author><name></name></author><category term="SwiftUI" /><category term="Trunk" /><category term="ThinkSocial" /><category term="iOS" /><summary type="html"><![CDATA[The last few weeks working on Trunk have been fairly frustrating, but I think it’s coming around the corner.]]></summary></entry><entry><title type="html">Animating and Downloading Images Incrementally with SwiftUI</title><link href="https://davidbeck.co/posts/2019-07-14-imageio-swiftui" rel="alternate" type="text/html" title="Animating and Downloading Images Incrementally with SwiftUI" /><published>2019-07-14T00:00:00+00:00</published><updated>2019-07-14T00:00:00+00:00</updated><id>https://davidbeck.co/posts/imageio-swiftui</id><content type="html" xml:base="https://davidbeck.co/posts/2019-07-14-imageio-swiftui"><![CDATA[<p>When Apple released their new view framework, SwiftUI, at WWDC 2019, they had several sessions throughout the week to breakdown and explain how to use the framework. There was a big hole that people are still trying to figure out though: how do you load images! To be fair, this has long been a void in the Apple frameworks. UIKit doesn’t have any builtin way to download and show images either. But given the radical new design of SwiftUI, it’s not obvious how it should be done.</p>

<p>I have a library called <a href="https://github.com/davbeck/ImageIOSwift">ImageIO.Swift</a> that uses <a href="https://developer.apple.com/documentation/imageio">Image I/O</a> to animated and load images incrementally. Basically it gives you a UIView that works like an <a href="https://www.w3schools.com/tags/tag_img.asp">html <code class="language-plaintext highlighter-rouge">img</code></a> tag. So I figured why not add support for SwiftIU! How hard could it be 😅.</p>

<p><span class="side-note">
I’ve been thinking of changing the name of this library. Originally the idea was that it would be a lightweight wrapper around Image I/O similar to the way GCD gets adapted into a more friendly Swift interface. However the feature that I use the most is the image source view, which handles downloading and animating images. There is no equivalent in Image I/O. If you have any suggestions, let me know.
</span></p>

<p>If you’re just interested in using the library, check out the <a href="https://github.com/davbeck/ImageIOSwift/tree/1.0.0">beta 1.0.0 release</a>. To learn more about how the SwiftUI integration was implemented, read on!</p>

<hr />

<p>The simplest way to display an image source in SwiftUI would be to use a <a href="https://developer.apple.com/documentation/swiftui/uiviewrepresentable">UIViewRepresentable</a> view and just wrap the existing UIView implementation of <code class="language-plaintext highlighter-rouge">ImageSourceView</code>. That would have been pretty quick and simple. But who wants that!</p>

<p>The big drawback there though is that it would only work with UIKit apps. To get the real cross platform benefit of SwiftUI, including watchOS and AppKit Mac apps, I’d need to use a more native view implementation.</p>

<h2 id="downloading-images">Downloading images</h2>

<p>The first challenge I ran into with SwiftUI was handling derived state. ImageIO.Swift uses <code class="language-plaintext highlighter-rouge">Task</code>s derived from urls to track downloading. We don’t really want tasks to be replaced for fear of the download starting again.</p>

<p>We can use <code class="language-plaintext highlighter-rouge">@State</code> to get SwiftUI to track our task for us. The task isn’t actually changing though, which feels weird, but is the only way I see to accomplish this.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">RemoteImageSourceView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="kd">@State</span> <span class="k">var</span> <span class="nv">task</span><span class="p">:</span> <span class="kt">ImageSourceDownloader</span><span class="o">.</span><span class="kt">Task</span>
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kt">ImageSourceView</span><span class="p">(</span><span class="nv">imageSource</span><span class="p">:</span> <span class="n">task</span><span class="o">.</span><span class="n">imageSource</span><span class="p">)</span>
		<span class="o">.</span><span class="n">onAppear</span> <span class="p">{</span>
			<span class="k">self</span><span class="o">.</span><span class="n">task</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">URLImageSourceView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span>
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kt">RemoteImageSourceView</span><span class="p">(</span><span class="nv">task</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">imageSourceDownloader</span><span class="o">.</span><span class="nf">task</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">url</span><span class="p">))</span>
		<span class="o">.</span><span class="nf">id</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Each time <code class="language-plaintext highlighter-rouge">URLImageSourceView</code> gets evaluated, it will create a new task, but it will actually get discarded in favor of the previous state. I made some changes to the downloader to make tasks lazy, similar to the way <code class="language-plaintext highlighter-rouge">URLSession</code> tasks work. So creating extra tasks that get thrown out immediately shouldn’t be a performance issue.</p>

<p>That’s how we can maintain a derived state, but if the url changes, we actually want to reset the task and track a new one. That’s what <a href="https://developer.apple.com/documentation/swiftui/view/3278578-id"><code class="language-plaintext highlighter-rouge">id(url)</code></a> does. When the value you pass to <code class="language-plaintext highlighter-rouge">id</code> changes, it will reset any state.</p>

<p>Note that we don’t need to worry about something like <code class="language-plaintext highlighter-rouge">@ObjectBinding</code> here because the changes to a task are primarily in it’s <code class="language-plaintext highlighter-rouge">imageSource</code>. That gets created immediately and updated as data becomes available. We can bind to that instead.</p>

<p>It’s a good idea to wait until <code class="language-plaintext highlighter-rouge">onAppear</code> to actually start any work. As people are finding out, things like <a href="https://developer.apple.com/documentation/swiftui/navigationlink">NavigationLink</a> evaluate their children immediately, but don’t call <code class="language-plaintext highlighter-rouge">onAppear</code> until it’s actually shown. But beware, <code class="language-plaintext highlighter-rouge">onAppear</code> and <code class="language-plaintext highlighter-rouge">onDisappear</code> can be called more often than you might think. Even if the view you attach them too isn’t being added and removed, if their only visible child changes down the hierarchy, these will get triggered. For that reason, I don’t explicitly call cancel on the task because it might “disappear” and “reappear” without the task ever actually changing. Instead, the task will be cancelled implicitly when it gets released (a pattern Swift is using more and more).</p>

<h2 id="animating">Animating</h2>

<p>Animation also needs a kind of derived state. The animations are driven by a <a href="https://developer.apple.com/documentation/quartzcore/cadisplaylink">CADisplayLink</a> (or a simple timer if it’s not available).</p>

<p>We could adapt a display link into a <a href="https://developer.apple.com/documentation/swiftui/bindableobject"><code class="language-plaintext highlighter-rouge">BindableObject</code></a>, but the bigger challenge is where do we create this object. Every time the parent gets re-evaluated, a new instance of the object would get created if we just created a default:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">AnimatedImageSourceView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="c1">// WRONG!</span>
	<span class="kd">@ObjectBinding</span> <span class="k">var</span> <span class="nv">displayLink</span> <span class="o">=</span> <span class="kt">DisplayLink</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Instead, I created a custom <a href="https://developer.apple.com/documentation/combine/publisher"><code class="language-plaintext highlighter-rouge">Publisher</code></a> that waits until the user subscribes to it before creating the display link. It then invalidates it when it gets cancelled.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">AnimatedImageSourceView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="kd">@ObjectBinding</span> <span class="k">var</span> <span class="nv">imageSource</span><span class="p">:</span> <span class="kt">ImageSource</span>
	<span class="k">var</span> <span class="nv">displayLink</span><span class="p">:</span> <span class="kt">DisplayLink</span>
	<span class="kd">@State</span> <span class="k">var</span> <span class="nv">startTimestamp</span><span class="p">:</span> <span class="kt">TimeInterval</span><span class="p">?</span> <span class="o">=</span> <span class="o">.</span><span class="k">none</span>
	<span class="kd">@State</span> <span class="k">var</span> <span class="nv">animationFrame</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
	<span class="k">var</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span>
	
	<span class="nf">init</span><span class="p">(</span><span class="nv">imageSource</span><span class="p">:</span> <span class="kt">ImageSource</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">self</span><span class="o">.</span><span class="n">imageSource</span> <span class="o">=</span> <span class="n">imageSource</span>
		<span class="k">self</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="n">label</span>
		<span class="k">self</span><span class="o">.</span><span class="n">displayLink</span> <span class="o">=</span> <span class="kt">DisplayLink</span><span class="p">(</span><span class="nv">preferredFramesPerSecond</span><span class="p">:</span> <span class="n">imageSource</span><span class="o">.</span><span class="n">preferredFramesPerSecond</span><span class="p">)</span>
	<span class="p">}</span>
	
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kt">StaticImageSourceView</span><span class="p">(</span><span class="nv">imageSource</span><span class="p">:</span> <span class="n">imageSource</span><span class="p">,</span> <span class="nv">animationFrame</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">animationFrame</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="n">label</span><span class="p">)</span>
			<span class="o">.</span><span class="n">onAppear</span> <span class="p">{</span>
				<span class="k">self</span><span class="o">.</span><span class="n">startTimestamp</span> <span class="o">=</span> <span class="kt">CACurrentMediaTime</span><span class="p">()</span>
			<span class="p">}</span>
			<span class="o">.</span><span class="nf">onReceive</span><span class="p">(</span><span class="n">displayLink</span><span class="p">)</span> <span class="p">{</span> <span class="n">targetTimestamp</span> <span class="k">in</span>
				<span class="k">if</span> <span class="k">let</span> <span class="nv">startTimestamp</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">startTimestamp</span> <span class="p">{</span>
					<span class="k">let</span> <span class="nv">timestamp</span> <span class="o">=</span> <span class="n">targetTimestamp</span> <span class="o">-</span> <span class="n">startTimestamp</span>
					<span class="k">self</span><span class="o">.</span><span class="n">animationFrame</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">imageSource</span><span class="o">.</span><span class="nf">animationFrame</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">timestamp</span><span class="p">)</span>
				<span class="p">}</span>
			<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We don’t need to worry about this being re-created. It is a struct with only one variable. It only does work when it gets subscribed to, and then all of its state is tracked within a subscription object, which SwiftUI keeps track of.</p>

<p>This is something that is a little hard for me to wrap my head around, but based on <a href="https://forums.swift.org/t/a-uicontrol-event-publisher-example/26215/9">this forum post</a> I think is the right way to think about publishers. The actual publisher is just a template for a subscription, which then manages the state and triggers work to be done.</p>

<h2 id="handling-orientation">Handling orientation</h2>

<p>One of the sticking points with using a <a href="https://developer.apple.com/documentation/imageio/cgimagesource-r84">CGImageSource</a> directly instead of a <a href="https://developer.apple.com/documentation/uikit/uiimage">UIImage</a> is that images with a non-standard <a href="https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/">exif orientation</a> don’t get adjusted. Exif orientation is used to avoid re-encoding images from a camera. Instead of taking a JPEG, rotating the pixels and then re-compressing it (which would lead to artifacts), software, especially phones, will just mark the image as rotated from the original.</p>

<p>When creating a UIImage from a CGImage, you can specify the orientation and things like UIImageView will adjust it appropriately. But funny enough, SwiftUI doesn’t handle this, even with images loaded from an asset catalog. So at least until that bug gets fixed (and yes, I did file a <s>radar</s> feedback) ImageIO.Swift has yet another advantage over plain <code class="language-plaintext highlighter-rouge">Image</code>s 😆.</p>

<p>But there were a few challenges with correcting the orientation. Generally, making the adjustment is just a matter of combining -1 transformations in the x and y directions with rotations left or right. The transformations were easy enough:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Image</span><span class="p">(</span><span class="n">cgImage</span><span class="p">,</span> <span class="nv">scale</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span><span class="p">(</span><span class="s">""</span><span class="p">))</span>
<span class="o">.</span><span class="nf">scaleEffect</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">properties</span><span class="o">.</span><span class="n">scaleX</span><span class="p">,</span>
             <span class="nv">y</span><span class="p">:</span> <span class="n">properties</span><span class="o">.</span><span class="n">scaleY</span><span class="p">,</span>
             <span class="nv">anchor</span><span class="p">:</span> <span class="o">.</span><span class="n">center</span><span class="p">)</span>
</code></pre></div></div>

<p>That’s all that’s needed for half of the orientations (1-4). But the other half actually involve a 90° rotation left or right. This actually changes the frame of the image from portrait to landscape or vice versa. Here’s where it gets tricky. SwiftUI has a <a href="https://developer.apple.com/documentation/swiftui/image/3269732-rotationeffect"><code class="language-plaintext highlighter-rouge">rotationEffect</code></a> modifier, but it doesn’t effect its bounding box. So if you had a landscape image that was rotated into portrait and you set its width to 200 pixels, it would actually be smaller than expected because the bounding box would be set to that width, and the the image rotated.</p>

<p>I came up with an interesting solution to this: using <a href="https://developer.apple.com/documentation/swiftui/view/3278622-overlay"><code class="language-plaintext highlighter-rouge">overlay</code></a> to place the actual image above a placeholder view. Using the image source’s size as a preferred frame:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ImageSourceBase</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">properties</span><span class="p">:</span> <span class="kt">ImageProperties</span>
	
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="kt">Rectangle</span><span class="p">()</span>
			<span class="o">.</span><span class="nf">fill</span><span class="p">(</span><span class="kt">Color</span><span class="o">.</span><span class="n">clear</span><span class="p">)</span>
			<span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">idealWidth</span><span class="p">:</span> <span class="n">properties</span><span class="o">.</span><span class="n">imageSize</span><span class="p">?</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="nv">idealHeight</span><span class="p">:</span> <span class="n">properties</span><span class="o">.</span><span class="n">imageSize</span><span class="p">?</span><span class="o">.</span><span class="n">height</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The actual image can then be placed over it with it’s various transforms:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">StaticImageSourceView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="kd">@ObjectBinding</span> <span class="k">var</span> <span class="nv">imageSource</span><span class="p">:</span> <span class="kt">ImageSource</span>
	<span class="k">var</span> <span class="nv">animationFrame</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
	<span class="k">var</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span>
	
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="k">let</span> <span class="nv">image</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">imageSource</span><span class="o">.</span><span class="nf">cgImage</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">animationFrame</span><span class="p">)</span>
		<span class="k">let</span> <span class="nv">properties</span> <span class="o">=</span> <span class="n">imageSource</span><span class="o">.</span><span class="nf">properties</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">animationFrame</span><span class="p">)</span>
		
		<span class="k">return</span> <span class="kt">ImageSourceBase</span><span class="p">(</span><span class="nv">properties</span><span class="p">:</span> <span class="n">properties</span><span class="p">)</span>
			<span class="o">.</span><span class="nf">overlay</span><span class="p">(</span>
				<span class="n">image</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="kt">Image</span><span class="p">(</span><span class="nv">$0</span><span class="p">,</span> <span class="nv">scale</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">label</span><span class="p">)</span>
					<span class="o">.</span><span class="nf">resizable</span><span class="p">()</span>
					<span class="c1">// adjust based on exif orientation</span>
					<span class="o">.</span><span class="nf">rotationEffect</span><span class="p">(</span><span class="n">properties</span><span class="o">.</span><span class="n">rotateZ</span><span class="p">)</span>
					<span class="o">.</span><span class="nf">scaleEffect</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">properties</span><span class="o">.</span><span class="n">scaleX</span><span class="p">,</span>
					             <span class="nv">y</span><span class="p">:</span> <span class="n">properties</span><span class="o">.</span><span class="n">scaleY</span><span class="p">,</span>
					             <span class="nv">anchor</span><span class="p">:</span> <span class="o">.</span><span class="n">center</span><span class="p">)</span>
				<span class="p">}</span>
			<span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>While the image source is available immediately from the download task, it doesn’t necessarily have enough data to render anything, or even know what size it is. I struggled a big to figure out how to conditionally show and hide the image when it wasn’t available. But, as it turns out, an optional <code class="language-plaintext highlighter-rouge">View</code> is itself a valid <code class="language-plaintext highlighter-rouge">View</code> thanks to conditional conformance. When its nil, nothing will be shown, when it’s non-nil, it will be shown.</p>

<h2 id="resizable-️">Resizable? 🤷🏽‍♂️</h2>

<p>Another thing you might notice in that code snippet is that I’m marking the image as resizable. I’ll be honest, I’m not sure what Apple was going for here. Views can have an “ideal” size, which seems to be equivalent to <a href="https://developer.apple.com/documentation/uikit/uiview/1622600-intrinsiccontentsize"><code class="language-plaintext highlighter-rouge">intrinsicContentSize</code></a>, which is what <code class="language-plaintext highlighter-rouge">UIImageView</code> uses.</p>

<p>However <a href="https://developer.apple.com/documentation/swiftui/image/3269730-resizable"><code class="language-plaintext highlighter-rouge">resizable</code></a> is specific to <code class="language-plaintext highlighter-rouge">Image</code>s. Most view modifiers like that return a wrapper around their original view, but <code class="language-plaintext highlighter-rouge">resizable</code> returns a plain <code class="language-plaintext highlighter-rouge">Image</code>. This means that it must be the first in a chain of modifiers:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// this won't compile</span>
<span class="kt">Image</span><span class="p">(</span><span class="s">"foo"</span><span class="p">)</span>
	<span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="kt">Color</span><span class="o">.</span><span class="n">blue</span><span class="p">)</span>
	<span class="o">.</span><span class="nf">resizable</span><span class="p">()</span>
<span class="c1">// Value of type '_ModifiedContent&lt;Image, _BackgroundModifier&lt;Color&gt;&gt;' has no member 'resizable'</span>

<span class="c1">// instead, you have to order it like this</span>
<span class="kt">Image</span><span class="p">(</span><span class="s">"foo"</span><span class="p">)</span>
	<span class="o">.</span><span class="nf">resizable</span><span class="p">()</span>
	<span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="kt">Color</span><span class="o">.</span><span class="n">blue</span><span class="p">)</span>
</code></pre></div></div>

<p>It also means that we can’t easily wrap an <code class="language-plaintext highlighter-rouge">Image</code> and maintain the ability to mark it as resizable or not.</p>

<p>Until I understand the purpose of this more, I’m always including it, because as far as I can tell, that’s what you would always want. For icons its perhaps more useful to have a fixed size, but images sources are primarily used for other types of images.</p>

<h2 id="packaging-it-all-up">Packaging it all up</h2>

<p>At WWDC, Apple pointed out that SwiftUI doesn’t include prefixes for it’s types. It’s a true Swift only framework! Ironically though they are able to get away with that because all of their other view frameworks use prefixes. You don’t confuse <code class="language-plaintext highlighter-rouge">View</code> for a <code class="language-plaintext highlighter-rouge">UIView</code> for instance, both in your mind and in your compiler.</p>

<p>For Apple’s other frameworks like MapKit, this hasn’t been an issue in the past either. Even though there are 2 versions of <code class="language-plaintext highlighter-rouge">MKMapView</code> (one for UIKit and one for AppKit), they are only available on different platforms. When they eventually do add a SwiftUI version of the view, it will probably just be named <code class="language-plaintext highlighter-rouge">MapView</code>.</p>

<p>That’s great for Apple, but for existing Swift native frameworks like ImageIO.Swift, we are already using non-prefixed classes. The natural name for the SwiftUI view here would be <code class="language-plaintext highlighter-rouge">ImageSourceView</code>. But that’s exactly what I’m already using for the <code class="language-plaintext highlighter-rouge">UIView</code> subclass.</p>

<p>After some deliberation, I decided to break up the library into several packages/podspecs. ImageIOUIKit has all the UIKit adaptations, including an <code class="language-plaintext highlighter-rouge">ImageSourceView</code>, which is a subclass of <code class="language-plaintext highlighter-rouge">UIView</code>. Meanwhile, <code class="language-plaintext highlighter-rouge">ImageIOSwiftUI</code> includes a <code class="language-plaintext highlighter-rouge">SwiftUI.View</code> implementation of <code class="language-plaintext highlighter-rouge">ImageSourceView</code>. And technically these can be used together with their full namespace: <code class="language-plaintext highlighter-rouge">ImageIOSwiftUI.ImageSourceView</code> and <code class="language-plaintext highlighter-rouge">ImageIOUIKit.ImageSourceView</code>. But I think most people will end up only using one or the other.</p>

<hr />

<p>The final result works really well. Images are downloaded correctly, appearing incrementally as data becomes available, and start to animate once they are loaded.</p>

<p>Here’s what the public API ends up looking like:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">URLImageSourceView</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="nv">isAnimationEnabled</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span><span class="p">(</span><span class="s">"alt text"</span><span class="p">))</span>
	<span class="o">.</span><span class="nf">aspectRatio</span><span class="p">(</span><span class="nv">contentMode</span><span class="p">:</span> <span class="o">.</span><span class="n">fit</span><span class="p">)</span>
	<span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="mi">300</span><span class="p">)</span>
</code></pre></div></div>

<p>Or, if you already have a reference to an <code class="language-plaintext highlighter-rouge">ImageSource</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">ImageSourceView</span><span class="p">(</span><span class="nv">imageSource</span><span class="p">:</span> <span class="n">imageSource</span><span class="p">,</span> <span class="nv">isAnimationEnabled</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span><span class="p">(</span><span class="s">"alt text"</span><span class="p">))</span>
	<span class="o">.</span><span class="nf">aspectRatio</span><span class="p">(</span><span class="nv">contentMode</span><span class="p">:</span> <span class="o">.</span><span class="n">fit</span><span class="p">)</span>
	<span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="mi">300</span><span class="p">)</span>
</code></pre></div></div>

<p>I’m including this integration in a <a href="https://github.com/davbeck/ImageIOSwift/tree/1.0.0">1.0.0</a> update I’m planning on releasing in the Fall after the Xcode GM is available, but in the meantime you can give it a try in your own SwiftUI apps using SwiftPM.</p>

<p>There has been quit a bit of talk about <a href="https://developer.apple.com/documentation/imageio/3333271-cganimateimageaturlwithblock">some additional api in Image I/O</a> that supposedly adds support for animated images. It is currently undocumented and unavailable in Swift (not to mention its iOS 13 only). It will be interesting to see if it is using any special tricks when it’s finally available, but for now, ImageIO.Swift has got your back!</p>]]></content><author><name></name></author><category term="SwiftUI" /><category term="iOS" /><category term="ImageIO" /><category term="Xcode" /><summary type="html"><![CDATA[When Apple released their new view framework, SwiftUI, at WWDC 2019, they had several sessions throughout the week to breakdown and explain how to use the framework. There was a big hole that people are still trying to figure out though: how do you load images! To be fair, this has long been a void in the Apple frameworks. UIKit doesn’t have any builtin way to download and show images either. But given the radical new design of SwiftUI, it’s not obvious how it should be done.]]></summary></entry><entry><title type="html">Digging deeper into the Swift behind SwiftUI</title><link href="https://davidbeck.co/posts/2019-06-11-digging-deeper-into-the-swift-behind-swiftui" rel="alternate" type="text/html" title="Digging deeper into the Swift behind SwiftUI" /><published>2019-06-11T00:00:00+00:00</published><updated>2019-06-11T00:00:00+00:00</updated><id>https://davidbeck.co/posts/digging-deeper-into-the-swift-behind-swiftui</id><content type="html" xml:base="https://davidbeck.co/posts/2019-06-11-digging-deeper-into-the-swift-behind-swiftui"><![CDATA[<p>I don’t know about you, but as awesome as SwiftUI looks, I can’t help but wonder at how it’s actually implimented. As Swift has “evolved” over the years, I feel like I’ve been able to keep up with how things actually work under the hood. Swift tends to build apon itself. For example, if you understand how properties work, it’s easier to understand how subscripts work (they are just properties with an argument).</p>

<p>But this year SwiftUI has really pushed on my understanding of how Swift works. <a href="https://www.swiftbysundell.com/posts/the-swift-51-features-that-power-swiftuis-api">John Sundell did a good job going over the high level features that power SwiftUI</a>. But even with this overview, and reading through the related proposals, I’ve still been very surprised by some of the behaviors of SwiftUI.</p>

<p>What follows are just a few things I’ve run into that made me scratch my head.</p>

<h2 id="view-returning-functions">View returning functions</h2>

<p>Here’s a pretty typical example of a SwiftUI view:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Row</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="kt">HStack</span> <span class="p">{</span>
			<span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"star.fill"</span><span class="p">)</span>
			<span class="kt">Text</span><span class="p">(</span><span class="s">"Hello World!"</span><span class="p">)</span>
			<span class="kt">Spacer</span><span class="p">()</span>
		<span class="p">}</span>
		<span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It’s quit clean and looks a bit like HTML. Notice that the elements under the <code class="language-plaintext highlighter-rouge">HStack</code> are just declared one after the other. Normally, if you wanted to do something like this in Swift, you would need to return an array of those values. With <a href="https://forums.swift.org/t/function-builders/25167">function builders</a>, the compiler will automatically translate this for you. That’s easy enough to grasp.</p>

<p>But what about the body property? That looks similar, but what if we put a second view in there:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Row</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="kt">Text</span><span class="p">(</span><span class="s">"Another one!"</span><span class="p">)</span>
		<span class="kt">HStack</span> <span class="c1">// ...</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Suddenly we get a compile error: <code class="language-plaintext highlighter-rouge">function declares an opaque return type, but has no return statements in its body from which to infer an underlying type</code>.</p>

<p>What’s happening? The body property isn’t using function builders! Instead, it’s expecting us to return a single view. And to avoid having a <code class="language-plaintext highlighter-rouge">return</code>, it’s taking advantage of a new feature, <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md">implicit returns from single-expression functions</a>. This essentially gives functions and properties the same behavior as closures where they can implicitly return their last line… but only if they only have a single line. So when we add an extra line to the body (in this case another view, but it could have been a print statement) the compiler freaks out because it doesn’t think you’re returning anything anymore.</p>

<p>2 new Swift features… that look very much alike… but are actually completely different.</p>

<h2 id="state-details">State details</h2>

<p>Let’s replace our label with a text field:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Row</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="kd">struct</span> <span class="kt">User</span> <span class="p">{</span>
		<span class="k">var</span> <span class="nv">email</span><span class="p">:</span> <span class="kt">String</span>
	<span class="p">}</span>
	<span class="kd">@State</span> <span class="k">var</span> <span class="nv">user</span> <span class="o">=</span> <span class="kt">User</span><span class="p">(</span><span class="nv">email</span><span class="p">:</span> <span class="s">"Hello"</span><span class="p">)</span>
	
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="kt">HStack</span> <span class="p">{</span>
			<span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"star.fill"</span><span class="p">)</span>
			<span class="kt">TextField</span><span class="p">(</span><span class="err">$</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
			<span class="kt">Spacer</span><span class="p">()</span>
		<span class="p">}</span>
		<span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Another new Swift feature, <a href="https://forums.swift.org/t/pitch-3-property-wrappers-formerly-known-as-property-delegates/24961">property wrappers</a> power a lot of SwiftUI. By marking our property with <code class="language-plaintext highlighter-rouge">@State</code> we get some update magic (more on that later). After reading the proposal, we learn that <code class="language-plaintext highlighter-rouge">State</code> is just a struct that wraps it’s property. In theory, it’s pretty easy to reason about. <code class="language-plaintext highlighter-rouge">self.user</code> gives us the wrapped value, while <code class="language-plaintext highlighter-rouge">self.$user</code> gives us the actual struct.</p>

<p>But what about that <code class="language-plaintext highlighter-rouge">$user.email</code>? If <code class="language-plaintext highlighter-rouge">$user</code> returns the <code class="language-plaintext highlighter-rouge">State</code> struct, wouldn’t we be pulling a value from that struct?</p>

<p>I got the answer to this one from <a href="https://developer.apple.com/videos/play/wwdc2019/415/">Session 415:Modern Swift API Design</a>. Turns out, there’s another new Swift feature that is being used here: <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0252-keypath-dynamic-member-lookup.md">key path member lookup</a>. I completely missed this when it went through evolution. It expands <code class="language-plaintext highlighter-rouge">@dynamicMemberLookup</code> to use key paths as an alternative to strings. What’s actually happening with <code class="language-plaintext highlighter-rouge">$user.email</code>, is a dynamic member lookup, using key paths from the wrapped value, to return the child property… also wrapped in a <code class="language-plaintext highlighter-rouge">Binding</code> struct (with a type <code class="language-plaintext highlighter-rouge">Binding&lt;String&gt;</code>).</p>

<h2 id="how-does-it-know-about-state">How does it know about state?!?!</h2>

<p>Apple says that when you wrap a property with <code class="language-plaintext highlighter-rouge">@State</code>,
SwiftUI “sees” when it’s used in the build method.</p>

<p>How? What is state doing that allows SwiftUI to know when it’s used.
How does it trigger a re-render on change?
Is it using thread local variables (not a good practice)?
Or since it only works on the main thread, maybe there is a global render object
(also not a good practice)?
Are they looking at the call stack?
That also doesn’t seem like a good idea, 
but it’s something Apple has done in the recent past.</p>

<h2 id="state-being-converted-into-binding">State being converted into Binding</h2>

<p>Going further with our text field above, the reason we pass in <code class="language-plaintext highlighter-rouge">$user.email</code> is because it takes a binding so it can update the value. We actually can’t pass in a static string. The dynamic member lookup wraps the property it returns into a binding so all is kosher.</p>

<p>But what if we were just using a state property directly?</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Row</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="kd">@State</span> <span class="k">var</span> <span class="nv">email</span> <span class="o">=</span> <span class="s">"Hello"</span>
	
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="kt">HStack</span> <span class="p">{</span>
			<span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"star.fill"</span><span class="p">)</span>
			<span class="kt">TextField</span><span class="p">(</span><span class="err">$</span><span class="n">email</span><span class="p">)</span>
			<span class="kt">Spacer</span><span class="p">()</span>
		<span class="p">}</span>
		<span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I would think that wouldn’t work, but it does. <code class="language-plaintext highlighter-rouge">TextField</code> takes a <code class="language-plaintext highlighter-rouge">Binding</code>, but here we are passing in a <code class="language-plaintext highlighter-rouge">State</code>.</p>

<p>How does this work? I don’t know. <code class="language-plaintext highlighter-rouge">State</code> does conform to a protocol, <a href="https://developer.apple.com/documentation/swiftui/bindingconvertible"><code class="language-plaintext highlighter-rouge">BindingConvertible</code></a>, but how this gets translated into a <code class="language-plaintext highlighter-rouge">Binding</code> is a mystery to me. It’s behavior kind of reminds me of <a href="https://swiftdoc.org/v1.2/protocol/_objectivecbridgeable/">_ObjectiveCBridgeable</a>, but there might be a less magical explenation.</p>

<h3 id="61419-update">6/14/19 Update</h3>

<p>Turns out this is implimented using a <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#delegating-access-to-the-storage-property">wrapperValue</a>. Property wrappers can optionally change what gets returned when you call the wrapper. I really do not like this part of the proposal (and it seems others in Swift Evolution agree). I can’t think of another use for it outside of SwiftUI and it is very confusing IMHO. It of course makes the demos of SwiftUI cleaner, but at the cost of making them harder to understand.</p>

<h2 id="generated-initializers-and-wrappers">Generated initializers and wrappers</h2>

<p>Speaking of init params, let’s define our own views with bindings:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyTextField</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">text</span><span class="p">:</span> <span class="kt">String</span>
	
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
		<span class="kt">TextField</span><span class="p">(</span><span class="err">$</span><span class="n">text</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I didn’t specify an intializer here, but Swift of course generates one for us (thank you Swift). If we drop this into our example from earlier, autocomplete tells us that the generated init wants a <code class="language-plaintext highlighter-rouge">Binding</code>.</p>

<p>But what if we swap out <code class="language-plaintext highlighter-rouge">Binding</code> for <code class="language-plaintext highlighter-rouge">State</code>? (I don’t think you would actually want to do this, but just go with it.) Well now the compiler complains that it can’t convert <code class="language-plaintext highlighter-rouge">Binding&lt;String&gt;</code> to <code class="language-plaintext highlighter-rouge">String</code>. The generated init method all of a sudden changes from wanting the wrapper to wanting the wrapped value. But why?!?</p>

<p>I figured this one out from session 415 as well.
It comes down to how those wrappers are defined. 
<code class="language-plaintext highlighter-rouge">State</code> has the following init method:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">init</span><span class="p">(</span><span class="n">initialValue</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Value</span><span class="p">)</span>
</code></pre></div></div>

<p>This is a magic format for property wrappers. When you have an init method of that form, Swift knows how to create the wrapper with an initial value.</p>

<p><code class="language-plaintext highlighter-rouge">Binding</code> on the other hand has several public init methods, but none that match that format. So Swift can’t create it on it’s own with a value, so it falls back to requiring you to manually create (or set) the wrapper.</p>

<p>Side note, you can actually initialize wrappers directly in your init method as well.
For instance, to expand upon the <code class="language-plaintext highlighter-rouge">UserDefault</code> example Apple has been using, 
we can allow customizing which <code class="language-plaintext highlighter-rouge">UserDefaults</code> instance gets used (along with a default):</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyDelegate</span>
<span class="kd">struct</span> <span class="kt">UserDefault</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span> <span class="p">{</span>
	<span class="k">let</span> <span class="nv">userDefaults</span><span class="p">:</span> <span class="kt">UserDefaults</span>
	<span class="k">let</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span>
	
	<span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">T</span> <span class="p">{</span>
		<span class="k">get</span> <span class="p">{</span>
			<span class="k">return</span> <span class="n">userDefaults</span><span class="o">.</span><span class="nf">object</span><span class="p">(</span><span class="nv">forKey</span><span class="p">:</span> <span class="n">key</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">T</span>
		<span class="p">}</span>
		<span class="k">set</span> <span class="p">{</span>
			<span class="n">userDefaults</span><span class="o">.</span><span class="nf">set</span><span class="p">(</span><span class="n">newValue</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="n">key</span><span class="p">)</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
	<span class="kd">@UserDefault</span> <span class="k">var</span> <span class="nv">bar</span><span class="p">:</span> <span class="kt">String</span>
	
	<span class="nf">init</span><span class="p">(</span><span class="nv">userDefaults</span><span class="p">:</span> <span class="kt">UserDefaults</span> <span class="o">=</span> <span class="o">.</span><span class="n">standard</span><span class="p">)</span> <span class="p">{</span>
		<span class="err">$</span><span class="n">bar</span> <span class="o">=</span> <span class="kt">UserDefault</span><span class="p">(</span><span class="nv">userDefaults</span><span class="p">:</span> <span class="n">userDefaults</span><span class="p">,</span> <span class="nv">key</span><span class="p">:</span> <span class="s">"com.me.Foo.bar"</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="some-what">Some what?</h2>

<p>When you define your views, they have 1 requirement: the body property. It returns another view. But not just <em>any</em> view! It returns <code class="language-plaintext highlighter-rouge">some View</code>. What’s the deal?</p>

<p>The <code class="language-plaintext highlighter-rouge">View</code> protocol has an associated type <a href="https://developer.apple.com/documentation/swiftui/view/3125355-body"><code class="language-plaintext highlighter-rouge">Body</code></a>. It’s similar to how <code class="language-plaintext highlighter-rouge">Collection</code> has an associated type for it’s elements. It’s a form of generics.</p>

<p>Normally, if you have a type that conforms to a protocol with an associated type, you need to define that type <strong>somehow</strong>. Either with a typealias, or implicitly like so:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Row</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
	<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kt">HStack</span> <span class="p">{</span>
		<span class="kt">HStack</span> <span class="p">{</span>
			<span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"star.fill"</span><span class="p">)</span>
			<span class="kt">TextField</span><span class="p">(</span><span class="err">$</span><span class="n">email</span><span class="p">)</span>
			<span class="kt">Spacer</span><span class="p">()</span>
		<span class="p">}</span>
		<span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Except that won’t work. <code class="language-plaintext highlighter-rouge">HStack</code> is also generic. The actual return type should be: <code class="language-plaintext highlighter-rouge">_ModifiedContent&lt;HStack&lt;TupleView&lt;(Image, Text, Spacer)&gt;&gt;, _PaddingLayout&gt;</code>. Kind of a mouthful huh? And it would change if we changed our hierarchy. Essentially every view we render in the entire hierarchy ends up being in that type, and then some. If you change the text color for instance, the <code class="language-plaintext highlighter-rouge">Text</code> part of that type becomes <code class="language-plaintext highlighter-rouge">_ModifiedContent&lt;Text, _EnvironmentKeyWritingModifier&lt;Color?&gt;&gt;</code>.</p>

<p><strong>This is generics on steroids. 😳</strong></p>

<p>And we can’t just reuturn <code class="language-plaintext highlighter-rouge">View</code>. <code class="language-plaintext highlighter-rouge">Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements</code>. You may have seen an error like that if you’ve ever tried to use <code class="language-plaintext highlighter-rouge">Equatable</code> like a regular protocol.</p>

<p>SwiftUI does come with <a href="https://developer.apple.com/documentation/swiftui/anyview">AnyView</a>, which is a type erased view, but you have to explicitely wrap your result in it. From what I’ve heard, using it has performance implications. 
Not to mention, how this works (it’s <code class="language-plaintext highlighter-rouge">Body</code> type is <code class="language-plaintext highlighter-rouge">Never</code> 😅) just leads to more questions.</p>

<p>Obviously normal users would never be able to figure out what their associated type should be without some help. So, we have <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md">opaque result types</a> instead. <code class="language-plaintext highlighter-rouge">some View</code> basically asks the compiler to figure out what our return type is. But the type is still there. That’s actually important for performance reasons though…</p>

<p>Since it’s inception, Swift has tried to hide as much of it’s safety and performance features as possible. For example, it’s <strong>very</strong> strongly typed. Everything must have a very specific type. But if you just type <code class="language-plaintext highlighter-rouge">var foo = "bar"</code>, the compiler will figure out the type for you. All while maintaining type safety and performance.</p>

<p>Generics have never quit reached that level of simplicity though. <code class="language-plaintext highlighter-rouge">foo.map{ $.count }</code> will figure out that you want to convert an array of strings into an array of integers, but when you start creating really complicated expressions it gets confused fast. Make a mistake in the wrong spot, and you’ll get a seemingly completely unrelated error several lines away.</p>

<p>And I’ve seen SwiftUI get <strong>very</strong> confused. At WWDC, Apple kept advertising how easy it was to break out parts of the view hierarchy into their own smaller views, and how it wouldn’t effect performance like creating extra <code class="language-plaintext highlighter-rouge">UIView</code> wrappers would. But if you <strong>don’t</strong> want to break up your views, you’ll quickly find that the type you are returning gets <strong>very</strong> complicated, and often the compiler gets confused in the process.</p>

<hr />

<p>SwiftUI definitely seems to be pushing the boundaries of what is possible in Swift. For years we’ve been talking about what a truly Swift native framework would look like, and I think this is the answer. By dropping interoperability with Objective-C, the framework is free to really dig deep into the language.</p>

<p>I’m curious to see how this plays out. Will we continue down this path? Will we realize that all these fancy features are a bit much and pull back a little? Time will tell.</p>

<p>When object oriented languages became popular, deep levels of subclasses became popular.
One class would subclass another, which would subclass another, and so on and so forth.
With time we realized that it was more useful to have shallow sublcass hierarchies.
Perhaps we’ll see a similar trend with generics.
Or maybe we’ll figure out a way to tame the generics dragon. 🤷🏻‍♀️</p>]]></content><author><name></name></author><category term="Swift" /><category term="SwiftUI" /><category term="Apple" /><category term="iOS" /><category term="macOS" /><category term="Xcode" /><summary type="html"><![CDATA[I don’t know about you, but as awesome as SwiftUI looks, I can’t help but wonder at how it’s actually implimented. As Swift has “evolved” over the years, I feel like I’ve been able to keep up with how things actually work under the hood. Swift tends to build apon itself. For example, if you understand how properties work, it’s easier to understand how subscripts work (they are just properties with an argument).]]></summary></entry><entry><title type="html">My SwiftUI Hot Take 🔥</title><link href="https://davidbeck.co/posts/2019-06-08-swiftui-hot-take" rel="alternate" type="text/html" title="My SwiftUI Hot Take 🔥" /><published>2019-06-08T00:00:00+00:00</published><updated>2019-06-08T00:00:00+00:00</updated><id>https://davidbeck.co/posts/swiftui-hot-take</id><content type="html" xml:base="https://davidbeck.co/posts/2019-06-08-swiftui-hot-take"><![CDATA[<p>SwiftUI looks amazing. There have been a lot of attempts to bring React concepts to Swift. This is the first implimentation I’ve seen that looked right.</p>

<h2 id="only-apple-could-do-this">Only Apple could do this</h2>

<p>Part of that is because Apple is backing the framework. You don’t have to worry about it integrating well with UIKit/AppKit. You also don’t have to worry about being punished next year for using something non-standard. You can be sure that Apple will support andy new features from the OS in SwiftUI.</p>

<p>But the big reason it works where other frameworks didn’t is because Apple actually added several new features to Swift itself. <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md">SE-0244 (enabling <code class="language-plaintext highlighter-rouge">some View</code>)</a> was approved recently. 2 other features are so new they don’t even have evolution numbers. <a href="https://forums.swift.org/t/pitch-3-property-wrappers-formerly-known-as-property-delegates/24961">Property wrappers (enabling <code class="language-plaintext highlighter-rouge">@State var</code>)</a> has gone through several pitch rounds and is still being ironed out. <a href="https://forums.swift.org/t/pitch-function-builders/25167">Function builders (enabling implicit returns of multiple children)</a> wasn’t even mentioned until SwiftUI was announced.</p>

<p>This is clearly only something Apple can do. Sure, theoretically Swift Evolution is an open process and anyone could have made these pitches, but Apple is using a pretty heavy hand to get them pushed through. I think it’s especially telling that they waited until after announcing SwiftUI to introduce the property builders pitch. I can imagine that if that had been introduced a few weeks ago, it might have easily been rejected for not being Swifty enough. The details of those features may change over the summer, but I garuntee you that some form of them will pass review by the time the Xcode 11 GM ships.</p>

<h2 id="at-least-for-now-swiftui-is-high-level">At least for now, SwiftUI is high level</h2>

<p>The actual engine that renders views is versitile enough to render even the smallest details of a UI. But the views that Apple has built in are all generic abstractions. That makes it great for hight level abstractions that can be adapted to multiple platforms. It makes it a little more difficult to fine tune your UI. Will your view render a UILabel or an NSMenuItem? You’re not suppose to know. We’ll see where this goes.</p>

<h2 id="the-integration-points-are-interesting">The integration points are interesting</h2>

<p>You can embed a UI/NSView or a UI/NSViewController anywhere in your view hierarchy. On the other side, you can embed SwiftUI in an UI/NSViewController and NSView, but not UIView. I wonder if that’s for performance reasons or a “political” reason. I imagine that since view controllers aren’t as popular on Mac, that they may have been somewhat forced to support NSView.</p>

<h2 id="there-are-going-to-be-things-that-are-hard">There are going to be things that are hard</h2>

<p>Reactive programming is really useful and powerful, but there are still interactions that are easier to do with imperitive code. Even more so, it’s going to be a big shift in how we think about rendering UI.</p>

<h2 id="i-wouldnt-hold-my-breath-for-other-platforms">I wouldn’t hold my breath for other platforms</h2>

<p>A lot of people are talking about SwiftUI on Android or the Web. It’s an obvious next step. Apple has made an <strong>excellent</strong> cross platform framework, and people immediately want to use it for all the platforms. But I highly doubt Apple will ever add support for Android or the Web. And furthermore, it wouldn’t be native on those platforms either unless Google abandons their react like frameworks and adopts Swift (unlikely).</p>

<h2 id="xcode-11-isnt-as-polished-as-the-demos-imply">Xcode 11 isn’t as polished as the demos imply</h2>

<p>On my 6 Core/32GB MacBook Pro previews take a lot longer to render than they did in those demos. Sometimes they pause rendering for some reason. This is with the very basic templates. Nothing fancy. I’m not sure what those demos are doing differently (perhaps that’s why Apple really needed to make the Mac Pro) but your milage may vary.</p>

<h2 id="its-still-early-stages">It’s still early stages</h2>

<p>Don’t get me wrong, you can build production apps with this come the Fall, but you might not want to. This feels a lot like Swift 1.0. Back then a lot of people (myself included) paniced and felt like ObjC would be deprecated immediately. Those who stuck with ObjC know that it hasn’t gone anywhere. UIKit will still work great for years to come. You have time. Which brings me to:</p>

<h2 id="the-fact-that-its-ios-13macos-1015etc-only-is-a-good-thing">The fact that it’s iOS 13/macOS 10.15/etc only is a good thing</h2>

<p>Swift suffered from too much attention in it’s infancy. I think the fact that most people won’t be able to ship anything for a year or 2 with this will help SwiftUI breath and develop more gracefully.</p>]]></content><author><name></name></author><category term="Swift" /><category term="SwiftUI" /><category term="Apple" /><category term="iOS" /><category term="macOS" /><category term="Xcode" /><summary type="html"><![CDATA[SwiftUI looks amazing. There have been a lot of attempts to bring React concepts to Swift. This is the first implimentation I’ve seen that looked right.]]></summary></entry><entry><title type="html">Rapid API Development with PostGraphile and Auth0</title><link href="https://davidbeck.co/posts/2019-02-25-graphile-and-auth0" rel="alternate" type="text/html" title="Rapid API Development with PostGraphile and Auth0" /><published>2019-02-25T00:00:00+00:00</published><updated>2019-02-25T00:00:00+00:00</updated><id>https://davidbeck.co/posts/graphile-and-auth0</id><content type="html" xml:base="https://davidbeck.co/posts/2019-02-25-graphile-and-auth0"><![CDATA[<p><a href="https://www.graphile.org/postgraphile/">PostGraphile</a> is a really great tool for <a href="http://localhost:4000/posts/2019-02-12-rapid-api-development-with-postgraphile">creating api backends very quickly primarily using PostgreSQL</a>. However, it does require you to rethink how you make apis. This is especially true for authorization and authentication. But it certainly is possible (even easy) to create secure apis using Graphile.</p>

<h2 id="an-example">An example</h2>

<p>Let’s look at what it takes to make an api for a web app that includes login and security. <a href="http://todomvc.com">TodoMVC</a> is a project that tries to impliment the same web app in multiple different frameworks. I’m using an <a href="https://github.com/ChrisWiles/React-todoMVC">implimentation by chriswiles</a> as a starting point. It uses a more recent version of React than the official React implimentation.</p>

<p>If you run the project locally (or try it out at <a href="https://chriswiles.github.io/React-todoMVC/">chriswiles.github.io/React-todoMVC</a>) you can add todo items, remove them, mark them complete, filter, etc. But, alas, if you refresh they will all be gone (that’s not the right way to clear your todos).</p>

<p>Let’s fix that by adding an api and saving results to the cloud ☁️🌈.</p>

<p>I’ve created a basic GraphQL api using PostGraphile and integrated that into the client using Apollo. You can see that on the <a href="https://github.com/davbeck/Postgraphile-todoMVC/tree/no-auth"><code class="language-plaintext highlighter-rouge">no-auth</code></a> branch in <a href="https://github.com/davbeck/Postgraphile-todoMVC">the sample project</a>.</p>

<p>I’m using <a href="https://github.com/amacneil/dbmate">dbmate</a> to handle the migrations. You can see the entire schema under <code class="language-plaintext highlighter-rouge">db/schema.sql</code>. The relavant parts for the api are the table and 2 functions:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="p">(</span>
    <span class="n">id</span> <span class="nb">integer</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">title</span> <span class="nb">text</span><span class="p">,</span>
    <span class="n">completed</span> <span class="nb">boolean</span> <span class="k">DEFAULT</span> <span class="k">false</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="nv">"order"</span> <span class="nb">integer</span> <span class="k">DEFAULT</span> <span class="mi">0</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">created_at</span> <span class="nb">timestamp</span> <span class="k">with</span> <span class="nb">time</span> <span class="k">zone</span> <span class="k">DEFAULT</span> <span class="n">now</span><span class="p">()</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">updated_at</span> <span class="nb">timestamp</span> <span class="k">with</span> <span class="nb">time</span> <span class="k">zone</span> <span class="k">DEFAULT</span> <span class="n">now</span><span class="p">()</span> <span class="k">NOT</span> <span class="k">NULL</span>
<span class="p">);</span>

<span class="k">CREATE</span> <span class="k">FUNCTION</span> <span class="n">app</span><span class="p">.</span><span class="n">clear_completed</span><span class="p">()</span> <span class="k">RETURNS</span> <span class="k">SETOF</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span>
    <span class="k">LANGUAGE</span> <span class="k">sql</span>
    <span class="k">AS</span> <span class="err">$$</span>
<span class="k">DELETE</span> <span class="k">FROM</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">WHERE</span> <span class="n">completed</span> <span class="o">=</span> <span class="k">true</span> <span class="n">RETURNING</span> <span class="o">*</span><span class="p">;</span>
<span class="err">$$</span><span class="p">;</span>

<span class="k">CREATE</span> <span class="k">FUNCTION</span> <span class="n">app</span><span class="p">.</span><span class="n">complete_all</span><span class="p">()</span> <span class="k">RETURNS</span> <span class="k">SETOF</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span>
    <span class="k">LANGUAGE</span> <span class="k">sql</span>
    <span class="k">AS</span> <span class="err">$$</span>
<span class="k">UPDATE</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span>
   <span class="k">SET</span> <span class="n">completed</span> <span class="o">=</span> <span class="k">NOT</span> <span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">WHERE</span> <span class="n">completed</span> <span class="o">=</span> <span class="k">true</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span><span class="p">)</span>
<span class="n">RETURNING</span> <span class="o">*</span><span class="p">;</span>
<span class="err">$$</span><span class="p">;</span>
</code></pre></div></div>

<p>It’s kind of cool to see how little it takes to create a basic api using Graphile. <a href="https://www.todobackend.com">Todo-Backend</a> is a similar project that impliments an api for todo items in multiple languages and frameworks. That project assumes a REST api, so our backend won’t be compatable with that, but at this point we have everything defined in that spec.</p>

<h2 id="adding-authentication">Adding authentication</h2>

<p>This is the part where I’m suppose to say, “this is an improvement but we can make it better…”. But, it’s not <em>really</em> an improvement if you think about it. Now when you refresh, or open the site on a different device, you’re changes are still available, but they are also available for everyone! That’s not very good for privacy.</p>

<p>What we need is to add a login and only show the current user’s todo items. We need an api that not only filters what the user wants to see, but also enforces any malicious users from accessing someone else’s items.</p>

<p>You can create your own login system using Graphile. <a href="https://www.graphile.org/postgraphile/postgresql-schema-design/">The docs have a pretty good tutorial on that</a>. But, for rapid development, you’ll want something pre-built. That is where <a href="https://auth0.com">Auth0</a> comes in. It provides the basics for creating accounts and login, but also things like forgot password, single sign on with sites like Google and Facebook, 2 factor authentication, and much much more.</p>

<p><span class="side-note">
If you want to follow along with the sample code and run it yourself, you’ll need to <a href="https://auth0.com/signup">create an Auth0 account</a> and add your account values to the <code class="language-plaintext highlighter-rouge">.env</code> files.
</span></p>

<p>There are a lot of ways to authenticate with Auth0, especially depending on what platform (web, iOS, Android) you are using. For our React app, I mostly used the <a href="https://auth0.com/docs/quickstart/spa/react/01-login">quickstart guide from Auth0</a> with 2 modifications. First, instead of showing a login button when a new user opens the page, I immediately redirect to the Auth0 login page. User’s won’t be able to access anything until they login, so why not skip a step for them. Second, when I get an access token I update a token cookie with it’s value. This will automatically get passed to our api (see <a href="https://www.apollographql.com/docs/react/recipes/authentication.html#Cookie">the apollo documentation for more info</a>). You can see this in <a href="https://github.com/davbeck/Postgraphile-todoMVC/blob/authentication/client/src/Auth.js"><code class="language-plaintext highlighter-rouge">client/src/Auth.js</code></a>.</p>

<p>Next, we need to parse that token in our api and pass it into Graphile and Postgres. Graphile has a config method called <a href="https://www.graphile.org/postgraphile/usage-library/#pgsettings-function"><code class="language-plaintext highlighter-rouge">pgSettings</code></a> that allows you to pass in values to the Postgres transaction, including a role:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">pgSettings</span><span class="p">:</span> <span class="k">async</span> <span class="nx">req</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">claimes</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">parseClaims</span><span class="p">(</span><span class="nx">req</span><span class="p">);</span>

    <span class="k">return</span> <span class="p">{</span>
      <span class="na">role</span><span class="p">:</span> <span class="dl">"</span><span class="s2">todo_user</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">user.id</span><span class="dl">"</span><span class="p">:</span> <span class="nx">claimes</span><span class="p">.</span><span class="nx">sub</span><span class="p">,</span>
    <span class="p">};</span>
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">failed to authenticate</span><span class="dl">"</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">{</span> <span class="na">role</span><span class="p">:</span> <span class="dl">"</span><span class="s2">todo_anonymous</span><span class="dl">"</span> <span class="p">};</span>
  <span class="p">}</span>
<span class="p">},</span>
</code></pre></div></div>

<p>You can see how the token gets parsed in <a href="https://github.com/davbeck/Postgraphile-todoMVC/blob/master/auth.js">auth.js</a>. We are also setting a default role (<code class="language-plaintext highlighter-rouge">todo_anonymous</code>) if the user is not authenticated. There are cases where an unauthenticated user can still use parts of your api, for instance on Twitter anyone can see public profiles. That’s not true here, but it’s still important to set a limited role as the default to keep unauthenticated users from having full access.</p>

<p>But, we don’t have a <code class="language-plaintext highlighter-rouge">todo_user</code> or <code class="language-plaintext highlighter-rouge">todo_anonymous</code> role yet. Let’s create that along with in a migration:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">ROLE</span> <span class="n">todo_anonymous</span><span class="p">;</span>
<span class="k">CREATE</span> <span class="k">ROLE</span> <span class="n">todo_user</span><span class="p">;</span>

<span class="k">GRANT</span> <span class="k">USAGE</span> <span class="k">ON</span> <span class="k">SCHEMA</span> <span class="n">app</span> <span class="k">TO</span> <span class="n">todo_anonymous</span><span class="p">,</span> <span class="n">todo_user</span><span class="p">;</span>

<span class="k">GRANT</span> <span class="k">SELECT</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">TO</span> <span class="n">todo_user</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="k">INSERT</span> <span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">completed</span><span class="p">,</span> <span class="nv">"order"</span><span class="p">)</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">TO</span> <span class="n">todo_user</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="k">UPDATE</span> <span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">completed</span><span class="p">,</span> <span class="nv">"order"</span><span class="p">)</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">TO</span> <span class="n">todo_user</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="k">DELETE</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">TO</span> <span class="n">todo_user</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="k">USAGE</span> <span class="k">ON</span> <span class="n">SEQUENCE</span> <span class="n">app</span><span class="p">.</span><span class="n">todo_id_seq</span> <span class="k">to</span> <span class="n">todo_user</span><span class="p">;</span>
</code></pre></div></div>

<p><span class="side-note">
While most things in Postgres are constrained to the database they are created in (including schemas, tables and functions) roles are global to a Postgres server isntance, so it’s important that we prefix them. If we just called this role something like “user” it would clash with other projects that also used that role name on the same server. This is especially common when working locally. Also, roles don’t show up in schema.sql generated by dbmate.
</span></p>

<p>Here we create our roles and give <code class="language-plaintext highlighter-rouge">todo_user</code> access to our todo table (including the auto increment id function which is required for INSERT). We don’t want clients manually setting an id, update_at, so we only grant permissions for updating and inserting specific fields.</p>

<p>If you run those migrations and try the app out at this point (see the <a href="https://github.com/davbeck/Postgraphile-todoMVC/tree/authentication"><code class="language-plaintext highlighter-rouge">authentication</code></a> branch in the sample code), you should be redirected to login, and then have access to the api just like before. If you try opening graphiql and sending requests without logging in, you’ll get access errors, meaning the api is secure from unauthenticated users.</p>

<h2 id="adding-authorization">Adding authorization</h2>

<p>However, we still have our original problem, which is that everyone is sharing a single todo list. Anyone can create an account, and from there anyone has access to everyone else’s todo items. This is the difference between authentication and authorization.</p>

<p>In order to authorize access to specific resources, we will use <a href="https://www.postgresql.org/docs/current/ddl-rowsecurity.html">row-level security</a>. But first, we need to mark todo items with their user:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">SCHEMA</span> <span class="n">app_hidden</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="k">USAGE</span> <span class="k">ON</span> <span class="k">SCHEMA</span> <span class="n">app_hidden</span> <span class="k">TO</span> <span class="n">todo_anonymous</span><span class="p">,</span> <span class="n">todo_user</span><span class="p">;</span>

<span class="k">CREATE</span> <span class="k">OR</span> <span class="k">REPLACE</span> <span class="k">FUNCTION</span> <span class="n">app_hidden</span><span class="p">.</span><span class="n">current_user_id</span><span class="p">()</span> <span class="k">RETURNS</span> <span class="nb">text</span> <span class="k">AS</span> <span class="err">$$</span>
  <span class="k">SELECT</span> <span class="k">nullif</span><span class="p">(</span><span class="n">current_setting</span><span class="p">(</span><span class="s1">'user.id'</span><span class="p">,</span> <span class="k">true</span><span class="p">),</span> <span class="s1">''</span><span class="p">)::</span><span class="nb">text</span><span class="p">;</span>
<span class="err">$$</span> <span class="k">LANGUAGE</span> <span class="k">sql</span> <span class="k">STABLE</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="k">EXECUTE</span> <span class="k">ON</span> <span class="k">FUNCTION</span> <span class="n">app_hidden</span><span class="p">.</span><span class="n">current_user_id</span><span class="p">()</span> <span class="k">TO</span> <span class="n">todo_anonymous</span><span class="p">,</span> <span class="n">todo_user</span><span class="p">;</span>

<span class="k">DELETE</span> <span class="k">FROM</span> <span class="nv">"app"</span><span class="p">.</span><span class="nv">"todo"</span><span class="p">;</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="nv">"app"</span><span class="p">.</span><span class="nv">"todo"</span> <span class="k">ADD</span> <span class="k">COLUMN</span> <span class="nv">"user_id"</span> <span class="nb">text</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">DEFAULT</span> <span class="n">app_hidden</span><span class="p">.</span><span class="n">current_user_id</span><span class="p">();</span>
</code></pre></div></div>

<p>We’re creating our 3rd schema here (<code class="language-plaintext highlighter-rouge">app_hidden</code>). This lives between <code class="language-plaintext highlighter-rouge">app</code> and <code class="language-plaintext highlighter-rouge">app_private</code>. This schema will not be exposed to the api, but will be usable from other functions, or in this case, as a default value. That’s different from <code class="language-plaintext highlighter-rouge">app_private</code> which is inaccessible for security reasons.</p>

<p>Graphile sets any values we return from <code class="language-plaintext highlighter-rouge">pgSettings</code> in Postgres, so we can access it using <code class="language-plaintext highlighter-rouge">current_setting</code>. Here we add a function to access that setting more easily.</p>

<p>Finally, we add a <code class="language-plaintext highlighter-rouge">user_id</code> column to our todo table, using the current user id as a default value. We have to delete the existing items though because they don’t have a user associated with them and Postgres would complain about our <code class="language-plaintext highlighter-rouge">NOT NULL</code> constraint.</p>

<p>If you create some new todo items now and view them in the database, you can see that they have a <code class="language-plaintext highlighter-rouge">user_id</code> set.</p>

<p>Next let’s use role level security to make sure users can only see their own items:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">ALTER</span> <span class="k">TABLE</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="n">ENABLE</span> <span class="k">ROW</span> <span class="k">LEVEL</span> <span class="k">SECURITY</span><span class="p">;</span>

<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">select_todo</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">FOR</span> <span class="k">SELECT</span> <span class="k">TO</span> <span class="n">todo_user</span>
 <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">app_hidden</span><span class="p">.</span><span class="n">current_user_id</span><span class="p">());</span>
</code></pre></div></div>

<p>What’s really cool here is that not only does it enforce who can see what rows from a security standpoint, but Postgres will also automatically filter out any rows that don’t match our condition. Try creating a few results using different user_ids and the api will only return the ones matching the current user.</p>

<p><span class="side-note">
The condition on policies can be quit complex too. For instance, in a project I’m working on, I control access to a “team” table based on a separate “membership” table as well as an <code class="language-plaintext highlighter-rouge">is_admin</code> column on the “user” table.
</span></p>

<p>Let’s do the same for insert, update and delete:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">insert_todo</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">FOR</span> <span class="k">INSERT</span> <span class="k">TO</span> <span class="n">todo_user</span>
  <span class="k">WITH</span> <span class="k">CHECK</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">app_hidden</span><span class="p">.</span><span class="n">current_user_id</span><span class="p">());</span>

<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">update_todo</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">FOR</span> <span class="k">UPDATE</span> <span class="k">TO</span> <span class="n">todo_user</span>
 <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">app_hidden</span><span class="p">.</span><span class="n">current_user_id</span><span class="p">());</span>

<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">delete_todo</span> <span class="k">ON</span> <span class="n">app</span><span class="p">.</span><span class="n">todo</span> <span class="k">FOR</span> <span class="k">DELETE</span> <span class="k">TO</span> <span class="n">todo_user</span>
 <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">app_hidden</span><span class="p">.</span><span class="n">current_user_id</span><span class="p">());</span>
</code></pre></div></div>

<p><span class="side-note">
Alternatively, if all your policies use the same restrictions like they do here, you can set all 4 CRUD policies with <code class="language-plaintext highlighter-rouge">CREATE POLICY all_own ON app.todo FOR ALL TO todo_user USING (user_id = app_hidden.current_user_id());</code>. Other times you might have different rules for different operations. A blog for instance would allow anyone to <code class="language-plaintext highlighter-rouge">SELECT</code> but only the author or admin to update.
</span></p>

<p>With that, the api is secure. You have to have an account to access todo items, you can only see your own todo items, you can only create todos that are assigned to you (and that gets set automatically), you can only update your own items (and you can’t change a user_id to or from someone else’s), and you can only delete your own items.</p>

<h2 id="cleaning-up-the-api">Cleaning up the api</h2>

<p>If you poke around GraphiQL (or the GraphQL schema) you’ll notice that <code class="language-plaintext highlighter-rouge">createTodo</code> and <code class="language-plaintext highlighter-rouge">updateTodo</code> include fields that the user can’t actually edit:</p>

<p><img src="/images/2019-02-25-graphile-and-auth0/api-cleanpup.png" /></p>

<p>If a client actually tries to set these fields, Postgres will block the change based on our grants above. However it’s fairly messy to have these hanging around the schema.</p>

<p>You could remove the fields using <a href="https://www.graphile.org/postgraphile/smart-comments/">smart comments</a>, but a cleaner approach is to use have Graphile detect what can and cannot be edited.</p>

<p>Graphile has an option <code class="language-plaintext highlighter-rouge">ignoreRBAC</code> (ignore role-based access control) which is currently true by default. When set to false it will remove any operations or fields that the connection doesn’t have access to. There is 1 catch though: so far we’ve been using an admin user to connect to Postgres with. We control access using roles, but the the schema is generated using the user we connect to the database with. In order for RBAC to take effect in the schema we need to create a new role/login for Graphile.</p>

<p>In a Postgres console:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">ROLE</span> <span class="n">todo_graphile</span> <span class="n">LOGIN</span> <span class="n">NOINHERIT</span> <span class="n">PASSWORD</span> <span class="s1">'password'</span><span class="p">;</span>
</code></pre></div></div>

<p>Or in a terminal shell:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psql <span class="nt">-c</span> <span class="s2">"CREATE ROLE todo_graphile LOGIN NOINHERIT PASSWORD 'password';"</span>
</code></pre></div></div>

<p>We don’t want to put this in a migration because on a production server, we would want to give this role a very long and secure password.</p>

<p>Next, we give this new user access to each of our other roles.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">GRANT</span> <span class="n">todo_user</span> <span class="k">TO</span> <span class="n">todo_graphile</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="n">todo_anonymous</span> <span class="k">TO</span> <span class="n">todo_graphile</span><span class="p">;</span>
</code></pre></div></div>

<p>Then the connetion string used with Graphile needs to be changed to use that new user. In the sample project, I’ve added <code class="language-plaintext highlighter-rouge">GRAPHILE_URL</code> to the .env file and switched to using that variable in index.js. The original <code class="language-plaintext highlighter-rouge">DATABASE_URL</code> is still needed to run migrations using dbmate.</p>

<p>Now the schema will include only items that one of it’s child roles include (in this case, just what todo_user has access to). If you have multiple roles for users (for instance, a regular user role and an admin role) the schema will include whatever the most privileged user has access to, but Postgres will still enforce restrictions if a less priviledged tries to make a request they aren’t allowed to make.</p>

<hr />

<p>If you are familiar with securing apis using manual checks from a traditional backend, enforcing your api with upfront declarations like this can be quit uncomfortable. But when you embrace it, I think you’ll find it’s actually quit secure and simple.</p>

<p><span class="side-note">
<strong>UPDATE:</strong> Thanks to Graphile maintainer <a href="https://twitter.com/Benjie">Benjie</a> for reaching out and pointing out some typos as well as suggestions on using RBAC instead of smart comments.
</span></p>]]></content><author><name></name></author><category term="PostGraphile" /><category term="GraphQL" /><category term="PostgreSQL" /><category term="Auth0" /><summary type="html"><![CDATA[PostGraphile is a really great tool for creating api backends very quickly primarily using PostgreSQL. However, it does require you to rethink how you make apis. This is especially true for authorization and authentication. But it certainly is possible (even easy) to create secure apis using Graphile.]]></summary></entry></feed>