<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Swiftui on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/swiftui/</link><description>Recent content in Swiftui on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Sat, 10 Dec 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/swiftui/index.xml" rel="self" type="application/rss+xml"/><item><title>Sharing is caring</title><link>https://blog.iankulin.com/sharing-is-caring/</link><pubDate>Sat, 10 Dec 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/sharing-is-caring/</guid><description>&lt;p&gt;Continuing on with the demo project from yesterday, in which we used the ImageRenderer class to turn a view into an image, today we want to let the user share it somehow.&lt;/p&gt;
&lt;p&gt;Typically, apps have a button using the square.and.arrow.up SF Symbol to share something from the current screen. It&amp;rsquo;s probably not an accident that it&amp;rsquo;s literally the first symbol in the app.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-12-05-at-9.23.33-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Pressing it generally opens the &amp;ldquo;share sheet&amp;rdquo; which has options for opening whatever is being shared in another app, printing it, saving it to photos, or whatever.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s our ticket app from a couple of days ago (the TicketView is unchanged). We&amp;rsquo;re still using ImageRenderer() to make the image version of the view in OnAppear(), but this time there&amp;rsquo;s a &amp;ldquo;sharelink&amp;rdquo;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; ticketView &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; TicketView&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; image&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Image&lt;span style="color:#eceff4"&gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ticketView
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; image &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; image &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ShareLink&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;item&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; image&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; preview&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; SharePreview&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;View&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt;image&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; image&lt;span style="color:#eceff4"&gt;))&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Label&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Share&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; systemImage&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;square.and.arrow.up&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;padding&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;onAppear &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; renderer &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; ImageRenderer&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;content&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; ticketView&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; uiImage &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; renderer&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;uiImage &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Image&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;uiImage&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; uiImage&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we click on the share link, it looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/simulator-screen-shot-iphone-14-pro-2022-12-05-at-21.10.20.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;The little thumbnail and the word &amp;ldquo;View&amp;rdquo; at the top of the share sheet is from the preview parameter in our call.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ShareLink(item: image, preview: SharePreview(&amp;#34;View&amp;#34;,image: image)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Label(&amp;#34;Share&amp;#34;, systemImage: &amp;#34;square.and.arrow.up&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since this is an image, perhaps you were expecting a &amp;ldquo;Save image&amp;rdquo; option? That&amp;rsquo;s the most likely thing we&amp;rsquo;d want to do with an image, but it&amp;rsquo;s not there. The reason is that saving an image to the users camera roll requires a specific permission. In the app settings, choose &amp;ldquo;Info&amp;rdquo; then right click on the entries and &amp;ldquo;Add Row&amp;rdquo;. Search for / add the key &amp;ldquo;Privacy - Photo Library Additions Usage Description&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-12-07-at-8.33.52-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;The text you add in the description is what the app will show to the user when the dialogue pops up asking the user if it&amp;rsquo;s okay to give this app the permission to save photos.&lt;/p&gt;
&lt;p&gt;If we go back and and try again to share the ticket, we&amp;rsquo;ve now got the options to &amp;ldquo;Save Image&amp;rdquo;. If we choose that for the first time, we&amp;rsquo;ll be asked to grant permission to this app.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-12-07-at-8.41.40-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;If the user chooses OK, then it will be saved to photos:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-12-07-at-8.42.04-pm.jpg" alt=""&gt;&lt;/p&gt;</description></item><item><title>ImageRenderer()</title><link>https://blog.iankulin.com/imagerenderer/</link><pubDate>Thu, 08 Dec 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/imagerenderer/</guid><description>&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/imagerenderer"&gt;ImageRenderer&lt;/a&gt;() is a SwiftUI class that creates an image from a view. You just initialize it with the view, then extract a cgImage (Core Graphics) or uiImage that can be cast to a SwiftUI Image.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll need a view to work with, so here it is; a crude version of my behaviour ticket.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;TicketView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ZStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Color&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;cyan&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;frame&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;width&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;300&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; height&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;350&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Fred Bloggs&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;font&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;largeTitle&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Putting rubbish in the bin&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Image&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;systemName&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;trash&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;foregroundColor&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;purple&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Green Faction&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;Date&lt;span style="color:#a3be8c"&gt;()&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;formatted&lt;span style="color:#a3be8c"&gt;())&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here it is, with a couple of buttons underneath:&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/simulator-screen-shot-iphone-14-pro-2022-12-05-at-19.56.18.png" width="209" alt=""&gt;
&lt;p&gt;I&amp;rsquo;ve assigned the view to a property of my content view, then displayed it along with the buttons. The first button saves the image to an @State and the second one displays it if it exists.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; ticketView &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; TicketView&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; image&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Image&lt;span style="color:#eceff4"&gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; showImage &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ticketView
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Save Image&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; renderer &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; ImageRenderer&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;content&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; ticketView&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; uiImage &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; renderer&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;uiImage &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Image&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;uiImage&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; uiImage&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}.&lt;/span&gt;padding&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Toggle image&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; withAnimation &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showImage&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;toggle&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; showImage &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; image &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; image &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;resizable&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;scaledToFit&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;padding&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we save the screen:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Button(&amp;#34;Save Image&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let renderer = ImageRenderer(content: ticketView)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; if let uiImage = renderer.uiImage {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image = Image(uiImage: uiImage)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}.padding()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then it can just be inserted in the view same as any bitmap image by hitting the Toggle Image button.&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/simulator-screen-shot-iphone-14-pro-2022-12-05-at-20.01.49.png" width="472" alt=""&gt;</description></item><item><title>SwiftUI provides</title><link>https://blog.iankulin.com/swiftui-provides/</link><pubDate>Wed, 07 Dec 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/swiftui-provides/</guid><description>&lt;img src="https://blog.iankulin.com/images/img_3476.png" width="284" alt=""&gt;
&lt;p&gt;A few hours after I speculated about pausing work on the tickets app because outputting the tickets was too far out of my expertise, a helpful instance of the &lt;a href="https://en.wikipedia.org/wiki/Frequency_illusion"&gt;Baader–Meinhof phenomenon&lt;/a&gt; threw up some help in the form of this tweet from &lt;a href="https://twitter.com/flowritescode"&gt;@FloWritesCode&lt;/a&gt;. It turns out this was an addition in iOS16 announced at WWDC that makes this straightforward.&lt;/p&gt;
&lt;p&gt;As soon as I googled around about it I also found good solutions that wrapped the old code to provide similar functionality. So that&amp;rsquo;s a lesson for me about not assuming something&amp;rsquo;s hard before I&amp;rsquo;ve spent some time investigating it. I took that lesson and applied it to rendering to a PDF, and of course, @twostraws &lt;a href="https://www.hackingwithswift.com/quick-start/swiftui/how-to-render-a-swiftui-view-to-a-pdf"&gt;has a code example&lt;/a&gt; for that from three days ago!&lt;/p&gt;
&lt;p&gt;Obviously I&amp;rsquo;m going to have some fiddling around to lay it out and so on, and I still need to figure out the share behaviour (which I&amp;rsquo;m expecting to be straightforward) but it feels like SwiftUI is just going to help me express my intents in code. I&amp;rsquo;m really enjoying this language, framework and community!&lt;/p&gt;</description></item><item><title>Something weird 'append</title><link>https://blog.iankulin.com/something-weird-about-append/</link><pubDate>Mon, 21 Nov 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/something-weird-about-append/</guid><description>&lt;p&gt;I&amp;rsquo;m noodling around making sure I understand how Core Data works. Thought I&amp;rsquo;d start with a master/detail app with an array of structs, then replicate it in a Core Data implementation. I&amp;rsquo;m using an array of this struct for my data:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;struct Garden &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; id &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; UUID&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; address &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; plants&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;Plant&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And I thought this code to load up some sample data was pretty sweet.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#bf616a"&gt;Button&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Sample Data&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; someGarden &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; Garden&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;White Lodge&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;address &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;72 Anderson Street, &lt;/span&gt;&lt;span style="color:#ebcb8b"&gt;\n&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;Rothwell QLD 4022&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gardens&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;append&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;someGarden&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Gordon Terrace&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;address &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;95 Learmouth Street&lt;/span&gt;&lt;span style="color:#ebcb8b"&gt;\n&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;Tahara Vic 3301&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gardens&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;append&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;someGarden&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Powlett Cottage&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;address &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;11 Bayfield Street&lt;/span&gt;&lt;span style="color:#ebcb8b"&gt;\n&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;White Beach Tas 7184&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gardens&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;append&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;someGarden&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Adams Garden&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;address &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;71 Swanston Street&lt;/span&gt;&lt;span style="color:#ebcb8b"&gt;\n&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;Kanya Vic 3381&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gardens&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;append&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;someGarden&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But, no. This is what happens:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-11-18-at-7.00.49-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;The reason I thought this was okay was that structs are value types in Swift. When I pass a struct in a function or method, it&amp;rsquo;s actually a copy at the other end. I could prove this by mutating the array copy and checking the original.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#bf616a"&gt;Button&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Sample Data&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; someGarden &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; Garden&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;White Lodge&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someGarden&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;address &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;72 Anderson Street, &lt;/span&gt;&lt;span style="color:#ebcb8b"&gt;\n&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;Rothwell QLD 4022&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gardens&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;append&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;someGarden&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gardens&lt;span style="color:#eceff4"&gt;[&lt;/span&gt;&lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;]&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Mutated&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;print&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Array: \(gardens[0].name)&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;print&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Local: \(someGarden.name)&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Prints:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Array: Mutated
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Local: White Lodge
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So clearly I do understand structs/reference types correctly. They are different structs. Also, if structs were somehow reference types, they should have all had the data from the last garden, not the first one.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;d might be thinking (as I did), perhaps the error is in the view code. That&amp;rsquo;s easily checked. I forced the second one by creating a new struct, it works correctly and is in the correct position so clearly the view code is working.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-11-18-at-7.18.59-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;The code mutating someGarden is getting run, so it&amp;rsquo;s not like the compiler&amp;rsquo;s eliminated those lines somehow. Also it is getting the number of entries correct - they are just somehow linking back to the first entry.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-11-18-at-7.31.44-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;It seems impossible! This is always the way with good bugs.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m almost tempted to ask for help.&lt;/p&gt;
&lt;p&gt;Before one does that, it&amp;rsquo;s always a good idea to boil things down to the essence of the problem. Playgrounds is an excellent tool for such an endeavor. I just need the simplest version of an array of structs. Here&amp;rsquo;s what I came up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import Foundation
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;struct Animal &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; name&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#bf616a"&gt;String&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; animals&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;Animal&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; animal1 &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; Animal&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;animal1&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Cat&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;animals&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;append&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;animal1&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;animal1&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Dog&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;animals&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;append&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;animal1&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;print&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;animals&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Produces the output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Page_Contents.Animal(name: &amp;#34;Cat&amp;#34;), Page_Contents.Animal(name: &amp;#34;Dog&amp;#34;)]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Grrr. It turns out Swift works perfectly.&lt;/p&gt;
&lt;p&gt;In the screenshot above, where I&amp;rsquo;ve broken on line 52, I couldn&amp;rsquo;t actually see how to inspect the gardens array. Perhaps I&amp;rsquo;d be better with the classic print() debugging instead.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[DataDemo.Garden(id: 0725A8FC-D410-4A2C-B925-C34340F25B05, name: &amp;#34;White Lodge&amp;#34;, address: &amp;#34;72 Anderson Street, \nRothwell QLD 4022&amp;#34;, plants: []), DataDemo.Garden(id: CF30FA66-A2F2-44FB-A6E0-A22E93492578, name: &amp;#34;Gordon Terrace&amp;#34;, address: &amp;#34;95 Learmouth Street\nTahara Vic 3301&amp;#34;, plants: []), DataDemo.Garden(id: 0725A8FC-D410-4A2C-B925-C34340F25B05, name: &amp;#34;Powlett Cottage&amp;#34;, address: &amp;#34;11 Bayfield Street\nWhite Beach Tas 7184&amp;#34;, plants: []), DataDemo.Garden(id: 0725A8FC-D410-4A2C-B925-C34340F25B05, name: &amp;#34;Adams Garden&amp;#34;, address: &amp;#34;71 Swanston Street\nKanya Vic 3381&amp;#34;, plants: [])]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Okay, so the bug I&amp;rsquo;ve been trying to fix, is not the bug at all. The array is working exactly as intended, it&amp;rsquo;s my view that&amp;rsquo;s the issue somehow.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-11-18-at-7.18.59-pm-1.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;And it&amp;rsquo;s not just a general problem with the view - it&amp;rsquo;s a problem with the view that goes away if I create a new Garden struct instead of recycling one&amp;hellip; Simultaneously I have these two thoughts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When I create a new struct, it gets a fresh UUID&lt;/li&gt;
&lt;li&gt;What was that console spam message I&amp;rsquo;ve been ignoring? Oh yeah, it was &lt;code&gt;ForEach, UUID, HStack&amp;gt;&amp;gt;: the ID 0725A8FC-D410-4A2C-B925-C34340F25B05 occurs multiple times within the collection, this will give undefined results!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can guess the rest, my ForEach is using the UUID:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;List {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach(gardens, id: \.id) {garden in
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(garden.name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(garden.address)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If I fix the ids:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-11-18-at-8.16.17-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s how you waste a hour fixing a bug the framework warned you about in the first five minutes.&lt;/p&gt;</description></item><item><title>Bookworm Challenges</title><link>https://blog.iankulin.com/bookworm-challenges/</link><pubDate>Sun, 06 Nov 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/bookworm-challenges/</guid><description>&lt;p&gt;Another set of challenges for a &lt;a href="https://www.hackingwithswift.com/100/swiftui"&gt;#100DaysofSwiftUI&lt;/a&gt; tutorial app. Project 11 was a book tracking app - the big new thing was using CoreData. Here&amp;rsquo;s the &lt;a href="https://www.hackingwithswift.com/books/ios-swiftui/bookworm-wrap-up"&gt;challenges for it&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Right now it’s possible to select no title, author, or genre for books, which causes a problem for the detail view. Please fix this, either by forcing defaults, validating the form, or showing a default picture for unknown genres – you can choose.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The genre is already forced since it uses a picker, but I added a default (plain grey) image to deal with the situation that there&amp;rsquo;s no data for it in a record. It doesn&amp;rsquo;t make sense to provide defaults for the title or author, but some validation to ensure those fields are not empty is worthwhile.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Section {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button(&amp;#34;Save&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let newBook = Book(context: moc)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newBook.id = UUID()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newBook.title = title
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newBook.author = author
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newBook.rating = Int16(rating)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newBook.genre = genre
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newBook.review = review
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; newBook.date = Date()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; try? moc.save()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dismiss()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.disabled(title.isEmpty || author.isEmpty || genre.isEmpty)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://github.com/IanKulin/Bookworm/commit/989942b228f96540f1f46e04e91e4816f9072a38"&gt;&lt;img src="https://blog.iankulin.com/images/github-mark-120px-plus.png" width="34" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Modify &lt;code&gt;ContentView&lt;/code&gt; so that books rated as 1 star are highlighted somehow, such as having their name shown in red.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Straightforward, with use of the ternary operator.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-11-04-at-6.17.12-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/Bookworm/commit/cc81d29a799e7bb97e9813c4ad17e66a64361e6a"&gt;&lt;img src="https://blog.iankulin.com/images/github-mark-120px-plus-1.png" width="35" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Add a new “date” attribute to the Book entity, assigning &lt;code&gt;Date.now&lt;/code&gt; to it so it gets the current date and time, then format that nicely somewhere in &lt;code&gt;DetailView&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Add the date field to the datamodel&lt;/li&gt;
&lt;li&gt;Set it to current date in the Save of the AddView&lt;/li&gt;
&lt;li&gt;Showing it in the Detail View was a little more interesting - adding a test to ensure it was in the database - since we&amp;rsquo;ve now got two versions of these records.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Text(book.author ?? &amp;#34;Unknown author&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .font(.title)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .foregroundColor(.secondary)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let date = book.date ?? Date()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Text(date.formatted(.dateTime.day().month().year()))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .foregroundColor(.secondary)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .opacity(date == book.date ? 1 : 0)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Text(book.review ?? &amp;#34;No review&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .padding()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://github.com/IanKulin/Bookworm/commit/9ec21d4d827b1ae1799c34d65b9d2366e5c4ce36"&gt;&lt;img src="https://blog.iankulin.com/images/github-mark-120px-plus-2.png" width="36" alt=""&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>@Binding - data between views</title><link>https://blog.iankulin.com/binding-data-between-views/</link><pubDate>Sat, 05 Nov 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/binding-data-between-views/</guid><description>&lt;p&gt;In C world, if we want to pass a parameter down into a functional call, and allow the receiving function to change it&amp;rsquo;s value, we&amp;rsquo;d pass a pointer to the variable. Something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5e81ac;font-style:italic"&gt;#include&lt;/span&gt; &lt;span style="color:#5e81ac;font-style:italic"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style="color:#5e81ac;font-style:italic"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5e81ac;font-style:italic"&gt;#include&lt;/span&gt; &lt;span style="color:#5e81ac;font-style:italic"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span style="color:#5e81ac;font-style:italic"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;void&lt;/span&gt; &lt;span style="color:#88c0d0"&gt;increment&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;int&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;*&lt;/span&gt; b&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;*&lt;/span&gt;b&lt;span style="color:#81a1c1"&gt;=*&lt;/span&gt;b&lt;span style="color:#81a1c1"&gt;+&lt;/span&gt;&lt;span style="color:#b48ead"&gt;1&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;int&lt;/span&gt; &lt;span style="color:#88c0d0"&gt;main&lt;/span&gt;&lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;int&lt;/span&gt; a &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;5&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#88c0d0"&gt;increment&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;amp;&lt;/span&gt;a&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#88c0d0"&gt;printf&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;%d&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; a&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// prints &amp;#39;6&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For youngsters, what&amp;rsquo;s happening is that we&amp;rsquo;ve set the value of a to 5, then passed the memory &lt;em&gt;address&lt;/em&gt; of a into the increment() function. That&amp;rsquo;s what the @a means.&lt;/p&gt;
&lt;p&gt;In the increment function we&amp;rsquo;re expecting an *int - ie the address of an int. Then we &lt;em&gt;dereference&lt;/em&gt; it with the asterisks to operate on the value stored in the address.&lt;/p&gt;
&lt;p&gt;The reason I mention all this ancient lore is because it&amp;rsquo;s what I imagine is happening with the @Binding in a subview - although I&amp;rsquo;m also sure the story underneath is more complex than that.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;import&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;SwiftUI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; someInt &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SubView&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;someValue&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#bf616a"&gt;$&lt;/span&gt;someInt&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Content view &lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;someInt&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;SubView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;Binding &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; someValue&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Subview &lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;someValue&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Inc&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someValue &lt;span style="color:#81a1c1"&gt;+=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}.&lt;/span&gt;padding&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s a couple of things to note here. In the SubView struct, we&amp;rsquo;ve used the @Binding property wrapper, and in the ContentView where we&amp;rsquo;ve passed the state variable from, we&amp;rsquo;ve marked it with the $ so indicate we know its a binding variable that can be mutated by whatever it&amp;rsquo;s being passed to.&lt;/p&gt;
&lt;p&gt;If we leave the $ off, the compiler will point out our error because it knows it&amp;rsquo;s bound in the other view.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-11-03-at-9.17.35-pm.png" alt=""&gt;&lt;/p&gt;</description></item><item><title>Cupcake Corner challenges</title><link>https://blog.iankulin.com/cupcake-corner-challenges/</link><pubDate>Wed, 02 Nov 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/cupcake-corner-challenges/</guid><description>&lt;p&gt;&lt;a href="https://www.hackingwithswift.com/books/ios-swiftui/cupcake-corner-wrap-up"&gt;Day 52&lt;/a&gt; of &lt;a href="https://www.hackingwithswift.com/100/swiftui"&gt;#100Days&lt;/a&gt; was the challenges to the Cupcake Corner app - an app that allows you to build a one-row order, encode it as JSON and submit it to an API with a URLSession. To allow the order to be passed around, it&amp;rsquo;s an @ObservedObject which meant that a few extra hoops needed to be jumped through to make it Codable.&lt;/p&gt;
&lt;h4 id="1-whitespace-validation"&gt;1) Whitespace validation&lt;/h4&gt;
&lt;p&gt;The tutorial app validates the order address by checking that each field is not empty, but it can be fooled by just entering some spaces. The first challenge was to fix that.&lt;/p&gt;
&lt;p&gt;The tutorial version of the app accomplished the checking with a computed property in the Order struct - which is a good place for it. Here it is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; hasValidAddress&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Bool &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#81a1c1"&gt;||&lt;/span&gt; streetAddress&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#81a1c1"&gt;||&lt;/span&gt; city&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#81a1c1"&gt;||&lt;/span&gt; zip&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s no .isEmptyIfYouIgnoreWhiteSpace method, so I did this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; hasValidAddress&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Bool &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let trimmedName &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;trimmingCharacters&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let trimmedStreetAddress &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; streetAddress&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;trimmingCharacters&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let trimmedCity &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; city&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;trimmingCharacters&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let trimmedZip &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; zip&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;trimmingCharacters&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; trimmedName&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#81a1c1"&gt;||&lt;/span&gt; trimmedStreetAddress&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#81a1c1"&gt;||&lt;/span&gt; trimmedCity&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#81a1c1"&gt;||&lt;/span&gt; trimmedZip&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isEmpty &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://github.com/IanKulin/CupcakeCorner/commit/2bd1247d268c41b8cb83c07af55ca4bb6f291e81#diff-5d943085c2460b6ea685f488eec227df2a6fec0aa2155bc7ffa85d457604c91e"&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="2-show-an-alert-if-the-post-fails"&gt;2) Show an alert if the POST fails&lt;/h4&gt;
&lt;p&gt;In the tute version, this is just a print statement. This challenge just involves adding a second alert to the checkout view. &lt;a href="https://github.com/IanKulin/CupcakeCorner/commit/cfb2347c3e48fd68ac47b4f2153cc1faa01149ee"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="3-change-order-to-struct"&gt;3) Change order to struct&lt;/h4&gt;
&lt;p&gt;This is a bit more complicated. A reason for preferring a struct is that all of the extra work we did to make the class Codable is eliminated. We need the thing being passed around to be an object because we want a reference type that can be mutated inside the view hierarchy, and because we want it to be an @Observed Object. The change proposed here is sort of the best of both worlds - have the object, but it&amp;rsquo;s sole property is the struct.&lt;/p&gt;
&lt;p&gt;I created a wrapper class, with the struct as an @Published var.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;class&lt;/span&gt; Wrapper&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; ObservableObject &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#bf616a"&gt;@&lt;/span&gt;Published &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; order &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; Order&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; deinit &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I changed the Order class to a struct, removed @Published from all the properties and deleted the encode/decode code. Then in all the views, I had to go through and fix the names. I did not love the less readable names I was creating; for example &lt;code&gt;wrapped.order.streetAddress&lt;/code&gt; instead of just &lt;code&gt;order.streetAddress&lt;/code&gt;. But that all worked.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/CupcakeCorner/commit/c8559232b681f527c72ee9b2d89bfba997894d84#diff-5d943085c2460b6ea685f488eec227df2a6fec0aa2155bc7ffa85d457604c91e"&gt;source&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Refreshing SwiftUI Views</title><link>https://blog.iankulin.com/refreshing-swiftui-views/</link><pubDate>Sun, 23 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/refreshing-swiftui-views/</guid><description>&lt;p&gt;SwiftUI does some property wrapper magic to (very efficiently) refresh your views, but what if you want to force a refresh for some reason? Here&amp;rsquo;s the techniques I&amp;rsquo;m currently using to do that.&lt;/p&gt;
&lt;p&gt;The tricks are below, but just so you can see them in context, here&amp;rsquo;s the sample app we&amp;rsquo;re working on. It&amp;rsquo;s a list of cars so you can keep track of how many of each kind you own. Here&amp;rsquo;s our data:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;Car&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Identifiable&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; Codable&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;Equatable&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; id &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; UUID&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; number &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;Cars&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; ObservableObject &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;Published &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; items &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;Car&lt;span style="color:#eceff4"&gt;]()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;init&lt;/span&gt;&lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; car1 &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Station wagon&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; car2 &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Sedan&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; car3 &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Hatchback&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; items &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;car1&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; car2&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; car3&lt;span style="color:#eceff4"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;func&lt;/span&gt; &lt;span style="color:#88c0d0"&gt;increment&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;_&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;Bool&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; index &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; items&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;firstIndex&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;of&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; index &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; index &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; items&lt;span style="color:#eceff4"&gt;[&lt;/span&gt;index&lt;span style="color:#eceff4"&gt;].&lt;/span&gt;number &lt;span style="color:#81a1c1"&gt;+=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;deinit&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The app is a list inside a navigation stack. The view for each car is split out into a subview:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;StateObject &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Cars&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; NavigationView &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;items&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; car &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CarView&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;navigationTitle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Cars&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;CarView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Car
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Cars
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;Int&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;random&lt;span style="color:#a3be8c"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10.&lt;/span&gt;&lt;span style="color:#eceff4"&gt;..&lt;/span&gt;&lt;span style="color:#b48ead"&gt;99&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;))&lt;/span&gt;&lt;span style="color:#a3be8c"&gt; &amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VStack&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;alignment&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;leading&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;font&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;headline&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34; &lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;number&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt; &amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;!&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;increment&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;print&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Unexpected error - car not found:&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;model&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; label&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Image&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;systemName&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The random Int is just so we can get a visual indication that our view has refreshed.&lt;/p&gt;
&lt;h4 id="trick-1---change-a-value"&gt;Trick 1 - change a value&lt;/h4&gt;
&lt;p&gt;All the other tricks rely on this trick. SwiftUI reacts to any @State property changing, so to force a change, there just needs to be a @State property we change. I add a:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#bf616a"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; refresh &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to my ContentView. Whenever this changes, ie refresh.toggle() the view will be redrawn. Of course we don&amp;rsquo;t want just the ContentView redrawn, but the subview as well, so it needs to be passed into the subview. We don&amp;rsquo;t do anything with it there, just pass it in.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;StateObject &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Cars&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; refresh &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; NavigationView &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;items&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; intItem &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CarView&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; intItem&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;navigationTitle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Cars&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;CarView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Car
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Cars
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;Bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;Int&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;random&lt;span style="color:#a3be8c"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10.&lt;/span&gt;&lt;span style="color:#eceff4"&gt;..&lt;/span&gt;&lt;span style="color:#b48ead"&gt;99&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;))&lt;/span&gt;&lt;span style="color:#a3be8c"&gt; &amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VStack&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;alignment&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;leading&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;font&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;headline&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34; &lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;number&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt; &amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;!&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;increment&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;print&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Unexpected error - car not found:&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;model&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; label&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Image&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;systemName&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="trick-2---refreshable"&gt;Trick 2 - .refreshable()&lt;/h4&gt;
&lt;p&gt;That&amp;rsquo;s the infrastructure in place, so now we can use it. We could add a button that the user could click to refresh, but 2022 users have been trained to pull down on views to make them refresh, so let&amp;rsquo;s do that. Just add a &lt;code&gt;.refreshable()&lt;/code&gt; modifier to our list, then toggle refresh in the closure.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;StateObject &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Cars&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; refresh &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; NavigationView &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;items&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; intItem &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CarView&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; intItem&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;navigationTitle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Cars&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;refreshable &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; refresh&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;toggle&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when the user pulls the list down, the familiar wait wheel appears, the view, and the car subviews, all redraw.&lt;/p&gt;
&lt;h4 id="trick-3---onchangeof-scene"&gt;Trick 3 - OnChange(of: scene)&lt;/h4&gt;
&lt;p&gt;In my Habit app, the activities are going to appear with a temporal description of when they should be done, like &amp;ldquo;next week&amp;rdquo;, &amp;ldquo;in a few minutes&amp;rdquo;, or &amp;ldquo;tomorrow&amp;rdquo;. Those will be created from the date/time the activity was last done, how often the activity should be done, and the current date/time. So if our app has been in the background and we open it up, we want fresh calculations. We can do that by detecting a &lt;code&gt;scenePhase&lt;/code&gt; change to &lt;code&gt;.active&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We have to declare a variable for it, and then add the .onChange to the view:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;StateObject &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Cars&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; refresh &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;Environment&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#bf616a"&gt;\&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;scenePhase&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; scenePhase
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; NavigationView &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;items&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; intItem &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CarView&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; intItem&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;navigationTitle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Cars&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;onChange&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;of&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; scenePhase&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; newPhase &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; newPhase &lt;span style="color:#eceff4"&gt;==&lt;/span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;active &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; refresh&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;toggle&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="trick-4---a-timer"&gt;Trick 4 - a timer&lt;/h4&gt;
&lt;p&gt;Even if our user leaves the app open and foregrounded, eventually the descriptions will be out of date, so another thing we could do is use a timer. We have to add a timer property, and add an .onReceive() to the view. The timer interval in seconds is set when the timer is created. The example below is going to trigger every second.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;StateObject &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Cars&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;State &lt;span style="color:#81a1c1;font-weight:bold"&gt;private&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; refresh &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; timer &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Timer&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;publish&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;every&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; on&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;main&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;common&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;autoconnect&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; NavigationView &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;items&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; intItem &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CarView&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; intItem&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; refresh&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;navigationTitle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Cars&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;onReceive&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;timer&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; perform&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;_&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; refresh&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;toggle&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>You need to enjoy puzzles</title><link>https://blog.iankulin.com/you-need-to-enjoy-puzzles/</link><pubDate>Sat, 22 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/you-need-to-enjoy-puzzles/</guid><description>&lt;p&gt;I&amp;rsquo;m writing the Habits &lt;a href="https://blog.iankulin.com/list-apps/"&gt;list based app&lt;/a&gt; from #100Days and had a working MVP, then for some reason, decided to refactor by changing the subview I&amp;rsquo;d written as a function, into a struct. Some time later, I discovered that my list items were not updating correctly, so detective time.&lt;/p&gt;
&lt;p&gt;I talked a little bit about the architecture yesterday - the item is a struct, and there&amp;rsquo;s a class containing an array of the items. Something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;Car&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Identifiable&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; Codable&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;Equatable&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; id &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; UUID&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; number &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;Cars&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; ObservableObject &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;Published &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; items &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;Car&lt;span style="color:#eceff4"&gt;]()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;init&lt;/span&gt;&lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; car1 &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Station wagon&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; car2 &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Sedan&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; car3 &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Hatchback&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; items &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;car1&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; car2&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; car3&lt;span style="color:#eceff4"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;func&lt;/span&gt; &lt;span style="color:#88c0d0"&gt;increment&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;_&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;Bool&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; index &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; items&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;firstIndex&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;of&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; index &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; index &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; items&lt;span style="color:#eceff4"&gt;[&lt;/span&gt;index&lt;span style="color:#eceff4"&gt;].&lt;/span&gt;number &lt;span style="color:#81a1c1"&gt;+=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;deinit&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then the ContentView is a NavigationView with a List. To build the list, I ForEach on the array inside the object, calling a subview on each one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;ContentView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;@&lt;/span&gt;StateObject &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars &lt;span style="color:#eceff4"&gt;=&lt;/span&gt; Cars&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; NavigationView &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;items&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; intItem &lt;span style="color:#81a1c1;font-weight:bold"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CarView&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; intItem&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;navigationTitle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Cars&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;struct&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;CarView&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Car
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; cars&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Cars
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VStack&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;alignment&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;leading&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;model&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;font&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;headline&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34; &lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;number&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt; &amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;!&lt;/span&gt;cars&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;increment&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;print&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Unexpected error - car not found:&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;\(&lt;/span&gt;car&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;model&lt;span style="color:#a3be8c"&gt;)&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; label&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Image&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;systemName&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When the user clicks on the button in a list item, the underlying data is amended somehow - in this pared down example the number of that type of car is incremented. Since my &amp;ldquo;car&amp;rdquo; is a value type there&amp;rsquo;s no point incrementing its number, so instead I call the .increment method on the cars object that is holding my array.&lt;/p&gt;
&lt;p&gt;Then, in that method, I have to search for the car the user intends to change, then change it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;func&lt;/span&gt; increment&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;_ car&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;-&amp;gt;&lt;/span&gt; Bool &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let index &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; items&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;firstIndex&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;of&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; car&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; let index &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; index &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; items&lt;span style="color:#eceff4"&gt;[&lt;/span&gt;index&lt;span style="color:#eceff4"&gt;]&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;number &lt;span style="color:#81a1c1"&gt;+=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The situation of not being able to find the car should never occur, but out of longstanding habit I check for it and log an error to the console. This then happens repeatedly&amp;hellip;&lt;/p&gt;
&lt;p&gt;After some print statement debugging, I got it into my head that I&amp;rsquo;d caused this by changing the subview from a function (which had worked) to a struct (which was not). I decided it was a value type / reference type problem cause between the difference between these two subview approaches. Since that seems like an interesting blog post topic, I spent a long time distilling the code down to a simple looking example of this phenomena.&lt;/p&gt;
&lt;p&gt;Then tried it, it worked correctly.&lt;/p&gt;
&lt;p&gt;Then changed the function to the struct, it worked correctly.&lt;/p&gt;
&lt;p&gt;Went back to the original app, changed the subview function to a struct, it worked correctly.&lt;/p&gt;
&lt;p&gt;So I don&amp;rsquo;t know what caused my original problem, or what changed to fix it. In theory I could go back through the commits (they have helpful names like &amp;ldquo;general progress&amp;rdquo; and &amp;ldquo;progress save&amp;rdquo;) and find it.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;ve already spent way longer on this app than I should have, and all I have to show for it is this post about what a doofus I am.&lt;/p&gt;</description></item><item><title>List Apps</title><link>https://blog.iankulin.com/list-apps/</link><pubDate>Thu, 20 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/list-apps/</guid><description>&lt;p&gt;When I was first programming professionally, it wasn&amp;rsquo;t long before I noticed that there were patterns to the sort of bread-and-butter things I was writing most times - the majority of the small business applications I wrote tracked several entities; for each entity there needed to be add/edit/delete screens, there would be some business rules around those things, and some reports and search functionality.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.hackingwithswift.com/guide/ios-swiftui/4/3/challenge"&gt;Day 47 Milestone app&lt;/a&gt; is for tracking habits - presented in a list; you need to be able to add and delete them, view details and do some business logic on them. Not only does this sound a lot like the earlier expense tracking app, but also not that different to the app idea I&amp;rsquo;ve got for tracking when I charge each of the rechargeable batteries in my house.&lt;/p&gt;
&lt;p&gt;Eventually these list type apps written in SwiftUI will need a better (perhaps MVVM) architecture, but at the simple end, it seems these apps are going to be some variation on this recipe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The item is going to be a struct with an id&lt;/li&gt;
&lt;li&gt;It will conform to Identifiable and Codable&lt;/li&gt;
&lt;li&gt;The collection of items will be a class that inherits from ObservableObject&lt;/li&gt;
&lt;li&gt;The collection property will be @Published&lt;/li&gt;
&lt;li&gt;The collection property will have an init() that reads the items from somewhere and a didset() that writes them&lt;/li&gt;
&lt;li&gt;The instance of that collection class will be an @StateObject&lt;/li&gt;
&lt;li&gt;The items will be shown as a List inside a NavigationView&lt;/li&gt;
&lt;li&gt;It will probably have a toolbar with a control to add items&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Patterns in development like this are convenient - they lead to high quality programs developed quickly, and being compliant with the &lt;a href="https://developer.apple.com/design/human-interface-guidelines/guidelines/overview/"&gt;Apple HIG&lt;/a&gt; means all the behaviours will be familiar to users as well as being accessible.&lt;/p&gt;
&lt;p&gt;The danger is that they look and feel like every other list type app, so developers need to think carefully about what all the common use cases are, as well as appealing design, to ensure the users&amp;rsquo; needs are being addressed and the application can justify it&amp;rsquo;s existence alongside all the other offerings.&lt;/p&gt;</description></item><item><title>Moonshot Feedback</title><link>https://blog.iankulin.com/moonshot-feedback/</link><pubDate>Sat, 15 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/moonshot-feedback/</guid><description>&lt;p&gt;I&amp;rsquo;ve watched Paul&amp;rsquo;s solution to the &lt;a href="https://www.hackingwithswift.com/books/ios-swiftui/moonshot-wrap-up"&gt;Moonshot challenges&lt;/a&gt; (the solutions are one of the perks of being a Hacking With Swift subscriber). When I&amp;rsquo;m solo learning like this its one of the few ways I can get any feedback on my coding, so I highly value it, and usually write one of these posts as a way to ensure I reflect on it.&lt;/p&gt;
&lt;p&gt;The second challenge was to pull out a couple of sub-views, Paul had used a struct as I had, but put them in their own files. I think that&amp;rsquo;s good practice if those sections are going to be used from other views, otherwise I like them in the the file with the view they&amp;rsquo;re a part of. I guess if you make it a habit pull them out into files, you&amp;rsquo;d look there for them so it would not be a drama, but XCode is pretty handy for finding what you want in a reasonable size file, so that&amp;rsquo;s not a big consideration.&lt;/p&gt;
&lt;p&gt;The challenge I was really interested to see was his use of a List for the &amp;ldquo;list&amp;rdquo; version of the main view. Although his list looked a bit nicer when he&amp;rsquo;d finished, that&amp;rsquo;s more of a comment on the effort I put into the design (minimal - I just made a list version of the grid style). The List does detect that it&amp;rsquo;s inside a NavigationView and has those &amp;gt; signs at the end of each row - so there&amp;rsquo;s a visual prompt to the user that these are tappable for more. In my version, I&amp;rsquo;d just kept the existing scroll view, deleted the grid and used HStacks to build out the view for each mission.&lt;/p&gt;
&lt;p&gt;Lists do have a heap of features - checkboxes, multiple selection, pull down for refresh, left slide for delete and all sorts of other goodies. However, none of those are needed for this app, so in that respect my solution is fine.&lt;/p&gt;
&lt;p&gt;There is however, a generally important reason for using the standard iOS controls in the way they are intended - accessibility. There&amp;rsquo;s a good example of that a few minutes later when Paul adds his toolbar to the NavigationView. Here&amp;rsquo;s mine - succinct, nice use of the ternary operator:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;toolbar &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#bf616a"&gt;Image&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;systemName&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; showingList &lt;span style="color:#bf616a"&gt;?&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;square.grid.2x2&amp;#34;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;list.bullet&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;onTapGesture &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showingList&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;toggle&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s Paul&amp;rsquo;s:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt; toolbar &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#bf616a"&gt;Button&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showingGrid&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;toggle&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; label&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; showingGrid
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#bf616a"&gt;Label&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Show as table&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; systemImage&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;list.dash&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#bf616a"&gt;Label&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Show as grid&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; systemImage&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;square.grid.2x2&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Essentially the same, except he&amp;rsquo;s used a Button rather than an Image with an .onTapGesture. You may ask why that matters - the answer is that if you were blind and using the iOS screen reader it would matter a lot. With Paul&amp;rsquo;s version the reader would know it was a button - something for collecting user input, AND it would read out the purpose contained in the label.&lt;/p&gt;
&lt;p&gt;Apple deserve credit for the effort and thought put into their accessibility features, and as developers we get a lot of that functionality for free, or at least very cheaply - but only if we do things the Apple way with standard controls.&lt;/p&gt;</description></item><item><title>CodeTrimmer - First MacOS App</title><link>https://blog.iankulin.com/codetrimmer-first-macos-app/</link><pubDate>Thu, 06 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/codetrimmer-first-macos-app/</guid><description>&lt;p&gt;I was listening to the StackTrace app this morning (&lt;a href="https://stacktracepodcast.fm/episodes/169/"&gt;episode 169 - &amp;ldquo;Choosing What Bugs to Ship&amp;rdquo;&lt;/a&gt;) and one of the ideas discussed was taking the time to automate some of your development processes, partially to save time, but also because if you make a process simple and quick, you&amp;rsquo;ll be more likely to do it multiple times to improve quality.&lt;/p&gt;
&lt;p&gt;Coincidentally, I&amp;rsquo;d been thinking about how often I paste some code from Xcode in order to display it in one of these blog posts. If it&amp;rsquo;s from the middle of a method, it will generally be indented a long way in, and there&amp;rsquo;s no point in displaying it like that (especially for a mobile reader) so I usually manually delete a heap of spaces from each line to left align it whilst keeping the needed indentation.&lt;/p&gt;
&lt;p&gt;Sounds like a job for a tiny MacOS app - my first! Here it is in action. You copy your code from Xcode, and paste it into the app:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-10-03-at-11.43.38-am.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Then press the &amp;ldquo;Strip Spaces&amp;rdquo; button to get:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-10-03-at-11.43.43-am.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve got a few little niceties to add/tidy up, but it was a breeze converting my SwiftUI iOS development skills to MacOS - literally ticking a box.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/CodeTrimmer/blob/f0fc616bc1d334f7d5268f5231630940cd14d57b/CodeTrimmer/ContentView.swift"&gt;Source&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Design Challenge</title><link>https://blog.iankulin.com/design-challenge/</link><pubDate>Wed, 05 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/design-challenge/</guid><description>&lt;p&gt;So, I&amp;rsquo;ve been working on translating the &lt;a href="https://blog.iankulin.com/design-help/"&gt;UI design&lt;/a&gt; created by the external designer into SwiftUI, and have done all of the easy bits:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-10-03-at-8.19.43-am.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;The rounded rectangles for things like the question display/number input are just ZStacks of roundedrects filled, then stroked:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ZStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RoundedRectangle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cornerRadius&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;fill&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;white&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;padding&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;horizontal&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RoundedRectangle&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;cornerRadius&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;stroke&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;black&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; lineWidth&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;2&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;padding&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;horizontal&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;questionText&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;font&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;title&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;fontWeight&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;heavy&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;calculatorDisplay&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;font&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;title&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;fontWeight&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;heavy&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;foregroundColor&lt;span style="color:#eceff4"&gt;(.&lt;/span&gt;blue&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;frame&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;maxWidth&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;350&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;offset&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;y&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;15&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Something I have learned in the process is the .offset modifer. This is what&amp;rsquo;s used to move a view from where SwiftUI would have placed it, and is what I&amp;rsquo;ve done to create that overlapped style where the question display/number input is sitting halfway over the bottom of the blue rounded rectangle. This is in the last line of the code above: &lt;code&gt;.offset(y: 15)&lt;/code&gt; This is moving the whole ZStack down by 15. A trick to watch with this is that since you&amp;rsquo;ve messed with SwiftUI&amp;rsquo;s arrangement, it doesn&amp;rsquo;t then shuffle everything else around this - you need to manually deal with making some space below it.&lt;/p&gt;
&lt;p&gt;The jobs still to do on this view is the customisation of the picker, which I think is going to have to be writing a picker from scratch (or finding the source for the library picker and altering it), and the even more custom combined picker/progress indicator.&lt;/p&gt;</description></item><item><title>Times Tables -Day 35 Challenge</title><link>https://blog.iankulin.com/times-tables-day-35-challenge/</link><pubDate>Sat, 01 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/times-tables-day-35-challenge/</guid><description>&lt;p&gt;The challenge for &lt;a href="https://www.hackingwithswift.com/guide/ios-swiftui/3/3/challenge"&gt;Day 35&lt;/a&gt; of &lt;a href="https://www.hackingwithswift.com/100/swiftui"&gt;100 Days of Swift&lt;/a&gt; UI was to create a simple times tables drilling app. I&amp;rsquo;ve met all the requirements, so I&amp;rsquo;ll move on, but I am struck by how ugly it is. Making better looking apps needs to be added to my goals. Especially since this app is intended to appeal to children, and is at the end of a few lessons on animation, this is definitely a weakness of mine at the moment.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/ui-copy.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I enjoyed this challenge, it had a few interesting aspects. One was the number keypad for the user to enter, and the other one was creating the dialogue shown to the user at the end of a round with the statistics. I really don&amp;rsquo;t love the iOS modal dialogue boxes, so I ZStacked a rounded rectangle with some views on top of it, and controlled the visibility with .opacity. I wasn&amp;rsquo;t sure what would happen to the user OnTap events - would they go through? The answer is that if you make it very see through they go through to the elements underneath, but if it&amp;rsquo;s reasonably solid, they stay there.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/lQFDcBNAr-s?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/IanKulin/TimesTables/blob/719229d3caf80b12ddfff65032e0bee29036e1c9/TimesTables/ContentView.swift"&gt;Source&lt;/a&gt;&lt;/p&gt;</description></item><item><title>User Defaults &amp; Horizontal Pickers</title><link>https://blog.iankulin.com/user-defaults-horizontal-pickers/</link><pubDate>Wed, 28 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/user-defaults-horizontal-pickers/</guid><description>&lt;p&gt;I&amp;rsquo;m on the challenges for &lt;a href="https://www.hackingwithswift.com/guide/ios-swiftui/3/3/challenge"&gt;Day 35&lt;/a&gt; of &lt;a href="https://www.hackingwithswift.com/100/swiftui"&gt;100 Days of SwiftUI&lt;/a&gt;, and despite Paul&amp;rsquo;s very clear warning:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; It’s really easy to get sucked into these challenges and spend hours&lt;/em&gt;&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have spent ages fiddling around, but of course still learning. My issue is not so much getting stuck on bugs, rather I keep wanting to do things I don&amp;rsquo;t know how to do.&lt;/p&gt;
&lt;p&gt;One issue was solved for my by the wonderful &lt;a href="https://firesideswift.fireside.fm/"&gt;Fireside Swift&lt;/a&gt; podcast. I&amp;rsquo;m working through the old (Steve &amp;amp; Zac) episodes, and they did one on the UserDefaults just when I wanted to be able to persist the multiplication table selection the user had made (this challenge app is a multiplication tables drill app for kids).&lt;/p&gt;
&lt;p&gt;First, because I hate hard coded strings, and they seem to be a thing in SwiftUI, and there&amp;rsquo;s no #const system, I&amp;rsquo;ve got an enum:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;enum&lt;/span&gt; HCS&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#bf616a"&gt;String&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; operand &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Operand&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; markerFeltWide &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;Marker Felt Wide&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then in the OnChange for the picker where I want to save/set the value into the default store:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.onChange(of: tablesSelection) { _ in 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; UserDefaults.standard.set(self.tablesSelection, 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; forKey: HCS.operand.rawValue)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; generateTable()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So basically, there&amp;rsquo;s a UserDefaults class that we can call the set method of, passing it the value we want to store, and a string value for the key.&lt;/p&gt;
&lt;p&gt;Getting it back is no harder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#bf616a"&gt;@&lt;/span&gt;State private &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; tablesSelection &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; UserDefaults&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;standard&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;integer&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;forKey&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; HCS&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;operand&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;rawValue&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Super simple!&lt;/p&gt;
&lt;p&gt;If the key doesn&amp;rsquo;t exist, &lt;a href="https://developer.apple.com/documentation/foundation/userdefaults/1407405-integer"&gt;it returns a zero&lt;/a&gt;, so the slightly more complicated production version is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#bf616a"&gt;@&lt;/span&gt;State private &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; tablesSelection &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;UserDefaults&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;standard&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;integer&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;forKey&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; HCS&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;operand&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;rawValue&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;!=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#bf616a"&gt;?&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; UserDefaults&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;standard&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;integer&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;forKey&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; HCS&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;operand&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;rawValue&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now to my horizontal wheel picker that the user manipulates to chose which times-table they want to practice:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-25-at-12.39.45-pm-1.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I love this little hack stolen from &lt;a href="https://stackoverflow.com/users/8279887/james-castrejon"&gt;James&lt;/a&gt; on &lt;a href="https://stackoverflow.com/questions/61965315/how-to-get-a-horizontal-picker-for-swift-ui"&gt;Stack Overflow&lt;/a&gt;. The first trick is that a .rotationEffect is applied to the picker, and the opposite .rotationEffect is applied to the selections. The second is to chop it off using the frame to make it a bit more compact.&lt;/p&gt;</description></item><item><title>Animation Feedback</title><link>https://blog.iankulin.com/animation-feedback/</link><pubDate>Sat, 24 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/animation-feedback/</guid><description>&lt;p&gt;I had a look at Paul&amp;rsquo;s version of the challenge to animate the Guess The Flags app, and like me he&amp;rsquo;d hit on altering the &lt;em&gt;amount&lt;/em&gt; of each animation depending on if it was the clicked on flag, but he&amp;rsquo;d done it with a lot less code - using the ternary operator in each of the modifiers - versus my approach of filling in an Int array for each of the effects and altering them depending on the user selection.&lt;/p&gt;
&lt;p&gt;Another round to Paul!&lt;/p&gt;</description></item><item><title>Animations in Views</title><link>https://blog.iankulin.com/animations-in-views/</link><pubDate>Fri, 23 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/animations-in-views/</guid><description>&lt;p&gt;It&amp;rsquo;s a very Apple-thinking thing to be learning about making beautiful and intuitive user experiences this early in a programing tutorial as I am with the &lt;a href="https://www.hackingwithswift.com/100/swiftui/32"&gt;100 Days of Swift UI&lt;/a&gt; series. Here&amp;rsquo;s a quick look at three different ways of doing animation in SwiftUI Views.&lt;/p&gt;
&lt;h4 id="implicit-animation"&gt;Implicit animation&lt;/h4&gt;
&lt;p&gt;An &lt;em&gt;implicit&lt;/em&gt; animation in SwiftUI is when you add a .&lt;em&gt;animation&lt;/em&gt;() modifier to a view. It needs to be bound to the value that&amp;rsquo;s changing so the framework knows to animate when that value changes, and the nature of the change.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2022-09-18-at-10.21.30-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-18-at-10.21.30-am.png" width="956" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://videopress.com/v/ItGiKMzz?resizeToParent=true&amp;amp;cover=true&amp;amp;preloadContent=metadata&amp;amp;useAverageColor=true"&gt;https://videopress.com/v/ItGiKMzz?resizeToParent=true&amp;amp;cover=true&amp;amp;preloadContent=metadata&amp;amp;useAverageColor=true&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the example above, the value that&amp;rsquo;s changing is &lt;code&gt;rounded&lt;/code&gt;. We declare this as an @State variable, then in the .animation() modifier, we tell the framework to watch it. When it does change (because the user presses the bottom) SwiftUI considers the difference between the view rendered in rounded state, and in the non-rounded state, then generates and outputs the frames between them.&lt;/p&gt;
&lt;h4 id="binding-animation"&gt;Binding animation&lt;/h4&gt;
&lt;p&gt;The .animation() modifier can be attached when a variable is bound to a control.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-18-at-11.13.05-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve slightly complicated things here by adding the .timingCurve() inside the .animation() - otherwise the animation wasn&amp;rsquo;t obvious visually - the effect of this is just to slow down the animation so it keeps happening for a bit after you&amp;rsquo;ve adjusted the slider.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://videopress.com/v/3caKJJkZ?resizeToParent=true&amp;amp;cover=true&amp;amp;autoPlay=true&amp;amp;controls=false&amp;amp;loop=true&amp;amp;preloadContent=metadata&amp;amp;useAverageColor=true"&gt;https://videopress.com/v/3caKJJkZ?resizeToParent=true&amp;amp;cover=true&amp;amp;autoPlay=true&amp;amp;controls=false&amp;amp;loop=true&amp;amp;preloadContent=metadata&amp;amp;useAverageColor=true&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="explicit-animation"&gt;Explicit animation&lt;/h4&gt;
&lt;p&gt;Explicit animation is used when we want to control when the animation occurs, a common reason for this would be when we want to combine a couple of changes to take place simultaneously) .&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-18-at-11.38.02-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://videopress.com/v/6dE3XCSP?resizeToParent=true&amp;amp;cover=true&amp;amp;autoPlay=true&amp;amp;controls=false&amp;amp;loop=true&amp;amp;preloadContent=metadata&amp;amp;useAverageColor=true"&gt;https://videopress.com/v/6dE3XCSP?resizeToParent=true&amp;amp;cover=true&amp;amp;autoPlay=true&amp;amp;controls=false&amp;amp;loop=true&amp;amp;preloadContent=metadata&amp;amp;useAverageColor=true&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Project 5 - Word Scramble</title><link>https://blog.iankulin.com/project-5-word-scramble/</link><pubDate>Mon, 19 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/project-5-word-scramble/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-17-at-4.30.14-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Another &lt;a href="https://www.hackingwithswift.com/100/swiftui"&gt;100 Days of Swift UI&lt;/a&gt; project wrapped up - this time a scrabble like word game. New techniques included saving a large text file in the app bundle and loading it (via a string) into an array on launch of the view. Also a short adventure into UIKit to use a UITextChecker.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/WordScramble/compare/c64c21d..ada15e2"&gt;Source&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Word Scramble Feedback</title><link>https://blog.iankulin.com/word-scramble-feedback/</link><pubDate>Mon, 19 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/word-scramble-feedback/</guid><description>&lt;p&gt;As is my practice now, after completing the &lt;a href="https://www.hackingwithswift.com/books/ios-swiftui/word-scramble-wrap-up"&gt;challenges for Project 5&lt;/a&gt;, I reviewed Paul&amp;rsquo;s solution (which is only available to subscribers) to see what he&amp;rsquo;d done better so I could learn from it.&lt;/p&gt;
&lt;p&gt;Most of the differences where not of much significance, but there was a couple of things I picked up:&lt;/p&gt;
&lt;p&gt;When the user had pressed the reset button, to empty the array of word guesses from the previous turn, I had&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;usedWords = [String]()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Whereas Paul had:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-17-at-6.19.35-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I have no idea if that&amp;rsquo;s better performing or safer, but to me, it&amp;rsquo;s a lot clearer, so I prefer Paul&amp;rsquo;s solution.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2022-09-17-at-4.30.14-pm-1.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-17-at-4.30.14-pm-1.png" width="266" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The second difference is a user experience one. I had chosen to put the score and reset button both in a bottom toolbar. It&amp;rsquo;s a good solution in the sense that it keeps the score and the reset buttons visible regardless of the size of the (scrollable) list. My code for this was appended to the bottom of the list:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;toolbar &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ToolbarItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;placement&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;bottomBar&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HStack &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;Score: \(score)&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer&lt;span style="color:#eceff4"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#bf616a"&gt;Button&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;New Word&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; startGame&lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Paul used a &lt;em&gt;SafeAreaInset&lt;/em&gt; appended to the bottom of the list, and as he was entering it I was thinking it was overly complex:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;•safeAreaInset(edge:.bottom) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(&amp;#34;Score: \(score)&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .frame (maxWidth: .infinitv)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .padding()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .background( .blue)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .foregroundColor(.white)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .font(.title)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;until I saw the result and loved it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2022-09-17-at-6.22.34-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-17-at-6.22.34-pm.png" width="490" alt=""&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Before SwiftUI</title><link>https://blog.iankulin.com/before-swiftui/</link><pubDate>Wed, 14 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/before-swiftui/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-10-at-9.28.24-am.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m on Day 26 of 100 Days, and didn&amp;rsquo;t grok the dates on my first read through, so I&amp;rsquo;ve read a couple of other explanations and sat down with a coffee and thought I&amp;rsquo;d see what YouTube had for me on the subject. I seen a few great &lt;a href="https://www.youtube.com/c/iOSAcademy/videos"&gt;iOS Academy&lt;/a&gt; videos, so &lt;a href="https://www.youtube.com/watch?v=HSFTzcYzuEQ"&gt;this one&lt;/a&gt; seemed like a good choice.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t seen enough to say if it is a good or great explanation of dates, calendars and date components in Swift yet, but man, getting to the stage of writing useful code when using storyboards and UIKit takes a while! It&amp;rsquo;s literally 3:42 in to the video before there&amp;rsquo;s enough infrastructure done for &amp;ldquo;hello world&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Takeaways: (1) Watching the launch of SwiftUI must have been mind-blowing. (2) I super appreciate the existence of Playgrounds for learning Swift.&lt;/p&gt;</description></item><item><title>Rock, Paper Scissors (2)</title><link>https://blog.iankulin.com/rock-paper-scissors-2/</link><pubDate>Tue, 13 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/rock-paper-scissors-2/</guid><description>&lt;p&gt;When I was forced by a deadline into delivering this project, I noted in its &lt;a href="https://blog.iankulin.com/rock-paper-scissors-1/"&gt;post&lt;/a&gt; that there was a number of improvements to make:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;The rock paper scissors could be some better data structure than an array and some ints.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;I don’t love the try to win, try to lose aspect, but the client specified it&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Having the didUserWin and didComputerWin funcs is a cop out – that should probably be a single function returning a win/lose/draw type&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;I also am unhappy with nesting them in the view namespace to use my #consts&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;duplicating the last part of the view but making the elements .hidden() to keep the same spacing seems like a kludge&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;when I added the link to the Hacking With SwiftUI page with the app brief just now, I noticed I haven’t done the scoring the way it was asked for&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here&amp;rsquo;s the progress&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The rock paper scissors could be some better data structure than an array and some ints&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Done. Made a sweet swifty enum. Read about it here. That also solved #4&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I don’t love the try to win, try to lose aspect, but the client specified it&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can&amp;rsquo;t always help what a client wants. Deal with it.&lt;/p&gt;
&lt;p&gt;_Having the didUserWin and didComputerWin funcs is a cop out – that should probably be a single function returning a win/lose/dra_w&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;enum&lt;/span&gt; GameResult &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; win
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; loss
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; draw
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;func&lt;/span&gt; gameResult&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;user&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; FingerShape&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; computer&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; FingerShape&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;-&amp;gt;&lt;/span&gt; GameResult &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;switch&lt;/span&gt; user &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;rock&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;switch&lt;/span&gt; computer &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;rock&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;draw
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;paper&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;loss
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;scissors&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;win
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;paper&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;switch&lt;/span&gt; computer &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;rock&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;win
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;paper&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;draw
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;scissors&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;loss
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;scissors&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;switch&lt;/span&gt; computer &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;rock&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;loss
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;paper&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;win
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;scissors&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;draw
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;duplicating the last part of the view but making the elements .hidden() to keep the same spacing seems like a kludge&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This was an issue - not just because of the mess of code, but because (according to Paul) it&amp;rsquo;s a workload issue - the view managing part of SwiftUI has to keep creating and destroying parts of our view instead of recycling them. In an earlier lesson, he encouraged the use of the ternary operator in modifiers to avoid this - but .hidden() doesn&amp;rsquo;t have any arguments. Here&amp;rsquo;s the sort of code I&amp;rsquo;m talking about - I&amp;rsquo;ve got this sort of thing several places.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;if revealResult {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(computerSelection.rawValue)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .font(.system(size: 200))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(winText).font(.title)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button(&amp;#34;Play again&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; goalIsWinThisTurn = Bool.random()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; revealResult = false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; if showScores {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; score = 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; numberOfPlays = 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showScores.toggle()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .buttonStyle(CustomButtonStyle())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;else {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(computerSelection.rawValue)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .font(.system(size: 200))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .hidden()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(winText)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .font(.title)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .hidden()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button(&amp;#34;Play again&amp;#34;) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .buttonStyle(CustomButtonStyle())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .hidden()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I was surprised when googling something like &amp;ldquo;swiftui view visibility not hidden()&amp;rdquo; I found, instead of a stack overflow question, a &lt;a href="https://developer.apple.com/tutorials/swiftui-concepts/choosing-the-right-way-to-hide-a-view?changes=_3"&gt;nice Apple tutorial&lt;/a&gt; at the top of the links.&lt;/p&gt;
&lt;p&gt;Within was my answer - most views have an .opacity() modifier. With that, the code above becomes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Group {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(computerSelection.rawValue)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .font(.system(size: 200))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(winText).font(.title)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Spacer()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Button(&amp;#34;Play again&amp;#34;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; goalIsWinThisTurn = Bool.random()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; revealResult = false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; if showScores {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; score = 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; numberOfPlays = 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showScores.toggle()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .buttonStyle(CustomButtonStyle())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.opacity(revealResult ? 1 : 0)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Noice. I then when on a ternary insertion spree that dropped my view body down from 74 lines of code to 52 and improved readability substantially.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/RockPaper/blob/fe8e2eea247e9c1ab13daa3e39929100991f69c5/RockPaper/ContentView.swift"&gt;Current source&lt;/a&gt;&lt;/p&gt;</description></item><item><title>.self in ForEach</title><link>https://blog.iankulin.com/self-in-foreach/</link><pubDate>Wed, 07 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/self-in-foreach/</guid><description>&lt;p&gt;I&amp;rsquo;m on Day 25 of Hacking With SwiftUI, and &lt;a href="https://www.hackingwithswift.com/guide/ios-swiftui/2/2/key-points"&gt;Paul is making a point&lt;/a&gt; about how SwiftUI can loop over an array to build a view. He starts with this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let agents = [&amp;#34;Cyril&amp;#34;, &amp;#34;Lana&amp;#34;, &amp;#34;Pam&amp;#34;, &amp;#34;Sterling&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VStack {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach(0..&amp;lt;agents.count) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text(agents[$0])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But then proposes an alternative:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;let agents = [&amp;#34;Cyril&amp;#34;, &amp;#34;Lana&amp;#34;, &amp;#34;Pam&amp;#34;, &amp;#34;Sterling&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VStack {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ForEach(agents, id: \.self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text($0)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;He explains the use of \.self here by saying&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So, we come back to how Swift can identify values in our array. When we were using a range such as &lt;code&gt;0..&amp;lt;5&lt;/code&gt; or &lt;code&gt;0..&amp;lt;agents.count&lt;/code&gt;, Swift knew for sure that each item was unique because it would use the numbers from the range – each number was used only once in the loop, so it would definitely be unique.&lt;/p&gt;
&lt;p&gt;In our array of strings that’s no longer possible, but we can clearly see that each value is unique: the values in &lt;code&gt;[&amp;quot;Cyril&amp;quot;, &amp;quot;Lana&amp;quot;, &amp;quot;Pam&amp;quot;, &amp;quot;Sterling&amp;quot;]&lt;/code&gt; don’t repeat. So, what we can do is tell SwiftUI that the strings themselves – “Cyril”, “Lana”, etc – are what can be used to identify each view in the loop uniquely.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m having a couple of problems with this.&lt;/p&gt;
&lt;h4 id="grumble-one"&gt;Grumble One&lt;/h4&gt;
&lt;p&gt;The first is that Swift can&amp;rsquo;t really use the &amp;ldquo;strings themselves&amp;rdquo;. So this doesn&amp;rsquo;t work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ForEach(agents, String($0)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text($0)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It complains that there&amp;rsquo;s no initialiser to take those arguments. So \.self must be something fancier. This is something I can presumably investigate by looking at the initialisers for ForEach.&lt;/p&gt;
&lt;h4 id="grumble-two"&gt;Grumble Two&lt;/h4&gt;
&lt;p&gt;And the second grumble is that the first formulation &lt;code&gt;ForEach(0..&amp;lt;agents.count)&lt;/code&gt; throws a compiler warning &amp;ldquo;&lt;code&gt;Non-constant range: argument must be an integer literal&lt;/code&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-03-at-11.44.26-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Which is true, if I swap it for the number &amp;lsquo;4&amp;rsquo; it stops complaining. I guess in the initialiser for ForEach there&amp;rsquo;s something specifying this although it&amp;rsquo;s not clear to me why. Again it&amp;rsquo;s an initialiser mystery because I can make the warning go away by adding the id reference to self.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-03-at-2.06.48-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Googling for the warning finds a &lt;a href="https://www.hackingwithswift.com/forums/swiftui/compiler-warning-non-constant-range-argument-must-be-an-integer-literal/14878"&gt;page on the Hacking with Swift forums&lt;/a&gt; that throws a little bit of light on the issue by looking at the inits.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-03-at-2.13.44-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;The difference between these two that we&amp;rsquo;re interested in is that in the first, the type is &lt;code&gt;Range&lt;/code&gt;, and the second &lt;code&gt;Data&lt;/code&gt;. &lt;a href="https://www.hackingwithswift.com/users/roosterboy"&gt;@RoosterBoy&lt;/a&gt;&amp;rsquo;s explanation for why we should use literal ints for the range is that it&amp;rsquo;s to be safe from the array changing size during the loop - which is slightly unsatisfying because that could still happen with the literal int range.&lt;/p&gt;
&lt;h4 id="grumble-one-again"&gt;Grumble One again&lt;/h4&gt;
&lt;p&gt;These initialisers also shed some light on my first problem, the references used by SwiftUI should be KeyPaths. &lt;a href="https://sarunw.com/posts/what-is-keypath-in-swift/"&gt;Sarun&amp;rsquo;s explanation&lt;/a&gt; of them makes them sound like actual references - ie memory addresses for the properties. That would make sense since SwiftUi wants them to keep track of things.&lt;/p&gt;
&lt;p&gt;So I tried this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ForEach(agents, id: \String.$0) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text($0)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;but the compiler is still unhappy - so clearly I am not knowledgeable enough yet to solve this one.&lt;/p&gt;</description></item><item><title>Project 3</title><link>https://blog.iankulin.com/project-3/</link><pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/project-3/</guid><description>&lt;p&gt;This one&amp;rsquo;s not really a project, just a couple of little updates to earlier work, and a code snippet.&lt;/p&gt;
&lt;h4 id="challenge-1"&gt;Challenge 1&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Go back to project 1 and use a conditional modifier to change the total amount text view to red if the user selects a 0% tip.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first one is pretty simple - a ternary condition to make the total red if the tip is set to zero.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-09-02-at-5.10.56-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Text(grandTotal, format: currencyCode)
.foregroundColor(tipPercentage == 0 ? .red : .primary)&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;ternary operator&lt;/em&gt; is like a little inline if then else statement. It has the format:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(w ? t : f)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;where w is the condition, t is the code if the condition is true and f is the code if the condition is false. So the code above checks if the tipPercentage is zero, if it is, the grandTotal text is red, otherwise it&amp;rsquo;s coloured &lt;em&gt;.primary&lt;/em&gt; - one of the semantic colors. The semantic colours are colours set by the system and referred to by their purpose. In this case it will be black (the &amp;ldquo;primary&amp;rdquo; text colour), unless I change the theme to dark mode, in which case it will be white.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/WeSplit/compare/v1.0...v1.1"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="challenge-2"&gt;Challenge 2&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Go back to project 2 and replace the&lt;/em&gt; &lt;code&gt;Image&lt;/code&gt; &lt;em&gt;view used for flags with a new&lt;/em&gt; &lt;code&gt;FlagImage&lt;/code&gt;&lt;em&gt;&lt;code&gt;()&lt;/code&gt; view that renders one flag image using the specific set of modifiers we had.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Again, pretty straightforward. I just made a new view struct in the main view.&lt;/p&gt;
&lt;p&gt;struct FlagView: View {
var flagOf: String&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; var body: some View {
 Image(flagOf)
 .renderingMode(.original)
 .shadow(radius: 5)
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;Then called it with our flag name.&lt;/p&gt;
&lt;p&gt;ForEach(0..&amp;lt;3) { number in
Button {
// flag was tapped
flagTapped(number)
} label: {
FlagView(flagOf: countries[number])
}
}&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/GuessTheFlag/compare/v1.0...v1.1"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="challenge-3"&gt;Challenge 3&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Create a custom&lt;/em&gt; &lt;code&gt;ViewModifier&lt;/code&gt; &lt;em&gt;(and accompanying&lt;/em&gt; &lt;code&gt;View&lt;/code&gt; &lt;em&gt;extension) that makes a view have a large, blue font suitable for prominent titles in a view.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;struct ContentView: View {
var body: some View {
VStack{
Text(&amp;ldquo;Hello World&amp;rdquo;)
.titleStyle()
Spacer()
}
}
}&lt;/p&gt;
&lt;p&gt;struct ProminentTitle: ViewModifier {
func body(content: Content) -&amp;gt; some View {
content
.font(.largeTitle)
.foregroundColor(.blue)
.padding()
}
}&lt;/p&gt;
&lt;p&gt;extension View {
func titleStyle() -&amp;gt; some View {
modifier(ProminentTitle())
}
}&lt;/p&gt;</description></item><item><title>Day 23 - Views and Modifiers - Part 4</title><link>https://blog.iankulin.com/day-23-views-and-modifiers-part-4/</link><pubDate>Sat, 03 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/day-23-views-and-modifiers-part-4/</guid><description>&lt;img src="https://blog.iankulin.com/images/psm_v10_d562_the_hindoo_earth-3.jpg" width="417" alt="This image has an empty alt attribute; its file name is psm\_v10\_d562\_the\_hindoo\_earth-3.jpg"&gt;
&lt;p&gt;Then the last trick for for decomposing the views, is to remember we can pass values when we init a struct. So something like this:&lt;/p&gt;
&lt;p&gt;struct ContentView: View {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
 VStack{
 GreenPaddedText(text: &amp;quot;Hello&amp;quot;)
 GreenPaddedText(text: &amp;quot;world&amp;quot;)
 }
}


struct GreenPaddedText: View {
 var text: String

 var body: some View {
 Text(text)
 .foregroundColor(.green)
 .padding()
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;This is probably my favourite - because although in this example I&amp;rsquo;ve created the mini-view struct in the body, if it&amp;rsquo;s a building block I can use elsewhere in a different view, it&amp;rsquo;s super portable.&lt;/p&gt;</description></item><item><title>Day 23 - Views and Modifiers - Part 3</title><link>https://blog.iankulin.com/day-23-views-and-modifiers-part-3/</link><pubDate>Fri, 02 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/day-23-views-and-modifiers-part-3/</guid><description>&lt;img src="https://blog.iankulin.com/images/psm_v10_d562_the_hindoo_earth-3.jpg" width="472" alt=""&gt;
&lt;p&gt;The next part of day 23 started to make my brain hurt a bit. It&amp;rsquo;s easy to imagine that when presenting a complex screen - perhaps some data from a source as a mixture of images and text loaded from a database into a scroll-able view, that the view may start to get complex. Then it becomes good practice to decompose the views to make the code clearer, less error prone, and to avoid any unnecessary repetition.&lt;/p&gt;
&lt;p&gt;Paul&amp;rsquo;s first suggestion is to pull some parts of the view as &lt;em&gt;properties&lt;/em&gt; of the same view.&lt;/p&gt;
&lt;p&gt;struct ContentView: View {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let greenText = Text(&amp;quot;Hello&amp;quot;).foregroundColor(.green)

var body: some View {
 VStack{
 greenText
 greenText
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;This works fine, and exactly how you expect, except that if you don&amp;rsquo;t enclose it in the VStack, you just get one Text, but two ContentPreviews. I do not understand why yet, but its probably something to do with the @ViewBuilder property wrapper.&lt;/p&gt;
&lt;p&gt;But&amp;hellip; a property can&amp;rsquo;t refer to another property, so this isn&amp;rsquo;t compilable Swift:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-8.24.46-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;To get around this, we can use a computed property. So this works:&lt;/p&gt;
&lt;p&gt;struct ContentView: View {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@State private var greeting = &amp;quot;Hello&amp;quot;

var greenText: some View {
 Text(greeting).foregroundColor(.green)
}

var body: some View {
 VStack{
 greenText
 greenText
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;In fact, this is what I&amp;rsquo;ve been doing so far to decompose views, although I&amp;rsquo;ve been dropping the segments underneath the main body to make things subjectively neater.&lt;/p&gt;
&lt;p&gt;Paul cautions about returning multiple views in this computed property manner. So this does not work:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-8.33.20-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;That makes sense - we were relying on the implied return. Putting them in a VStack would work - because we&amp;rsquo;re just returning a single view (the VStack) which happens to contain multiple views.&lt;/p&gt;
&lt;p&gt;struct ContentView: View {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@State private var greeting = &amp;quot;Hello&amp;quot;

var body: some View {
 VStack{
 greenText
 greenText
 }
}

var greenText: some View {
 VStack{
 Text(greeting).foregroundColor(.green)
 Text(greeting).foregroundColor(.green)
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s another view container type called Group which is like a stack, but just contains, rather than arranging, a collection of views, that can be used in the same way.&lt;/p&gt;
&lt;p&gt;Alternatively, and I assume this is related to the problem I had above, we can just wrap the property with the @ViewBuilder attribute.&lt;/p&gt;
&lt;p&gt;struct ContentView: View {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@State private var greeting = &amp;quot;Hello&amp;quot;

var body: some View {
 VStack{
 greenText
 greenText
 }
}

@ViewBuilder var greenText: some View {
 Text(greeting).foregroundColor(.green)
 Text(greeting).foregroundColor(.green)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;</description></item><item><title>Day 23 - Views and Modifiers - Part 2</title><link>https://blog.iankulin.com/day-23-views-and-modifiers-part-2/</link><pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/day-23-views-and-modifiers-part-2/</guid><description>&lt;img src="https://blog.iankulin.com/images/psm_v10_d562_the_hindoo_earth-2.jpg" width="484" alt=""&gt;
&lt;p&gt;Although &amp;ldquo;immutable&amp;rdquo; the view structs can contain some control statements such as if/then and for loops. So this is quite legal, and useful.&lt;/p&gt;
&lt;p&gt;struct ContentView: View {
@State private var likesGreen = true&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
 if likesGreen {
 Text(&amp;quot;Hello World&amp;quot;)
 .background(.green)
 }
 else
 {
 Text(&amp;quot;Hello World&amp;quot;)
 .background(.blue)
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;But Paul cautions against this, saying:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.hackingwithswift.com/books/ios-swiftui/conditional-modifiers"&gt;You can often use regular if conditions to return different views based on some state, but this actually creates more work for SwiftUI – rather than seeing one Button being used with different colors, it now sees two different Button views, and when we flip the Boolean condition it will destroy one to create the other rather than just recolor what it has.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead, he encourages the use of the ternary operator in modifiers for these situations. The ternary operator is like an if/then/else statement packaged up neatly. So the code above becomes:&lt;/p&gt;
&lt;p&gt;struct ContentView: View {
@State private var likesGreen = true&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
 Text(&amp;quot;Hello World&amp;quot;)
 .background(likesGreen ? .green : .blue)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;He&amp;rsquo;s not arguing that it&amp;rsquo;s neater, but also that it&amp;rsquo;s more efficient - that the first version has to destroy and create a Text when the value of the flag changes, whereas the second one just changes the colour. I assume he&amp;rsquo;s correct, but it&amp;rsquo;s not obvious why that would be so. It must be in the magic of how SwiftUI optimises how and when it renders each part of a view.&lt;/p&gt;</description></item><item><title>Day 23 - Views and Modifiers - Part 1</title><link>https://blog.iankulin.com/day-23-views-and-modifiers-part-1/</link><pubDate>Wed, 31 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/day-23-views-and-modifiers-part-1/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/psm_v10_d562_the_hindoo_earth-1.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I found this one of the trickier days, so I&amp;rsquo;ll write it out to clear up my thinking.&lt;/p&gt;
&lt;p&gt;To draw to to screen in SwiftUI, we don&amp;rsquo;t call a command to draw on a canvas or window. Rather, a &lt;em&gt;view&lt;/em&gt; is defined as an immutable struct of type some View. Here&amp;rsquo;s the simple one from the default Xcode project.&lt;/p&gt;
&lt;p&gt;struct ContentView: View {
var body: some View {
Text(&amp;ldquo;Hello, world!&amp;rdquo;)
.padding()
}
}&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;body&lt;/em&gt; var must be present, and can contain up to ten views. This simple example only contains one view - a text box. There are container view types that can, well, contain other views - for example a &lt;em&gt;HStack&lt;/em&gt; which arranges its content horizontally.&lt;/p&gt;
&lt;p&gt;struct ContentView: View {
var body: some View {
HStack {
Text(&amp;ldquo;Hello, world!&amp;rdquo;)
.padding()
Rectangle()
.frame(width: 100, height: 100, alignment: .center)
}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;produces:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-7.20.54-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-7.20.54-pm.png" width="164" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Having defined our view struct, we don&amp;rsquo;t call for it to be rendered on the screen, SwiftUI is just going to do that for us whenever it thinks it needs to. This seems bizarre at first, but you get used to it. It will happen when it needs to - usually because the information that makes up the view has changed. We help that to happen by binding the views to their data in various ways. More on that another day.&lt;/p&gt;
&lt;p&gt;If you look back at the code above, you&amp;rsquo;ll see that views often have &lt;em&gt;modifiers&lt;/em&gt; attached to them. In our example the &lt;em&gt;padding()&lt;/em&gt; on the text and the &lt;em&gt;.frame()&lt;/em&gt; on the rectangle. There&amp;rsquo;s many different modifiers for all of the different view primitives. Many of them are common to different primitives, some are different. It&amp;rsquo;s possible to attach them to container views - in which case they are applied to all the views in the container. For example, if we move the .frame() to the HStack, like this:&lt;/p&gt;
&lt;p&gt;struct ContentView: View {
var body: some View {
HStack {
Text(&amp;ldquo;Hello, world!&amp;rdquo;)
.padding()
Rectangle()
}
.frame(width: 100, height: 100, alignment: .center)
}
}&lt;/p&gt;
&lt;p&gt;We get&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-7.33.07-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-7.33.07-pm.png" width="188" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The order of the modifiers is important. You can think of each modifier that&amp;rsquo;s added as wrapping around the view and any previous modifiers. In this example, we&amp;rsquo;ve got the same text field with the &lt;em&gt;.padding()&lt;/em&gt; and &lt;em&gt;.background(.blue)&lt;/em&gt; modifiers. The left one has the padding first, then is wrapped in the blue background. The right one has the blue background applied first, then the padding.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-7.39.53-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-08-31-at-7.39.53-pm.png" width="192" alt=""&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>@ObservedObject v @StateObject</title><link>https://blog.iankulin.com/observedobject-v-stateobject/</link><pubDate>Sat, 13 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/observedobject-v-stateobject/</guid><description>&lt;p&gt;The Youtube algorithm thinks I need to watch more MVVM videos, and it turns out it&amp;rsquo;s probably right. A day or two ago in an &lt;a href="https://blog.iankulin.com/simple-mvvm/"&gt;MVVM&lt;/a&gt; post using a super simple example, I stored the view model as a property of the view using the @ObservedObject wrapper, as I created it.&lt;/p&gt;
&lt;p&gt;struct ContentView: View {
@ObservedObject var light = LightViewModel()&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
 VStack{
 Spacer()
 if light.isOn(){
 drawLitBulb
 }
 else{
 Image(systemName: &amp;quot;lightbulb.fill&amp;quot;).font(.system(size: 72))
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But then today, Youtube served me up this video from &lt;a href="https://www.youtube.com/c/BeyondOnesAndZeros/videos"&gt;BeyondOnesAndZeros&lt;/a&gt;&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/LntH6moCuo0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;

&lt;p&gt;They start off with @ObservableObject, but then say that if the View Model is instantiated there, that this is repeated every time the View is recreated (which happens every time it&amp;rsquo;s redrawn).&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t really understand the property wrappers (like @ObservableObject) but I assume this was a type property - ie static. Apparently not. The &lt;a href="https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app"&gt;Apple documentation on managing data&lt;/a&gt; says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SwiftUI might create or recreate a view at any time, so it’s important that initializing a view with a given set of inputs always results in the same view. As a result, it’s unsafe to create an observed object inside a view. Instead, SwiftUI provides the &lt;a href="https://developer.apple.com/documentation/swiftui/stateobject"&gt;&lt;code&gt;StateObject&lt;/code&gt;&lt;/a&gt; attribute for this purpose.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So that code should use @StateObject, and @ObservedObject should be used where I pass it down into other view structs in the hierarchy.&lt;/p&gt;
&lt;p&gt;Paul Hudson gives a good example in his explanation on this topic &lt;a href="https://www.avanderlee.com/swiftui/stateobject-observedobject-differences/"&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Simple MVVM</title><link>https://blog.iankulin.com/simple-mvvm/</link><pubDate>Thu, 11 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/simple-mvvm/</guid><description>&lt;p&gt;MVVM (Model-View-View Model) is an architectural pattern for apps that separates the data (Model) from the user interface (View). The communication between these two parts is facilitated by a View Model.&lt;/p&gt;
&lt;p&gt;Model &amp;lt;-&amp;gt; View Model &amp;lt;-&amp;gt; View&lt;/p&gt;
&lt;h3 id="model"&gt;Model&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;Model&lt;/em&gt; is platform independent - we should be able to pluck it out and add it to a different application running on a different platform without any trouble. Any business rules will be part of the Model along with the data. For example, if it&amp;rsquo;s a rule that every customer has a sales contact, this can be enforced in the Model.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2022-08-06-at-4.20.38-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-08-06-at-4.20.38-pm.png" width="85" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Model (or Models - an app could have more than one) does not know anything about the &lt;em&gt;View&lt;/em&gt; or the &lt;em&gt;View Model&lt;/em&gt;. In a SwiftUI app, we&amp;rsquo;ll almost always have the model in its own file.&lt;/p&gt;
&lt;p&gt;Our simple example app for this post is going to be a light bulb app. There will be a picture of a light bulb, and a button which will toggle the light off an on. It&amp;rsquo;s difficult to think of a simpler Model. This is what I&amp;rsquo;ve come up with.&lt;/p&gt;
&lt;p&gt;struct Light{
var on: Bool = false
}&lt;/p&gt;
&lt;p&gt;A Model in a real application could be massive - with connections to online data stores and complex business rules. Our light just has two exclusive states - off and on. We could make it more complex - it could be an incandescent light with a particular resistance and a formula for the brightness output for any particular voltage that was applied. All of that would go into the Model. But for today, we&amp;rsquo;ll just have &lt;em&gt;on&lt;/em&gt; or not.&lt;/p&gt;
&lt;p&gt;In all of the SwiftUI examples I&amp;rsquo;ve seen so far, the Model has been a struct. Perhaps it can be other things, but Swift has deep magic (structs are mysteriously immutable, so they are &lt;a href="https://www.hackingwithswift.com/books/ios-swiftui/why-state-only-works-with-structs"&gt;actually rebuilt when any properties change&lt;/a&gt;) for efficiently knowing if structs have changed, so perhaps not.&lt;/p&gt;
&lt;h3 id="view-model"&gt;View Model&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;View Model&lt;/em&gt; will have the &lt;em&gt;Model&lt;/em&gt; as a property. That way it can do things to the Model, and to access the bits it needs to pass off to the View. The View Model is always a class, as it needs to comply with the protocol of ObservableObject. If &lt;em&gt;protocol&lt;/em&gt; and &lt;em&gt;ObservableObject&lt;/em&gt; are foreign to you, don&amp;rsquo;t panic. You don&amp;rsquo;t need to understand any more than that the View needs to know when the View Model changes, so it observes the View Model, and for that magic to happen, the View Model needs to be a class having that ability (of being observed) which it gets from having the protocol ObservableObject.&lt;/p&gt;
&lt;p&gt;The View Model will also have properties or methods that the View can use to access the Model data. Remember the Model is completely hidden from the View, so the View Model provides that access. In a real situation, it would also do whatever translation or packaging was required to make the View&amp;rsquo;s life easy. In our example it is rather simple.&lt;/p&gt;
&lt;p&gt;class LightViewModel: ObservableObject {
@Published private var lightBulb = Light(on: false)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func toggle() {
 lightBulb.on = !lightBulb.on
}

var isOn: Bool { return lightBulb.on }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;Again, the View Model is in it&amp;rsquo;s own file. The only thing we haven&amp;rsquo;t mentioned is the &lt;em&gt;@Published&lt;/em&gt; used in the property for our Model. This is just part of the magic mentioned earlier in the discussion about &lt;a href="https://developer.apple.com/documentation/combine/observableobject"&gt;ObservableObject&lt;/a&gt; which allows the View to know that something has changed, and that it needs to react to this by rebuilding the View.&lt;/p&gt;
&lt;h3 id="view"&gt;View&lt;/h3&gt;
&lt;p&gt;The View is just our regular SwiftUI view. A crucial part is that it holds the View Model for the light as a property wrapped with the @ObservedObject. This completes out connections between the three parts of our architecture.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Model View is an @ObservableObject which has the Model as a @Published property&lt;/li&gt;
&lt;li&gt;The View contains the View Model as an @StateObject property&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These connections have these effects&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The View cannot ever access the Model directly&lt;/li&gt;
&lt;li&gt;If the Model changes, the View Model is aware, and broadcasts this in a way that the View knows about&lt;/li&gt;
&lt;li&gt;The View just redraws itself if that happens&lt;/li&gt;
&lt;li&gt;As the View does this, it asks the View Model for the parts of the data it needs&lt;/li&gt;
&lt;li&gt;This both protects and hides the Model from the view, and is an opportunity for the View Model to do any work it needs to to the data to make it easy for the View to put on the screen.&lt;/li&gt;
&lt;li&gt;Any user interaction that occurs in the View is passed to the View Model to deal with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;struct ContentView: View {
@StateObject var light = LightViewModel()&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var body: some View {
 VStack{
 Spacer()
 if light.isOn {
 drawLitBulb
 }
 else {
 Image(systemName: &amp;quot;lightbulb.fill&amp;quot;).font(.system(size: 72))
 }
 Spacer()
 Button(&amp;quot;Toggle Light&amp;quot;, action: {
 light.toggle()}
 )
 .padding()
 .font(.title)
 .foregroundColor(.white)
 .background(Color.accentColor)
 .cornerRadius(10)
 .padding()
 }
}

var drawLitBulb: some View {
 // view of an iluminated bulb
 ZStack{
 Circle().fill(.yellow).frame(width: 150, height: 150)
 Image(systemName: &amp;quot;lightbulb&amp;quot;).font(.system(size: 72))
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;To make the code read nicely, the View Model is called &amp;ldquo;light&amp;rdquo;, and it&amp;rsquo;s an @StateObject so for the redrawing trigger to work correctly. The rest of the code should be reasonably clear if you&amp;rsquo;ve made a few SwiftUI views.&lt;/p&gt;
&lt;p&gt;We check if the light is on (by asking the View Model), if it is, we draw a lit bulb, if not, an unlit bulb is drawn. The last UI element is our Button() whose action is the toggle() method of light - our View Model.&lt;/p&gt;
&lt;p&gt;The source for this project is on &lt;a href="https://github.com/IanKulin/MVVMLight"&gt;GitHub here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>@ScaledMetric</title><link>https://blog.iankulin.com/scaledmetric/</link><pubDate>Fri, 29 Jul 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/scaledmetric/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-07-23-at-9.04.21-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I solved the problem (well, I googled a &lt;a href="https://stackoverflow.com/questions/72568296/sf-symbol-images-different-sizes"&gt;stackoverflow result&lt;/a&gt; to the problem) in the previous post about the different heights of the SF Symbols. The answer was to put them in a frame and lock the height. A problem that then arises from that is that when the user changes the text size, they&amp;rsquo;ll be out of wack. Apple&amp;rsquo;s solution to that, introduced in iOS 14 is the &lt;a href="https://developer.apple.com/documentation/swiftui/scaledmetric"&gt;@ScaledMetric property wrapper&lt;/a&gt; that does some magic I don&amp;rsquo;t fully understand yet.&lt;/p&gt;</description></item><item><title>SwiftUI Essentials</title><link>https://blog.iankulin.com/swiftui-essentials/</link><pubDate>Wed, 27 Jul 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/swiftui-essentials/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-07-23-at-4.12.38-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I hadn&amp;rsquo;t fully gotten my head around what&amp;rsquo;s going on with the declarative nature of SwiftUI, until I&amp;rsquo;d watched this video&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s from the &lt;a href="https://developer.apple.com/videos/play/wwdc2019/216/"&gt;2019 WWDC&lt;/a&gt; which is when (I guess) SwiftUI was new. I still don&amp;rsquo;t have a good handle on how the views are bound to their data, but there is a video from this same series about Data Flows which I imagine will also answer those questions.&lt;/p&gt;
&lt;p&gt;Of course, I could also have been pushing forwards on the two courses I&amp;rsquo;m undertaking - no doubt they are about to teach me these same things, but at least I&amp;rsquo;m procrastinating constructively.&lt;/p&gt;</description></item><item><title>iOS Dev Weekly</title><link>https://blog.iankulin.com/ios-dev-weekly/</link><pubDate>Tue, 26 Jul 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/ios-dev-weekly/</guid><description>&lt;p&gt;Dave Verwer&amp;rsquo;s &lt;a href="https://iosdevweekly.com/"&gt;iOS Dev Weekly&lt;/a&gt; digest of links mainly about Swift libraries was mentioned on a podcast I was listening to last night - perhaps the &lt;em&gt;Swift with Sundell&lt;/em&gt; &lt;a href="https://www.swiftbysundell.com/podcast/16/"&gt;chat with Sommer Panage&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My &lt;a href="https://iosdevweekly.com/issues/568?#start"&gt;first issue&lt;/a&gt; (it&amp;rsquo;s an email newsletter) arrived, and it&amp;rsquo;s pretty great. Not too long, chatty but on topic, and with links to follow for more info. As well as new or improved libraries, other topics are mentioned - I went down a rabbit hole on &lt;a href="https://useyourloaf.com/blog/swiftui-split-view-configuration/"&gt;SwiftUI Split View Configuration&lt;/a&gt;, ending up at this WWDC video about it.&lt;/p&gt;</description></item><item><title>Passing Data</title><link>https://blog.iankulin.com/passing-data/</link><pubDate>Wed, 20 Jul 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/passing-data/</guid><description>&lt;p&gt;Sean Allen has come to my notice a couple of times, once where he was mentioned as freelance contractor who is a great contributor to the community (I think perhaps that was on &lt;a href="https://podcasts.apple.com/au/podcast/swiftcoders-interviews-with-swift-developers/id1082937962"&gt;Swiftcoders Podcast&lt;/a&gt;), and I&amp;rsquo;ve also bumped into him as co-host (with Paul Hudson) of the early episodes of the &amp;ldquo;&lt;a href="https://podcasts.apple.com/au/podcast/swift-over-coffee/id1435076502"&gt;Swift over Coffee&lt;/a&gt;&amp;rdquo; podcast.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/HXoVSbwWUIk?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;

&lt;p&gt;This video I watched last night is a compilation of the first few videos of &lt;a href="https://seanallen.teachable.com/p/swiftui-fundamentals"&gt;Sean&amp;rsquo;s SwiftUI course&lt;/a&gt;, and it&amp;rsquo;s pretty great. In particular he does a great job of explaining how to start to refactor child views out and call them, and how all the stacks go together to make a pretty interface. What he does not do is vist/explain any of the Swift language fundamentals. If you don&amp;rsquo;t already know what a struc is, and the Swift flavour of them, it may be a challenging place to start.&lt;/p&gt;
&lt;p&gt;In the last couple of tutorials he starts on the way the views are called, and how we can pass values into them. This is great marketing for me - it&amp;rsquo;s exactly where I&amp;rsquo;m up to in my journey - I&amp;rsquo;m perplexed about the structure of a SwiftUI app (where&amp;rsquo;s main?!) and the engine that&amp;rsquo;s watching when the UI needs updated and building the views. For example, I want to write a little hello world that just prints the time on the screen. I got this far:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;struct ContentView&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let today &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; Date&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;now
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;var&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; some View &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Text&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;today&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; style&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;time&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But then a minute later when it needs changed what happens? In traditional programming there would be a loop in main where I could check the minutes have changed, and force the redraw of the label.&lt;/p&gt;
&lt;p&gt;I feel this is the thing that&amp;rsquo;s going to be explained clearly in the next few videos of Sean&amp;rsquo;s course, which is cunningly behind the paywall. I&amp;rsquo;m very tempted, although my learning is already spread over two proper courses plus a shotgun blast of other reading, podcasts and self created programming tasks.&lt;/p&gt;
&lt;p&gt;I did have one moment of confusion when Sean passes down an @State variable to a child view and said the variable in the child view needed to be made @Binding. One of the top comments points this out as being unneeded and Sean agrees.&lt;/p&gt;</description></item></channel></rss>