
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>TrozWare</title>
  <subtitle>Crazy Mac lady. Mac author &amp; developer. Lover of Apple computers and devices. Swift &amp; SwiftUI enthusiast. Unofficial Mac app dev evangelist</subtitle>
  <link href="https://troz.net/atom.xml" rel="self" />
  <link href="https://troz.net/" />
  <updated>2026-05-26T15:00:00Z</updated>
  <id>https://troz.net/</id>
  <author>
    <name>Sarah Reichelt</name>
  </author>
  <entry>
    <title>In Praise of SwiftUI</title>
    <link href="https://troz.net/post/2026/swiftui_praise/" />
    <updated>2026-05-26T15:00:00Z</updated>
    <id>https://troz.net/post/2026/swiftui_praise/</id>
    <content type="html">&lt;p&gt;Recently, I have seen multiple posts on Mastodon suggesting that SwiftUI is a failure, that nobody is using it, and that Apple should have focused on improving or replacing AppKit and UIKit instead of wasting everyone&#39;s time with SwiftUI. While I have great respect for some of the people saying these things, I disagree. I love using SwiftUI, I think a large number of developers are using it, and I want to explain what I find so great about it.&lt;/p&gt;
&lt;p&gt;Perhaps one problem was the name. Yes, it allows you to define your user interface in Swift, but so do AppKit and UIKit. For me, the part that I miss most when going back to work on an older project is the data flow and the reactive nature of SwiftUI, and that&#39;s not reflected in the name at all.&lt;/p&gt;

&lt;h2&gt;Data Flow&lt;/h2&gt;
&lt;p&gt;I have written several articles about SwiftUI&#39;s data flow. The most recent one was in 2023 and it&#39;s still valid today: &lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/&quot;&gt;SwiftUI Data Flow 2023&lt;/a&gt;. I won&#39;t repeat all the details here, but SwiftUI gives us features that allow for concise and logical data control, while making it clear which view owns the data and provides the single source of truth.&lt;/p&gt;
&lt;p&gt;The reactive aspect of SwiftUI feels like magic! It allows me to have something like a stored setting with a default value, connected to a menu item with a checkmark, a toggle in a settings window, a button in the toolbar, and a switch on the main interface. SwiftUI keeps these all in sync without any effort on my part. This sort of flexibility was a lot of work in AppKit and UIKit, and it was easy to end up with sections of the UI out of sync with the data. In AppKit apps, you could use Cocoa Bindings, but these were tricky to set up, not good with source control and totally opaque to any developer new to the project.&lt;/p&gt;
&lt;h2&gt;User Interface&lt;/h2&gt;
&lt;p&gt;Let&#39;s go deeper into the UI part. AppKit and UIKit have two options for creating user interfaces. You can either build graphically in Xcode&#39;s Interface Builder, or write all the interface in code. The &amp;quot;interface-in-code&amp;quot; lobby has always been very vocal and while I only ever did this for small sub-views, I can see part of the appeal. It&#39;s definitely better for source control as the interface is saved as text instead of in Xcode&#39;s strange XML that changes frequently for no apparent reason. But it&#39;s also a lot of code to write, and it doesn&#39;t give the interface positioning guidelines and the easy access to interface element properties that Interface Builder provides. However, unlike SwiftUI, both these methods give you pixel perfect control over the positioning, even if AutoLayout frequently drove me to distraction.&lt;/p&gt;
&lt;p&gt;SwiftUI takes a very different approach and to some extent, it takes the power away from the developer and insists that Apple knows best. This means that when designing apps for different platforms, you don&#39;t have to remember the differences between the AppKit and UIKit versions of a control and you don&#39;t have to apply the different interface guidelines. SwiftUI applies styling and behavior for each platform, but this doesn&#39;t always give the result that people want and expect.&lt;/p&gt;
&lt;p&gt;Paulo Andrade wrote an article recently titled &lt;a href=&quot;https://pfandrade.me/blog/mac-assed-swiftui-app/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Using SwiftUI to build a Mac-assed App in 2026&lt;/a&gt;. He thoughtfully detailed several ways in which SwiftUI fails to create the Mac experience that we have learned to expect. A lot of his points were to do with how the app indicated selections, but he also covered toolbars, drag &amp;amp; drop and keyboard shortcuts. While I understand what he&#39;s saying, and agree that SwiftUI and AppKit provide different experiences, I would argue that the Mac experience has changed many times and in lots of ways over the years. We developers are familiar with the small details and notice the changes, but to most users, this is just how Mac software is now. I got my first Mac in 1985, so I may have a broader perspective on this than most people. I do agree with his point that the Mac doesn&#39;t seem to matter inside Apple these days, but I also think it&#39;s part of the general trend of Apple rushing to release the big yearly OS updates and not taking the time to test and polish them, which lowers the quality of all their software, not just macOS or SwiftUI.&lt;/p&gt;
&lt;h2&gt;AI&lt;/h2&gt;
&lt;p&gt;Critics of SwiftUI are using AI as an excuse to go back to AppKit or UIKit. There is no doubt that these older frameworks require a lot more boilerplate code, especially if you&#39;re designing the interfaces in code. The AI proponents argue that if AI is writing all this verbose code, then there is no need for the more concise SwiftUI. While I have started using AI to write some code, I still review every line and the more the AI writes, the harder this becomes and the more likely it is that there will be un-detected bugs.&lt;/p&gt;
&lt;p&gt;There&#39;s no doubt that right now, the various LLMs have had access to a lot more AppKit and UIKit code than they have SwiftUI code. This is purely a factor of the ages of the frameworks. But that doesn&#39;t mean that they can&#39;t write SwiftUI. You need to provide guidance, but that&#39;s true for any code generation.&lt;/p&gt;
&lt;h2&gt;SwiftUI Problems&lt;/h2&gt;
&lt;p&gt;I don&#39;t want to imply that I think SwiftUI is perfect. I have struggled with various aspects of it, and I have had to find workarounds for some things that I want to do. Partly, this is due to the buggy nature of Apple software at the moment, but as someone who primarily works with Mac apps, I am feeling the pain of the lack of attention to the Mac. Again, this is not a SwiftUI problem, but a general Apple problem.&lt;/p&gt;
&lt;p&gt;As an example, I have had to recommend that people building Mac apps avoid &lt;code&gt;NavigationSplitView&lt;/code&gt; completely. It is very buggy and doesn&#39;t work well with multi-window apps. My numerous feedbacks have been ignored except for one response telling me that I shouldn&#39;t assemble all my feedbacks on the topic into a single report!&lt;/p&gt;
&lt;p&gt;There are still some performance issues, particularly with long lists, but these can be worked around by dropping back to AppKit or UIKit. The ability to mix and match SwiftUI with UIKit and AppKit is a great feature, and it allows for gradual adoption of SwiftUI as well as filling in the gaps with AppKit or UIKit when needed.&lt;/p&gt;
&lt;h2&gt;My Plans&lt;/h2&gt;
&lt;p&gt;SwiftUI started off missing a lot of features that had to be added using UIKit or AppKit. This was a sensible approach as it allowed Apple to release it before it was fully featured, however with the addition of the SwiftUI web view last year, there are very few things that you can&#39;t do in SwiftUI now.&lt;/p&gt;
&lt;p&gt;My current strategy is &lt;strong&gt;ALWAYS&lt;/strong&gt; to start with SwiftUI. I find it much easier to write, read and maintain, and really appreciate the ease of passing data around the app.&lt;/p&gt;
&lt;p&gt;If I come across a bug in a SwiftUI component, I will try to work around it or see if I can use an alternative SwiftUI approach. If that doesn&#39;t work, I will drop back to AppKit or UIKit for that part of the app.&lt;/p&gt;
&lt;p&gt;For long lists, again I&#39;ll start using SwiftUI, but if I find performance issues, I&#39;ll drop back to AppKit for that part of the app.&lt;/p&gt;
&lt;p&gt;I can&#39;t see myself ever basing an app on AppKit or UIKit again, but I will continue to use them as needed to fill in the gaps and work around SwiftUI bugs.&lt;/p&gt;
&lt;p&gt;I ❤️ SwiftUI!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you have any feedback about this article, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>macOS Apprentice: 3rd Edition</title>
    <link href="https://troz.net/post/2026/macos-apprentice-update-3/" />
    <updated>2026-05-13T11:18:04Z</updated>
    <id>https://troz.net/post/2026/macos-apprentice-update-3/</id>
    <content type="html">&lt;p&gt;The third edition of &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS Apprentice&lt;/a&gt; has been released!&lt;/p&gt;
&lt;p&gt;If you&#39;re a beginner or near-beginner who wants to start learning Swift, SwiftUI and AppKit for building Mac apps, then this is the book for you.&lt;/p&gt;
&lt;p&gt;This edition has been updated for Swift 6, macOS 26 and Xcode 26. It now includes details on using the new Icon Generator as well as major changes in the chapter that deals with embedding an AppKit view in a SwiftUI app. All the code and screenshots have been updated to reflect changes in Xcode, Swift, SwiftUI and macOS.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://troz.net/images/books/maca_cover.png&quot; alt=&quot;Book Cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;macOS Apprentice is a series of multi-chapter tutorials where you&#39;ll learn about developing native macOS apps in Swift, using both SwiftUI — Apple&#39;s newest user interface technology — and AppKit — the venerable UI framework. Along the way, you&#39;ll learn several ways to execute Swift code and you&#39;ll build two fully featured apps from scratch.&lt;/p&gt;
&lt;p&gt;If you&#39;re new to macOS and Swift, or to programming in general, learning how to write an app can seem incredibly overwhelming.&lt;/p&gt;
&lt;p&gt;That&#39;s why you need a guide that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shows you how to write an app step-by-step.&lt;/li&gt;
&lt;li&gt;Uses tons of illustrations and screenshots to make everything clear.&lt;/li&gt;
&lt;li&gt;Guides you in a fun and easy-going manner.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&#39;ll start at the very beginning. The first section assumes you have little to no knowledge of programming in Swift. It walks you through installing Xcode and then teaches you the basics of the Swift programming language. Along the way, you&#39;ll explore several different ways to run Swift code, taking advantage of the fact that you&#39;re developing natively on macOS.&lt;/p&gt;
&lt;p&gt;macOS Apprentice doesn&#39;t cover every single feature of macOS; it focuses on the absolutely essential ones. Instead of just covering a list of features, macOS Apprentice does something much more important: It explains how all the building blocks fit together and what is involved in building real apps. You&#39;re not going to create quick example programs that demonstrate how to accomplish a single feature. Instead, you&#39;ll develop complete, fully-formed apps, while exploring many of the complexities and joys of programming macOS.&lt;/p&gt;
&lt;h3&gt;Contents&lt;/h3&gt;
&lt;p&gt;The book consists of four sections:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 1&lt;/strong&gt;: Install Xcode and learn the basics of programming in Swift. Experiment with several different ways to execute Swift code on your Mac.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 2&lt;/strong&gt;: Use SwiftUI to develop a word-guessing game called Snowman. Learn about data flow in SwiftUI, managing multiple windows, using charts and adding macOS-specific features such as toolbars and menus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 3&lt;/strong&gt;: There are still a number of tasks where AppKit works better than SwiftUI. In this section, build an AppKit app to browse movie data from IMDb, the online movie database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 4&lt;/strong&gt;: Add AppKit to your SwiftUI app and add SwiftUI to your AppKit app in order to add some finishing touches to both of the apps from the previous sections.&lt;/p&gt;
&lt;p&gt;You can read more details of the book contents in the &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/v3.0/chapters/v-introduction&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Introduction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are a more experienced iOS developer who wants to branch out into macOS development, then my previous book - &lt;a href=&quot;https://troz.net/books/macos_apps_step_by_step/&quot;&gt;macOS Apps Step by step&lt;/a&gt; - might be a better fit, although I think you could still learn a lot of Mac app tips and tricks from this one.&lt;/p&gt;
&lt;h3&gt;Where Can You Find the Resources?&lt;/h3&gt;
&lt;p&gt;You can read the book online at &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;kodeco.com&lt;/a&gt; as part of a Kodeco subscription. The introduction and the first chapter are free to read.&lt;/p&gt;
&lt;p&gt;All the code and extra materials for the book can be downloaded or cloned from &lt;a href=&quot;https://github.com/kodecocodes/maca-materials/tree/editions/3.0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For support, to ask questions or to report any errors, please go to the &lt;a href=&quot;https://forums.kodeco.com/c/books/macos-apprentice/107&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;forum&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Thanks to...&lt;/h3&gt;
&lt;p&gt;This book was made possible by an awesome team. There were a lot of people involved, but I want to give special thanks to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kodeco.com/u/rcritz&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Richard Critz&lt;/a&gt;, the wonderful editor who had the unenviable task of fixing my grammar.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kodeco.com/u/audrey&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Audrey Tam&lt;/a&gt; and &lt;a href=&quot;https://www.kodeco.com/u/ehabamer&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ehab Amer&lt;/a&gt; were the amazing tech editors who had to make sure that it all worked. Audrey also contributed the first chapter on installing and setting up Xcode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole team at &lt;a href=&quot;https://www.kodeco.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco&lt;/a&gt; was great, so a huge thanks to them all. I hope they enjoyed working on the book as much as I did! It&#39;s wonderful to have such a supportive group working to make my content as good as it can be.&lt;/p&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;I would love to hear from anyone who read the book, loved it, hated it, found an error or just wanted to say hello.&lt;/p&gt;
&lt;p&gt;You can contact me directly using any of the contact links at the bottom of this page or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Playing in the Mac App Sandbox</title>
    <link href="https://troz.net/post/2026/playing_mac_sandbox/" />
    <updated>2026-03-23T14:57:12Z</updated>
    <id>https://troz.net/post/2026/playing_mac_sandbox/</id>
    <content type="html">&lt;p&gt;Every app running on any Apple device, including a Mac, runs inside a sandbox by default. This keeps the app&#39;s data and settings isolated from other parts of the system so they cannot interfere with each other. As a Mac app developer, this is usually what you want, but sometimes the default sandbox settings do not allow your app to operate the way you need. In many cases you can work around this by adjusting the sandbox, but for some apps, you have to turn off sandboxing.&lt;/p&gt;
&lt;p&gt;In this article, I want to walk through what the sandbox does by default, how you can configure its settings for various tasks, when you need to turn it off, and why it&#39;s actually a good idea.&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;top&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#what-does-the-mac-app-sandbox-do&quot;&gt;What does the Mac App Sandbox do?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#app-sandbox-settings&quot;&gt;App Sandbox Settings&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#network&quot;&gt;Network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#hardware&quot;&gt;Hardware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#app-data&quot;&gt;App Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#file-access&quot;&gt;File Access&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#temporary-entitlements&quot;&gt;Temporary Entitlements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#when-you-must-turn-off-the-sandbox&quot;&gt;When You Must Turn Off the Sandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#why-use-the-sandbox-at-all&quot;&gt;Why Use the Sandbox At All?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a id=&quot;what-does-the-mac-app-sandbox-do&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;What does the Mac App Sandbox do?&lt;/h2&gt;
&lt;p&gt;In Xcode, create a new macOS app project that uses Swift and SwiftUI. Add a button that calls this function:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;saveTestString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; docsUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentsDirectory
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fileUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; docsUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendingPathComponent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;save_test.txt&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; saveString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&quot;
    This is a test.
    Where do you think it will save?
    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; saveString
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      to&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fileUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      atomically&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      encoding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first two lines construct a URL based on the &lt;strong&gt;Documents&lt;/strong&gt; directory. The function then tries to write a test string to this URL.&lt;/p&gt;
&lt;p&gt;So the question is: where does this file end up? If you were expecting it to be in your Documents folder, you&#39;d be disappointed.&lt;/p&gt;
&lt;p&gt;Run the app or refresh the SwiftUI Preview and click the button. Now let&#39;s go and find the file...&lt;/p&gt;
&lt;p&gt;In Finder, hold down &lt;strong&gt;Option&lt;/strong&gt; and open the &lt;strong&gt;Go&lt;/strong&gt; menu. Select &lt;strong&gt;Library&lt;/strong&gt; and when the window opens, locate and open the &lt;strong&gt;Containers&lt;/strong&gt; folder. My app is called Sandbox, so in Containers, I see a folder called &lt;strong&gt;Sandbox&lt;/strong&gt; that has a single folder called &lt;strong&gt;Data&lt;/strong&gt;. And here&#39;s where things get interesting:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/container_contents.png&quot; alt=&quot;Container folder contents&quot;&gt;&lt;/p&gt;
&lt;p&gt;The folders where the icons have a small black arrow at the bottom left are aliases to your actual Desktop, Downloads etc. The folders with the standard folder icon are specific to this app and they are stored inside the container so that the app can&#39;t over-write any other app&#39;s data. The default sandbox settings allow your app to read and write any files here.&lt;/p&gt;
&lt;p&gt;You should write any app data to these folders. Your app stores its settings in the &lt;strong&gt;Library/Preferences&lt;/strong&gt; folder inside this Data folder.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#top&quot;&gt;Back to top&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;app-sandbox-settings&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;App Sandbox Settings&lt;/h2&gt;
&lt;p&gt;To see the available settings for your app&#39;s sandbox, go back to Xcode and select the project at the top of the Project navigator (press &lt;strong&gt;Command-1&lt;/strong&gt; if this isn&#39;t visible). Next, click on the app target and select &lt;strong&gt;Signing &amp;amp; Capabilities&lt;/strong&gt; along the top to see several sections, including the one for the &lt;strong&gt;App Sandbox&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/sandbox_settings.png&quot; alt=&quot;Target sandbox settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;There are four groups of settings, so let&#39;s step through them:&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;network&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Network&lt;/h3&gt;
&lt;p&gt;You&#39;ll almost never need to change the &lt;strong&gt;Incoming Connections (Server)&lt;/strong&gt; option. This is for running a server that needs to accept connections from other devices.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Outgoing Connections (Client)&lt;/strong&gt; option is one you&#39;ll probably use a lot. As you would expect, if your app connects to the internet in any way, you need to enable this. But there is another use case that might surprise you.&lt;/p&gt;
&lt;p&gt;If your app contains a web view of any kind, you need to enable this option, &lt;strong&gt;even if the web view is only used to display local content&lt;/strong&gt; from inside your app.&lt;/p&gt;
&lt;p&gt;I added a &lt;code&gt;WebView&lt;/code&gt; and &lt;code&gt;WebPage&lt;/code&gt; to a new tab in my sample app and asked it to load a static HTML string. When I ran the app, nothing appeared in the web view and the console showed these unhelpful warnings:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/web_errors.png&quot; alt=&quot;WebKit warnings&quot;&gt;&lt;/p&gt;
&lt;p&gt;I think the one I have highlighted is the relevant one. When I enabled &lt;strong&gt;Outgoing Connections (Client)&lt;/strong&gt; and tried again, I got even more warnings but this time, the web view showed my content.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;hardware&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Hardware&lt;/h3&gt;
&lt;p&gt;The hardware section lets you enable any extra hardware features that your app needs access to. The only one I&#39;ve ever used is &lt;strong&gt;Printing&lt;/strong&gt;. The &lt;strong&gt;USB&lt;/strong&gt; option does not need to be checked to allow standard peripherals like keyboards and mice to work, but if your app needs to communicate with a USB device directly, then enable this.&lt;/p&gt;
&lt;p&gt;Similarly, for your app to record audio, take photos or connect to any Bluetooth devices directly, you need to enable the relevant options here.&lt;/p&gt;
&lt;p&gt;If you enable any of these, click the &lt;strong&gt;+ Capability&lt;/strong&gt; button and search for a new capability for the name that you can use to explain to the user why the app needs this access.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;app-data&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Data&lt;/h3&gt;
&lt;p&gt;Similar to the hardware settings, if your app needs to access the user&#39;s contacts, calendars or location, you need to enable that here.&lt;/p&gt;
&lt;p&gt;In an iOS app, this is done using Privacy settings in the &lt;strong&gt;Info.plist&lt;/strong&gt; where you have to give a reason for why your app wants access. For a Mac app, you enable the relevant option in the sandbox settings and the system will prompt the user for permission when needed. As with the hardware settings, look for a new capability that can tell the user why you need this access.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;file-access&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;File Access&lt;/h3&gt;
&lt;p&gt;Now we get to the really interesting section. Ignoring the first option for now, the other four options give access to specific folders on the users system. You can choose to give your app no access by selecting &lt;strong&gt;None&lt;/strong&gt; (the default), &lt;strong&gt;Read Only&lt;/strong&gt; access, or &lt;strong&gt;Read/Write&lt;/strong&gt; access to any of these folders.&lt;/p&gt;
&lt;p&gt;The top entry is the &lt;strong&gt;User Selected File&lt;/strong&gt; option. This lets your app access any files and folders that the user has specifically selected using a file dialog. Again, you can select &lt;strong&gt;Read Only&lt;/strong&gt; or &lt;strong&gt;Read/Write&lt;/strong&gt; access. But there are some oddities about this that need more explanation.&lt;/p&gt;
&lt;p&gt;Leaving this at the default of &lt;strong&gt;Read Only&lt;/strong&gt;, I&#39;m going to add to my sample app. I added another tab which has its own view – &lt;code&gt;OpenFilesDemo&lt;/code&gt;. This contains an &lt;code&gt;AsyncImage&lt;/code&gt; that loads an image from an optional URL and two buttons:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/image_reader_view.png&quot; alt=&quot;ImageReaderView&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;using-nsopenpanel&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Using NSOpenPanel&lt;/h4&gt;
&lt;p&gt;The first button uses AppKit to show a file dialog and uses the result to populate the image URL. Here&#39;s the function that it calls:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;selectAppKit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; panel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSOpenPanel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allowedContentTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Select any image file…&quot;&lt;/span&gt;&lt;/span&gt;

  imageUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;

  panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;begin &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; response &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      imageUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses AppKit&#39;s &lt;code&gt;NSOpenPanel&lt;/code&gt; configured to allow only image files to be selected. When the user selects a file, the URL is stored in &lt;code&gt;imageUrl&lt;/code&gt;. This optional URL is already connected to the &lt;code&gt;AsyncImage&lt;/code&gt;, so when it is set, the image will load. It&#39;s cleared before showing the panel to make sure that the image does a complete refresh, even if there&#39;s been an error or the same image is selected again. This method works perfectly and the selected image appears.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;using-fileimporter&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Using fileImporter&lt;/h4&gt;
&lt;p&gt;Now on to the SwiftUI technique that uses a &lt;code&gt;fileImporter&lt;/code&gt; modifier. First, set up a Boolean to toggle the visibility of the file importer:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showFileImporter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second of the buttons clears &lt;code&gt;imageUrl&lt;/code&gt; and sets this Boolean to true so that the file importer sheet appears. To create and configure the importer, I added this modifier to the view:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fileImporter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showFileImporter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  allowedContentTypes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    imageUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;failure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Image import failed: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As with the &lt;code&gt;NSOpenPanel&lt;/code&gt;, this restricts the user to selecting image files. The completion handler receives a &lt;code&gt;Result&lt;/code&gt; with the selected URL if successful or an error if something went wrong. Again, I assigned the selected URL to &lt;code&gt;imageUrl&lt;/code&gt; so that the image will load into the &lt;code&gt;AsyncImage&lt;/code&gt;. But this time, when I run the app and select an image, nothing appears. The console shows this error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/file_importer_warning.png&quot; alt=&quot;File importer warning&quot;&gt;&lt;/p&gt;
&lt;p&gt;As with so many Xcode warnings, this doesn&#39;t really help but the docs for &lt;code&gt;fileImporter&lt;/code&gt; are more useful. It turns out that &lt;code&gt;NSOpenPanel&lt;/code&gt; returns a usable URL but when you use &lt;code&gt;fileImporter&lt;/code&gt; inside a sandboxed app, it gives you a &lt;strong&gt;security-scoped bookmark&lt;/strong&gt; which you cannot use before requesting access from the system.&lt;/p&gt;
&lt;p&gt;I updated the &lt;code&gt;.success&lt;/code&gt; case to this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; gotAccess &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startAccessingSecurityScopedResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; gotAccess &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    imageUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now the image appears when using either button. However the Xcode docs are very insistent that every call to &lt;code&gt;startAccessingSecurityScopedResource()&lt;/code&gt; must be balanced with a call to &lt;code&gt;stopAccessingSecurityScopedResource()&lt;/code&gt; or your app will leak kernel resources. I&#39;m not sure what effect that will have but it sounds bad...&lt;/p&gt;
&lt;p&gt;Because I&#39;m using an &lt;code&gt;AsyncImage&lt;/code&gt;, I can&#39;t stop the access immediately after setting the URL because the image will not have finished loading and that would mean the URL became unavailable before it had been used. Instead, I added an &lt;code&gt;onAppear&lt;/code&gt; modifier to the &lt;code&gt;Image&lt;/code&gt; inside the &lt;code&gt;AsyncImage&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onAppear &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imageUrl&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stopAccessingSecurityScopedResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the &lt;code&gt;imageUrl&lt;/code&gt; property is cleared every time, this runs whenever a new image appears. It doesn&#39;t matter whether the URL is security-scoped or not – this works either way.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;saving-urls&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Saving URLs&lt;/h4&gt;
&lt;p&gt;Before moving on from this topic, there is one more issue that effects URLs no matter how they are selected and that&#39;s if you want to save them for re-use next time the app runs. Let&#39;s say we want to store the selected image URL and re-display that image when the app is launched again.&lt;/p&gt;
&lt;p&gt;In a non-sandboxed app, this is easy – change the &lt;code&gt;imageURL&lt;/code&gt; property from using &lt;code&gt;@State&lt;/code&gt; to &lt;code&gt;@AppStorage&lt;/code&gt; and it &lt;em&gt;just works&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// @State private var imageUrl: URL?&lt;/span&gt;
&lt;span class=&quot;token attribute atrule&quot;&gt;@AppStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;imageUrl&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imageUrl&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But with the sandbox in the way, this becomes a whole lot more complicated, so I reverted this change, re-sandboxed the app and tried again.&lt;/p&gt;
&lt;p&gt;You have to create a security-scoped bookmark from the URL and save that instead of saving the URL itself. First, I added an &lt;code&gt;@AppStorage&lt;/code&gt; property to hold the bookmark, which is &lt;code&gt;Data&lt;/code&gt; and not a &lt;code&gt;URL&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@AppStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;storedBookmark&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; storedBookmark&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I added a method to convert the URL to a bookmark and save it:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;saveBookmarkData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bookmarkData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bookmarkData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;withSecurityScope&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    storedBookmark &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bookmarkData
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses a URL method to convert the URL into bookmark data with the &lt;code&gt;.withSecurityScope&lt;/code&gt; option. If this succeeds, I store the bookmark data in &lt;code&gt;storedBookmark&lt;/code&gt; which is saved in &lt;code&gt;AppStorage&lt;/code&gt;. In a production app, I&#39;d put this inside a &lt;code&gt;do/catch&lt;/code&gt; block and handle any errors that might occur, but I&#39;m trying to keep the demo code as concise as possible.&lt;/p&gt;
&lt;p&gt;To use this, I amended the &lt;code&gt;onAppear&lt;/code&gt; for the &lt;code&gt;Image&lt;/code&gt; inside the &lt;code&gt;AsyncImage&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onAppear &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageUrl &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;saveBookmarkData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    imageUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stopAccessingSecurityScopedResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This checks for a valid &lt;code&gt;imageUrl&lt;/code&gt; and if found, saves the bookmark data and only then stops accessing the resource.&lt;/p&gt;
&lt;p&gt;The strange thing about this is that it only works if the sandbox setting for &lt;strong&gt;User Selected File&lt;/strong&gt; is set to &lt;strong&gt;Read/Write&lt;/strong&gt; instead of &lt;strong&gt;Read Only&lt;/strong&gt;. I don&#39;t see why this should be so, but it is.&lt;/p&gt;
&lt;p&gt;With this in place, I added a method to re-load the image from this stored bookmark:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;loadImageUrlFromBookmark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; storedBookmark &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isStale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    resolvingBookmarkData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; storedBookmark&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    options&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;withSecurityScope&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    relativeTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    bookmarkDataIsStale&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;isStale
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; gotAccess &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startAccessingSecurityScopedResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;gotAccess &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  imageUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; isStale &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;saveBookmarkData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is rather more complicated than the saving method!&lt;/p&gt;
&lt;p&gt;First, check for data in &lt;code&gt;storedBookmark&lt;/code&gt; and return if there isn&#39;t any. Then try to resolve this data back into a URL, using the security scope option that was used when saving the bookmark. The &lt;code&gt;resolvingBookmarkData&lt;/code&gt; method reports if the URL is &lt;strong&gt;stale&lt;/strong&gt; meaning that the file has moved or been deleted since you saved it. And again, this should be in a &lt;code&gt;do/catch&lt;/code&gt; block with error handling.&lt;/p&gt;
&lt;p&gt;If there is a URL, it&#39;s security scoped so try to start accessing it and if that fails, return. If it succeeds, assign the URL to &lt;code&gt;imageUrl&lt;/code&gt; so that the image will load. Finally, if the bookmark was stale, create a new bookmark from the updated URL and save it.&lt;/p&gt;
&lt;p&gt;To call this, I added a modifier to the &lt;code&gt;VStack&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onAppear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;perform&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; loadImageUrlFromBookmark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now we have a technique that saves and loads URLs in a sandboxed app – hurray!&lt;/p&gt;
&lt;p&gt;If you want to test the &lt;code&gt;isStale&lt;/code&gt; part, run the app and select an image. Then quit the app and move the image file to a different folder. When you run the app again, it will load the image from the new location which is pretty amazing, in my opinion.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#top&quot;&gt;Back to top&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;temporary-entitlements&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Temporary Entitlements&lt;/h3&gt;
&lt;p&gt;So far, I&#39;ve covered the standard features that you can enable as required inside the sandbox. There is another category called &lt;strong&gt;temporary entitlements&lt;/strong&gt;, which you can request in an entitlements file. Although these are listed as temporary, I think that&#39;s just so that Apple can feel free to shut them down at any time. They do not expire and have to be renewed.&lt;/p&gt;
&lt;p&gt;There&#39;s a rather old page in Apple&#39;s developer documentation that describes these: &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html#//apple_ref/doc/uid/TP40011195-CH5-SW1&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;AppSandboxTemporaryExceptionEntitlements.html&lt;/a&gt;. The most common one (and the only one I have ever used) is the one that requests file access to a particular folder. In my &lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader&lt;/a&gt; app, I search for &lt;code&gt;man&lt;/code&gt; pages in the &lt;code&gt;/opt/&lt;/code&gt; and &lt;code&gt;/usr/&lt;/code&gt; folders so I request read-only access to those two paths.&lt;/p&gt;
&lt;p&gt;To demonstrate this, I added another tab to the sample app with buttons that try to read from these folders using this method:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;readFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; folder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fileManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FileManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contents &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; fileManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contentsOfDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;atPath&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; folder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    filesList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; contents
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This time I am using a &lt;code&gt;do/catch&lt;/code&gt; so that I can see the errors, and when I test using the current sandbox settings, I get this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/file_permission_error.png&quot; alt=&quot;File permission error&quot;&gt;&lt;/p&gt;
&lt;p&gt;So now we need to add an entitlements file to the project.&lt;/p&gt;
&lt;p&gt;Apple has made this more difficult over the years. Previously, Xcode created an entitlements file for every project so it was there to be edited. Now it doesn&#39;t exist so you have to create it manually which is a multi-stage process.&lt;/p&gt;
&lt;p&gt;First, we have to add a new file to the project. Select the Sandbox folder in the Xcode project navigator – this is the second entry from the top and selecting it ensures your entitlements file will be where it should be. Use &lt;strong&gt;Command-N&lt;/strong&gt; to open the file template chooser and search for &lt;strong&gt;property&lt;/strong&gt; to find the &lt;strong&gt;Property List&lt;/strong&gt; template:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/plist_template.png&quot; alt=&quot;Property List template&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt; and change the name of the file to &lt;strong&gt;Sandbox.entitlements&lt;/strong&gt; – make sure you change the file extension from the default &lt;strong&gt;.plist&lt;/strong&gt; to &lt;strong&gt;.entitlements&lt;/strong&gt;. Un-check the target – this file will not be part of the target app bundle, it&#39;s used by Xcode when building the app:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/plist_save_settings.png&quot; alt=&quot;Entitlements file save settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now that the entitlements file has been created, we need to add the temporary exception for file access. Open the file and add a new array with the name &lt;code&gt;com.apple.security.temporary-exception.files.absolute-path.read-only&lt;/code&gt;. Then add two &lt;code&gt;String&lt;/code&gt; items to this array with the values &lt;code&gt;/usr/&lt;/code&gt; and &lt;code&gt;/opt/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In Property List edit mode, it will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/entitlements.png&quot; alt=&quot;Entitlements file in Property List edit mode&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you want to check this or prefer to edit as text, right-click on the file in the Project navigator and select &lt;strong&gt;Open As &amp;gt; Source Code&lt;/strong&gt; to see the XML:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;/span&gt;
&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;plist&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//Apple//DTD PLIST 1.0//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;plist&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;com.apple.security.temporary-exception.files.absolute-path.read-only&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;/usr/&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;/opt/&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;plist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final stage is to tell Xcode to use this entitlements file. Select the project at the top of the Project navigator, then select the app target and go to &lt;strong&gt;Build Settings&lt;/strong&gt;. Select &lt;strong&gt;All&lt;/strong&gt; at the top and then search for &lt;strong&gt;entitlements&lt;/strong&gt; to find the &lt;strong&gt;Code Signing Entitlements&lt;/strong&gt; entry.&lt;/p&gt;
&lt;p&gt;Double-click to the right of this entry to popup an edit box and type in &lt;strong&gt;Sandbox/Sandbox.entitlements&lt;/strong&gt; – this is the path to the entitlements file that we just created, relative to the project folder:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/build_settings.png&quot; alt=&quot;Build settings for entitlements&quot;&gt;&lt;/p&gt;
&lt;p&gt;The edit box doesn&#39;t show a cursor or any indication that you can edit it, but start typing and
it will work. Press Return to apply your change.&lt;/p&gt;
&lt;p&gt;Finally, run the app again, try to read the contents of the &lt;code&gt;/usr/&lt;/code&gt; or &lt;code&gt;/opt/&lt;/code&gt; folders and it works!&lt;/p&gt;
&lt;p&gt;I used the folders I needed access to as examples. You would need to specify exactly what temporary entitlements you need for your app. Also, when submitting to App Store review, tell them why you need these and the reviewers will be more likely to approve your app.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#top&quot;&gt;Back to top&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;when-you-must-turn-off-the-sandbox&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;When You Must Turn Off the Sandbox&lt;/h3&gt;
&lt;p&gt;So far, I&#39;ve shown how to make the sandbox work for you, by adjusting settings and using entitlements. But there are some cases where you just have to turn it off.&lt;/p&gt;
&lt;p&gt;To do this, go to &lt;strong&gt;Signing &amp;amp; Capabilities&lt;/strong&gt; and click the trash button at the top right of the &lt;strong&gt;App Sandbox&lt;/strong&gt; section. You can always add it back using the &lt;strong&gt;+ Capability&lt;/strong&gt; button if you change your mind later.&lt;/p&gt;
&lt;p&gt;In my experience, I have only had to turn off the sandbox when I needed to use command-line tools that are reading or writing files outside the sandbox. For example, in my &lt;a href=&quot;https://troz.net/books/macos_apps_step_by_step/&quot;&gt;macOS Apps Step by Step&lt;/a&gt; book, one of the apps uses the &lt;code&gt;sips&lt;/code&gt; command-line tool to edit image files. &lt;code&gt;sips&lt;/code&gt; needs to read and write files as specified by the user, but because it&#39;s operating outside the app, it doesn&#39;t inherit the sandbox permissions that the app has. In this case, I had to turn off sandboxing for the app.&lt;/p&gt;
&lt;p&gt;Your experience will differ but sometimes this is the only way to allow your app to do what you need.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#top&quot;&gt;Back to top&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;why-use-the-sandbox-at-all&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Why Use the Sandbox At All?&lt;/h3&gt;
&lt;p&gt;So after nearly 3,000 words on how to work around all the sandbox limitations, you&#39;re probably wondering if it&#39;s worth the effort. Why not just turn it off and make everything easier?&lt;/p&gt;
&lt;p&gt;There are two main reasons to use the sandbox, even if it causes some extra work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Only sandboxed apps can be distributed through the Mac App Store. This is not a deal-breaker because Mac apps can be distributed outside the App Store, but it&#39;s something to consider.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sandboxing is a security feature that protects your app and your users. With your data and settings confined to their own container, there is little chance of any interference between your app, the system, and any other apps, including any malware.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Apple is tightening the restrictions on what apps can be installed and run on a Mac by default. Even if you decide not to sandbox your app, I strongly recommend that you sign it and get Apple to notarize it, so that your users can install and run it without having to put up with scary warnings and obscure activation steps. This doesn&#39;t require sandboxing but does require an Apple Developer account and the app&#39;s &lt;strong&gt;Signing &amp;amp; Capabilities&lt;/strong&gt; settings must include the &lt;strong&gt;Hardened Runtime&lt;/strong&gt; capability.&lt;/p&gt;
&lt;p&gt;My recommendation is to start every project with the sandbox enabled and using all the default settings. Enable extra permissions only as you need them. Add entitlements if the settings do not allow what you need. Only turn off sandboxing if there is no other way to make your app work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2026/playing_mac_sandbox/#top&quot;&gt;Back to top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you have any feedback about this article, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All the sample code from this article is on &lt;a href=&quot;https://github.com/trozware/mac-app-sandbox&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; so download it and you&#39;ll see the rest of the code that was too long to fit in the article. Thanks to Stewart Lynch for his &lt;a href=&quot;https://github.com/StewartLynch/IconComposerLite&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Icon Composer Lite&lt;/a&gt; app which I used to create the sample app icon.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>NSTableView scroll bug in macOS Tahoe</title>
    <link href="https://troz.net/post/2026/appkit-table-scroll-bug-in-macos-tahoe/" />
    <updated>2026-01-31T09:39:37Z</updated>
    <id>https://troz.net/post/2026/appkit-table-scroll-bug-in-macos-tahoe/</id>
    <content type="html">&lt;p&gt;Recently. I was working with an AppKit app that uses an &lt;code&gt;NSTableView&lt;/code&gt; to display long list of items. While testing the app on macOS Tahoe, I noticed a bug in the scrolling behavior: when I scrolled down from the top, the content rows would scroll into the header, making the top messy and unreadable. This sort of overlapping and unreadable text is a &lt;em&gt;feature&lt;/em&gt; of the various OS 26s, but in this case, there wasn&#39;t a hint of transparency, so it looked like a bug to me.&lt;/p&gt;
&lt;p&gt;I did a lot of searching online, but couldn&#39;t find any references to this specific issue, so once I worked out the cause and a workaround, I thought I&#39;d share it here, for anyone else who encounters this issue.&lt;/p&gt;

&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;First, let me show you what happens. This is from the MovieTables app for an upcoming update to the &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS Apprentice&lt;/a&gt; book.&lt;/p&gt;
&lt;p&gt;When the table is scrolled to the top you can see the headers:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/table_top_scroll.png&quot; alt=&quot;Table view scrolling into header view&quot;&gt;&lt;/p&gt;
&lt;p&gt;After scrolling, it&#39;s a mess:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/table_overlap_scroll.png&quot; alt=&quot;Table view rows overlapping header view when scrolled&quot;&gt;&lt;/p&gt;
&lt;p&gt;This exact layout worked fine on earlier versions of macOS, but as is often the case with all the OS 26s, things that used to work no longer do.&lt;/p&gt;
&lt;h3&gt;Investigating the Problem&lt;/h3&gt;
&lt;p&gt;I tried many things to fix this: adjusting all the various settings for the table view, its clip view, the enclosing scroll view and the header view but nothing worked. I adjusted content offsets, turned on or off various clip settings and so on. I even tried manually adding constraints to pin the content inside the safe area. And as I already mentioned, I couldn&#39;t find anyone online who had encountered this problem.&lt;/p&gt;
&lt;p&gt;Finally, I decided to request code-level support using my Apple Developer account. Before starting that, I needed to create a stripped down sample app that demonstrated the problem. After using AI to generate a JSON file containing 500 sample data points (one thing AI is really good at), I added all the boilerplate code and setup needed to display data in an &lt;code&gt;NSTableView&lt;/code&gt; using a data source and delegate. I ran the app and ... it worked perfectly, with no overlapping text!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/test_table_1.png&quot; alt=&quot;Sample app working fine&quot;&gt;&lt;/p&gt;
&lt;p&gt;WHAT!!!&lt;/p&gt;
&lt;p&gt;Once I calmed down a bit, I realized that at least this meant that there had to be a solution. Now I had to find the difference between my main app and the sample app that was causing the problem.&lt;/p&gt;
&lt;h3&gt;Finding the Solution&lt;/h3&gt;
&lt;p&gt;It looked like nearly everything was identical in both tables. The only obvious difference was that my main app&#39;s table had 3 columns and the sample app only had 2. I modified the sample app to add another column and that changed nothing.&lt;/p&gt;
&lt;p&gt;Then I stopped looking at the table itself and considered the overall layout of the window. In the sample app, the table was the only view in the window&#39;s content view, so I set its Auto Layout constraints to make it fill the window completely. In my main app, I had left space for other views below and to the right of the table view. Could this be significant?&lt;/p&gt;
&lt;p&gt;Acting on the principal of only changing one experimental variable at a time, I added space on the right and set a background color for the view to make the table positioning clear. No change - the table still scrolled perfectly, with the content disappearing under the headers as expected:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/tableview_right_spacer.png&quot; alt=&quot;Table with space on right working fine&quot;&gt;&lt;/p&gt;
&lt;p&gt;I reversed that change and then added space below the table view. Bingo! ... The problem appeared!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/tableview_bottom_spacer.png&quot; alt=&quot;Table with space below showing content overlapping header when scrolled&quot;&gt;&lt;/p&gt;
&lt;p&gt;So that was the bug. If a table doesn&#39;t stretch from top to bottom of its view controller&#39;s content view, in macOS Tahoe, the content will scroll into the header.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: When you add an &lt;code&gt;NSTableView&lt;/code&gt; to a storyboard, it arrives wrapped in an &lt;code&gt;NSClipView&lt;/code&gt; which is itself wrapped in an &lt;code&gt;NSScrollView&lt;/code&gt;. The &lt;code&gt;NSScrollView&lt;/code&gt; is the view that actually gets positioned and sized using Auto Layout. That&#39;s the view that has to fill the entire height of the content view.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So now I had identified what caused the problem, but how to fix it? One possibility was to work out a different location for the view I wanted to add to the bottom of the window, so that the table could fill the view vertically, but I didn&#39;t want to do that. I wanted a workaround that would allow me to keep my existing layout.&lt;/p&gt;
&lt;p&gt;What if I added space above the table view as well as below it? I tried that, and it worked! The table scrolled correctly, with the content disappearing under the headers as expected.&lt;/p&gt;
&lt;p&gt;I ended up spacing the table view 1 point down from the top and 1 point in from the left of its enclosing view. The left spacing is not required but it makes the layout look more symmetrical. This prevents the bug from occurring and allows me to add the content I want underneath the table:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2026/table_scroll_fixed.png&quot; alt=&quot;Final working layout with spacing above and left of table view&quot;&gt;&lt;/p&gt;
&lt;p&gt;I don&#39;t think it looks as neat and clean as the original layout, but at least it works.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;If you&#39;re using an &lt;code&gt;NSTableView&lt;/code&gt; in an AppKit app running on macOS Tahoe, and you notice that the content scrolls into the header view, check the layout constraints of the &lt;code&gt;NSScrollView&lt;/code&gt; that contains the table. If it doesn&#39;t fill the entire height of its enclosing view, add a small amount of spacing above it to fix the problem.&lt;/p&gt;
&lt;p&gt;I discovered later that another option is to turn off the border for the &lt;code&gt;NSScrollView&lt;/code&gt;, but I prefer the spacing solution as I think the table looks better with a border.&lt;/p&gt;
&lt;p&gt;If anyone from Apple is reading this, I filed a feedback report about this and included a sample project for testing (FB21850950).&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you have any feedback about this article, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Moving from Process to Subprocess</title>
    <link href="https://troz.net/post/2025/process-subprocess/" />
    <updated>2025-11-27T00:12:50Z</updated>
    <id>https://troz.net/post/2025/process-subprocess/</id>
    <content type="html">&lt;p&gt;For many years, I&#39;ve used &lt;code&gt;Process&lt;/code&gt; to call Terminal commands from my macOS apps. &lt;code&gt;Process&lt;/code&gt; is an old technology, formerly known as &lt;code&gt;NSTask&lt;/code&gt;. It works, but it&#39;s complicated to set up and it can have issues. The Swift language team have now published a modern alternative called &lt;a href=&quot;https://github.com/swiftlang/swift-subprocess&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Subprocess&lt;/code&gt;&lt;/a&gt;. Since I&#39;m currently using &lt;code&gt;Process&lt;/code&gt; in my &lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader&lt;/a&gt; app and in my &lt;a href=&quot;https://troz.net/books/macos_apps_step_by_step/&quot;&gt;macOS Apps Step by Step&lt;/a&gt; book, I thought it was time to assess the new option and see if I should swap to it.&lt;/p&gt;

&lt;p&gt;I started by creating a sample project using the macOS App template. Then I added the package dependency by searching for &lt;code&gt;https://github.com/swiftlang/swift-subprocess&lt;/code&gt;. This also adds &lt;code&gt;swift-system&lt;/code&gt; which the ReadMe says &lt;em&gt;provides idiomatic interfaces to system calls and low-level currency types&lt;/em&gt;. Next, I removed the &lt;strong&gt;App Sandbox&lt;/strong&gt; in &lt;strong&gt;Target -&amp;gt; Signing &amp;amp; Capabilities&lt;/strong&gt;. Man Reader gets round the sandbox restrictions using temporary entitlements and security-scoped bookmarks, but I didn&#39;t want to complicate my tests with those. I set up my sample app with two tabs so I could directly compare &lt;code&gt;Subprocess&lt;/code&gt; and &lt;code&gt;Process&lt;/code&gt;. Each tab has the same buttons that run the same Terminal commands, but using the two different methods. I also switched the build settings to use &lt;strong&gt;Swift 6&lt;/strong&gt; as I am gradually adopting this in all my projects.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/subprocess_app.png&quot; alt=&quot;Subprocess Test App&quot;&gt;&lt;/p&gt;
&lt;p&gt;The first step was to try a simple command with no parameters. In &lt;strong&gt;SubprocessView.swift&lt;/strong&gt;, I imported the &lt;code&gt;Subprocess&lt;/code&gt; library, and set the &lt;strong&gt;whoami&lt;/strong&gt; button to call this method using a &lt;code&gt;Task&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runWhoami&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; output &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;whoami&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    output&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;output &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;no result from whoami&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is based on the first example in the Subprocess ReadMe. It uses &lt;code&gt;ls&lt;/code&gt; but I used &lt;code&gt;whoami&lt;/code&gt; in my book, so I changed to that. I resumed the Live Preview, clicked the button, and the Canvas console showed this, which I&#39;ve split on to two lines here for readability:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;CollectedResult&lt;StringOutput&lt;UTF8&gt;, DiscardedOutput&gt;(processIdentifier: 14711,
terminationStatus: exited(0), standardOutput: Optional(&quot;sarah&#92;n&quot;), standardError: ())&lt;/StringOutput&lt;UTF8&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are immediate benefits to using Subprocess. I didn&#39;t have to specify the full path to the &lt;code&gt;whoami&lt;/code&gt; command, and I didn&#39;t have to set up a pipe and a file handle to see the result. The type of &lt;code&gt;output&lt;/code&gt; is a &lt;code&gt;CollectedResult&lt;/code&gt; which contains the &lt;code&gt;processIdentifier&lt;/code&gt;, &lt;code&gt;terminationStatus&lt;/code&gt;, &lt;code&gt;standardOutput&lt;/code&gt; and &lt;code&gt;standardError&lt;/code&gt;. A &lt;code&gt;terminationStatus&lt;/code&gt; of 0 means the command completed successfully.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If I had wanted to use a particular version of &lt;code&gt;whoami&lt;/code&gt;, I could have specified the full path like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; output &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/usr/bin/whoami&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  output&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This option requires &lt;code&gt;import System&lt;/code&gt; in order to have access to the &lt;code&gt;FilePath&lt;/code&gt; type.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To get the output I wanted into a &lt;code&gt;String&lt;/code&gt;, I added:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; output&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standardOutput &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unknown&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;whoami: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next I wanted to test sending arguments. I added a new &lt;strong&gt;ping&lt;/strong&gt; button and set it to call this method:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runPing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; commandName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ping&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arguments &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;apple.com&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; output &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commandName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    arguments&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arguments&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    output&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;output&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standardOutput &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;no result from ping&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s when I found out that &lt;code&gt;arguments&lt;/code&gt; must be an array of &lt;code&gt;Arguments&lt;/code&gt;, not &lt;code&gt;Strings&lt;/code&gt;. This was solved by changing the second setup line to:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arguments &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;apple.com&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked, but there was no output for about 5 seconds, then it all appeared. Reading further down the Subprocess ReadMe I saw a way to stream the output as an &lt;code&gt;AsyncSequence&lt;/code&gt;, so after a bit of experimentation and some help from ChatGPT, I arrived at this &lt;code&gt;runPing&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runPing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; commandName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ping&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arguments &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;apple.com&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pingResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commandName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    arguments&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arguments
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; execution&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; standardOutput &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; line &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; standardOutput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trimmingCharacters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;whitespacesAndNewlines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; pingResult &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ping result = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;No result from ping&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s essential to &lt;code&gt;await pingResult&lt;/code&gt; or the method ends before any data arrives.&lt;/p&gt;
&lt;p&gt;This worked, but I was still getting the complete output at the end, and not seeing each line as it arrived. To help in my research, I selected &lt;strong&gt;Build Documentation&lt;/strong&gt; from the &lt;strong&gt;Product&lt;/strong&gt; menu which gave me the docs for &lt;code&gt;Subprocess&lt;/code&gt; in Xcode&#39;s Developer Documentation. There are a lot of custom types, but only one function - &lt;code&gt;run&lt;/code&gt; - which has 14 different versions. Seven of these variants expect an &lt;code&gt;Executable&lt;/code&gt;, which is what I&#39;ve been using when I specify a &lt;code&gt;.name&lt;/code&gt;. The others expect a &lt;code&gt;Configuration&lt;/code&gt; which is a way of combining an executable and its arguments plus other configuration details, into a single object.&lt;/p&gt;
&lt;p&gt;I also discovered that the versions that streamed data had a &lt;code&gt;preferredBufferSize&lt;/code&gt; setting. The docs say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Larger buffer sizes may improve performance for subprocesses that produce large amounts of output, while smaller buffer sizes may reduce memory usage and improve responsiveness for interactive applications.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I set the &lt;code&gt;preferredBufferSize&lt;/code&gt; to 32 and watched the lines come in one by one. It would be great if there was an option to buffer until a line feed, but this is workable.&lt;/p&gt;
&lt;p&gt;This basically covers how I use &lt;code&gt;Process&lt;/code&gt; in &lt;a href=&quot;https://troz.net/books/macos_apps_step_by_step/&quot;&gt;macOS Apps Step by Step&lt;/a&gt;, so now on to what &lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader&lt;/a&gt; needs. It uses &lt;code&gt;find&lt;/code&gt; to search for man pages, &lt;code&gt;man&lt;/code&gt; with a &lt;code&gt;-w&lt;/code&gt; argument to find the path to a man page, and &lt;code&gt;mandoc&lt;/code&gt; with &lt;code&gt;col&lt;/code&gt; to get the formatted page data. This adds two new challenges: &lt;code&gt;find&lt;/code&gt; will return a &lt;em&gt;lot&lt;/em&gt; of data and &lt;code&gt;mandoc&lt;/code&gt; needs to pipe its output to &lt;code&gt;col&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Starting with &lt;code&gt;find&lt;/code&gt;, Man Reader searches the &lt;code&gt;/opt&lt;/code&gt; and &lt;code&gt;/usr&lt;/code&gt; directories by default. On my system, &lt;code&gt;/opt&lt;/code&gt; contains 32504 man pages and &lt;code&gt;/usr&lt;/code&gt; has 2979. For both of these, I was able to use the basic form of &lt;code&gt;run&lt;/code&gt;, but I set the output string limit to &lt;code&gt;Int.max&lt;/code&gt;. This is &lt;em&gt;way&lt;/em&gt; more than necessary, but better to be safe.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;findManPages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; directory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; commandName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;find&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arguments &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;directory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-path&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;*/man/*.*&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commandName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arguments&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arguments&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; output &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;max&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; output&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standardOutput &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pages &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;separatedBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;newlines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;found &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;pages&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; man pages in &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;string length = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;error finding man pages in &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This time, I used a &lt;code&gt;Configuration&lt;/code&gt; to assemble the command and its arguments, then passed that to the appropriate version of &lt;code&gt;run&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I tested streaming the results, but that was more CPU intensive, even when using the default &lt;code&gt;preferredBufferSize&lt;/code&gt;. I added some timing routines so I could compare it to the &lt;code&gt;Process&lt;/code&gt; version. On my Mac, searching &lt;code&gt;/usr&lt;/code&gt; takes between 400 and 500 milliseconds, and searching &lt;code&gt;/opt&lt;/code&gt; takes between 1.2 and 2.5 seconds. Since this is all async code, the app remains totally responsive during this time.&lt;/p&gt;
&lt;p&gt;The last problem I needed to solve was how to pipe the output from one command to another. When using &lt;code&gt;Process&lt;/code&gt;, this is surprisingly easy - you create a pipe and set it as the &lt;code&gt;standardOutput&lt;/code&gt; for the first command. Then you set the same pipe as the &lt;code&gt;standardInput&lt;/code&gt; for the second command.&lt;/p&gt;
&lt;p&gt;Checking the &lt;code&gt;run&lt;/code&gt; options, I can see that some variants have an &lt;code&gt;input&lt;/code&gt; parameter. So if I get the output from the first command, I can set it as the input for the second. The command I want to run is:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;mandoc &lt;span class=&quot;token parameter variable&quot;&gt;-T&lt;/span&gt; ascii /usr/share/man/man1/ls.1 &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; col &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s what I came up with. This has the page to the &lt;code&gt;ls&lt;/code&gt; man page hard-coded for testing:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runPipedCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; manPagePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/usr/share/man/man1/ls.1&quot;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; commandName1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mandoc&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arguments1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-T&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ascii&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; manPagePath&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; commandName2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;col&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arguments2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-b&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; output1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commandName1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    arguments&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arguments1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    output&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; outputString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; output1&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standardOutput &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;no output from &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;commandName1&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Uncomment the next two lines to see what happens without `col`&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//    print(outputString)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//    return&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; output2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commandName2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    arguments&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arguments2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    input&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    output&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; output2&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standardOutput &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;no output from &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;commandName2&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to see why the &lt;code&gt;col -b&lt;/code&gt; part is essential, uncomment the two marked lines and you&#39;ll see a result that starts like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/mandoc_no_col.png&quot; alt=&quot;mandoc wihout col&quot;&gt;&lt;/p&gt;
&lt;p&gt;The second tab has the same buttons with the same commands, but they run using &lt;code&gt;Process&lt;/code&gt;. For the &lt;code&gt;find&lt;/code&gt; command, the times are much the same, but I have to stream the incoming data as I did with &lt;code&gt;pipe&lt;/code&gt;. Without this, the app freezes and the process never completes.&lt;/p&gt;
&lt;p&gt;After these experiments, I &lt;strong&gt;will&lt;/strong&gt; be moving to &lt;code&gt;Subprocess&lt;/code&gt; in both the book and the app. Over the years I have added many tweaks, fail-safes, and workarounds to handle all the things that can go wrong with &lt;code&gt;Process&lt;/code&gt;, but it will be great to use a more modern API without these issues. Sadly, I can&#39;t use it everywhere. I have a client app that uses &lt;code&gt;Process&lt;/code&gt; but it has to support systems back to macOS 11 and it looks like&lt;code&gt;Subprocess&lt;/code&gt; only goes back to macOS 13.&lt;/p&gt;
&lt;p&gt;My sample app is available on GitHub: &lt;a href=&quot;https://github.com/trozware/subprocess-tests&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;https://github.com/trozware/subprocess-tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have any feedback about this article, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Black Friday Sale</title>
    <link href="https://troz.net/post/2025/black_friday_2025/" />
    <updated>2025-11-23T22:08:15Z</updated>
    <id>https://troz.net/post/2025/black_friday_2025/</id>
    <content type="html">&lt;p&gt;For Black Friday 2025, my self-published books: &lt;a href=&quot;https://sarahreichelt.gumroad.com/l/oximx/4xfw8q6&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS Apps Step by Step&lt;/a&gt; and &lt;a href=&quot;https://sarahreichelt.gumroad.com/l/iqdry/4xfw8q6&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Escape from Tutorial Hell&lt;/a&gt; are on sale for 30% off, from now until December 1st.&lt;/p&gt;
&lt;p&gt;If you&#39;re looking for a discount on &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS Apprentice&lt;/a&gt;, check it out the Kodeco web site when their Black Friday sale is live.&lt;/p&gt;

&lt;hr&gt;
&lt;h2&gt;macOS Apps Step by Step&lt;/h2&gt;
&lt;p&gt;If you&#39;re an iOS developer who wants to branch out into creating native macOS apps, this is the book for you!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/books/macos_apps_step_by_step/&quot;&gt;&lt;img src=&quot;https://troz.net/images/2025/mac_apps_cover_square.png&quot; alt=&quot;macOS Apps book cover&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click the book cover from more information, and if you&#39;d like to check out the start of the book including the table of contents and the first chapter, you can read it online at &lt;a href=&quot;https://troz.net/books/mac_apps_sample.html&quot;&gt;macOS Apps Step by Step Sample&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For anyone who previously bought the book from Kodeco when it was still called &lt;strong&gt;macOS by Tutorials&lt;/strong&gt;, please &lt;a href=&quot;mailto:books@troz.net?subject=macOS%20by%20Tutorials%20Discount&quot;&gt;email me&lt;/a&gt; for a 50% discount code.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Escape from Tutorial Hell&lt;/h2&gt;
&lt;p&gt;I wrote this book for people who have done some tutorials, read books, watched videos, and who know some Swift and SwiftUI but are finding it difficult to make the leap from there to working on their own apps. It&#39;s also for more experienced developers who want to learn how to design and structure their projects to make them easier to maintain.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/books/escape/&quot;&gt;&lt;img src=&quot;https://troz.net/images/2025/escape_cover_square.png&quot; alt=&quot;Escape book cover&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click the book cover from more information, or read the start of the book including the table of contents and the first chapter at &lt;a href=&quot;https://troz.net/books/escape_sample.html&quot;&gt;Escape from Tutorial Hell Sample&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>macOS Apps Step by Step 4.0</title>
    <link href="https://troz.net/post/2025/macos_tutorials_4/" />
    <updated>2025-11-05T23:44:23Z</updated>
    <id>https://troz.net/post/2025/macos_tutorials_4/</id>
    <content type="html">&lt;p&gt;macOS by Tutorials has a new name and a new edition!&lt;/p&gt;
&lt;p&gt;The book is available for purchase or update at &lt;a href=&quot;https://sarahreichelt.gumroad.com/l/oximx&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gumroad&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS Apps Step by Step&lt;/strong&gt; was previously titled &lt;strong&gt;macOS by Tutorials&lt;/strong&gt; and was released in 2022 by &lt;a href=&quot;https://www.kodeco.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco&lt;/a&gt;. Since they transferred the rights to me, I have released two updates under the same name, but now it feels like time to move on from the Kodeco naming convention and give it a name that is more part of my brand. After considering several options, I put a &lt;a href=&quot;https://mastodon.social/@troz/115365045162246609&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;poll on Mastodon&lt;/a&gt; and &lt;strong&gt;macOS Apps Step by Step&lt;/strong&gt; was the winner.&lt;/p&gt;
&lt;p&gt;If you bought the first edition of macOS by Tutorials from either Kodeco or Amazon, please &lt;a href=&quot;mailto:books@troz.net?subject=macOS%20Apps%20Step%20by%20Step%20Discount&quot;&gt;email me&lt;/a&gt; for a 50% discount code.&lt;/p&gt;
&lt;p&gt;If you bought a previous edition of macOS by Tutorials from me via Gumroad, version 4 is a free update that you can download from your &lt;a href=&quot;https://gumroad.com/library&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gumroad library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://troz.net/books/macos_apps_step_by_step/&quot;&gt;&lt;img src=&quot;https://troz.net/images/2025/mac_apps_cover_horizontal.png&quot; alt=&quot;Book cover&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click the image for more details, and if you’d like to check out the start of the book including the table of contents and the first chapter, you can read it online at &lt;a href=&quot;https://troz.net/books/mac_apps_sample.html&quot;&gt;macOS Apps Step by Step Sample&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The major changes in this fourth edition include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new title!&lt;/li&gt;
&lt;li&gt;All projects updated to include new features introduced in macOS 26 Tahoe and Xcode 26.&lt;/li&gt;
&lt;li&gt;All projects use &lt;em&gt;Swift 5 or 6&lt;/em&gt; with &lt;em&gt;Approachable Concurrency&lt;/em&gt;. Swift 5 is still the default in Xcode but I switched all the projects to Swift 6 and made sure that all the code works for both versions.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;On This Day&lt;/em&gt; app has been re-designed to use a more stable &lt;code&gt;HSplitView&lt;/code&gt; instead of the problematic &lt;code&gt;NavigationSplitView&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Icons are created using Apple&#39;s Icon Composer app.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;MarkWriter&lt;/em&gt; app uses SwiftUI&#39;s &lt;code&gt;WebView&lt;/code&gt; to display HTML content and adds the ability to work with and edit selected text.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you already own the book and have enjoyed it or found it useful, I&#39;d really appreciate it if you could leave a rating or review at the book&#39;s page in your &lt;a href=&quot;https://gumroad.com/library&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gumroad library&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Apple Has Two Problems</title>
    <link href="https://troz.net/post/2025/apple-has-two-problems/" />
    <updated>2025-10-28T02:01:52Z</updated>
    <id>https://troz.net/post/2025/apple-has-two-problems/</id>
    <content type="html">&lt;p&gt;Over the last few years, Apple Inc. has come in for its fair share of criticism. In my opinion, this boils down to two main problems: politics and software quality.&lt;/p&gt;
&lt;p&gt;The company has lost its way several times over the years but it has always kept going and eventually bounced back. Now it&#39;s a massive company with more money than any human can imagine, and it&#39;s losing its way again. What can they do to bounce back this time?&lt;/p&gt;

&lt;p&gt;The reason I care is because I love using Apple&#39;s products. I fell in love with the very first Macintosh, way back in 1984. It was many years before I was able to afford one, but I always admired them. The Mac is the computer I use every day and it&#39;s the computer I want to keep using. The iPhone is my principal communication device and the iPad is my entertainment center. I want to see Apple get back to being a company that I can feel good about buying from, with products that I want to use and that work as expected.&lt;/p&gt;
&lt;h2&gt;Politics&lt;/h2&gt;
&lt;p&gt;The first and most obvious problem is Tim Cook&#39;s personal and corporate funding and encouragement of the current US administration. This started with Cook&#39;s contribution to the inauguration and has continued through various cringe-making events to the stage where Apple is paying to assist in the legally suspect demolition of a piece of America&#39;s heritage.&lt;/p&gt;
&lt;p&gt;Apologists say that Cook is doing this to avoid tariffs on Apple products, but what is the point in being one of the richest companies in the world if you don&#39;t use this power to stand up to bullies? People with $40 frog costumes can do it - why can&#39;t Apple?&lt;/p&gt;
&lt;p&gt;As everyone who has ever been bullied knows, giving the bully what they want is only ever a temporary solution. They respect you even less for giving in and will just keep asking for more. At some point, one has to wonder if Tim Cook is doing this reluctantly because he feels it best for Apple, or if he really is a full-on MAGA supporter.&lt;/p&gt;
&lt;p&gt;The second part of the political problem is the way Apple deals with the EU. The company has been very quick to cooperate with authoritarian governments around the world and at home. If you want a good list (which makes for very depressing reading) check out Jesse Squires&#39; recent post: &lt;a href=&quot;https://www.jessesquires.com/blog/2021/03/30/apple-cooperation-with-authoritarian-governments/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Apple&#39;s cooperation with authoritarian governments&lt;/a&gt;. But when the democratically elected European Parliament passes a law requiring Apple to do anything, they suddenly descend into petulant toddler mode and lie on the floor screaming. When eventually forced to comply, they do it with extremely bad grace and as poorly as possible, so as to give the worst possible result.&lt;/p&gt;
&lt;p&gt;While I understand that a lot of politicians are not well enough informed about tech matters to legislate well, has it occurred to anyone at Apple to act like an adult and negotiate honestly and in good faith?&lt;/p&gt;
&lt;h3&gt;The Solution&lt;/h3&gt;
&lt;p&gt;The solution to this problem is that Tim Cook must go. I don&#39;t know how far down the rot goes and how many other Apple execs also need to go, but ditching Cook is an essential first step. I haven&#39;t any suggestions as to who should replace him, but I would suggest looking for an external hire - maybe even one from outside the US who doesn&#39;t believe that companies have the same rights as people.&lt;/p&gt;
&lt;h2&gt;Software Quality&lt;/h2&gt;
&lt;p&gt;The ever decreasing quality of Apple software is more of an existential threat to Apple. Cook will eventually go, and presumably democracy will return to the US, but if Apple software becomes more difficult to use and more buggy than Windows or Linux, they will lose customers who will then not want to risk coming back.&lt;/p&gt;
&lt;p&gt;Apple Silicon is incredible. They appear to be designing and making the best chips in the world right now. I haven&#39;t seen any real-world benchmarks for the new M5 chip, but I&#39;m sure it&#39;s another huge advance.&lt;/p&gt;
&lt;p&gt;Apple hardware is pretty good. They make some odd design choices, like the power button underneath the Mac mini and the iPhone Air, but the overall quality is good. Even the Apple Vision Pro is a great piece of hardware. Like many others, I tested it in the local Apple Store and was blown away. If it was half the price, I might have actually spent a few minutes considering buying, but at that price, for what is essentially a media watching device, it is way too expensive for me.&lt;/p&gt;
&lt;p&gt;It&#39;s the software that lets down the hardware and the quality has been going downhill for some years now. We are way out of the &amp;quot;it just works&amp;quot; phase of using Apple products. I consider myself an expert and I have great trouble accomplishing some tasks.&lt;/p&gt;
&lt;p&gt;The latest versions of all the operating systems seem particularly buggy, even if one ignores the obvious readability problems of Liquid Glass. Apple is a company that has always prided itself on its commitment to accessibility, so how they got this so wrong is a mystery. It seems like they wanted a major redesign just for the sake of it, and were prepared to put up with a huge reduction in usability in order to look different.&lt;/p&gt;
&lt;p&gt;Then we get to the fiasco of Apple Intelligence which is sounding more like an oxymoron every day. First, Apple showed us vapor-ware at WWDC 24. Then they used this mythical system to advertise the iPhone 16 - at least at first. Finally, they did a partial implementation which is embarrassingly bad. It sounds like a great idea to have Mail and Messages show summaries, but when the summaries make stuff up or reverse the meaning, this is much worse than no summary. And when you can&#39;t trust the summary, it becomes totally pointless.&lt;/p&gt;
&lt;p&gt;In the developer world, we were promised a two stage roll-out. First predictive code completion in Xcode and second, Swift Assist. The predictive code completion has been released, but I&#39;ve had to turn it off. I don&#39;t know what language it was trained on, but it obviously wasn&#39;t Swift! Despite having all the docs and APIs there in Xcode, it suggests functions that just don&#39;t exist. Or if they exist, it suggests incorrect parameters. This has been incredibly disappointing. Apple has a captive audience using Xcode and with all their resources, it
could have been great.&lt;/p&gt;
&lt;p&gt;Swift Assist never appeared. Apple gave up on this and instead integrated ChatGPT into Xcode. I&#39;ve tried it a few times and once or twice it has even worked. The biggest problem is that Swift is still a rapidly developing language and ChatGPT is not up-to-date with the latest changes. So it&#39;s not much use for Swift development - at least not yet.&lt;/p&gt;
&lt;h3&gt;The Solution&lt;/h3&gt;
&lt;p&gt;Unlike the first problem, this one has a multi-stage solution:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move Craig Federighi from software to marketing. He seems like a really nice person and he makes great videos, but he is not managing the software teams.&lt;/li&gt;
&lt;li&gt;Fire Alan Dye and hire a team of UI, UX and accessibility experts to fix OS26.&lt;/li&gt;
&lt;li&gt;Instead of penalizing engineers for spending time on tests and QA instead of new features, &lt;em&gt;insist&lt;/em&gt; that they do.&lt;/li&gt;
&lt;li&gt;Give up on the yearly OS release cycle and give the Apple engineers time to fix things.&lt;/li&gt;
&lt;li&gt;Use the developer community which is full of people who want to help, but are sick of being treated like the enemy. I&#39;ve written before about &lt;a href=&quot;https://troz.net/post/2025/apple_dev_rel/&quot;&gt;Apple&#39;s developer relations&lt;/a&gt; and nothing has changed since then.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So there it is - my opinion of what is wrong at Apple and what needs to be done to fix it. If you have any other ideas or comments, I&#39;d love to hear them. Please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article interesting, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI WebView</title>
    <link href="https://troz.net/post/2025/swiftui-webview/" />
    <updated>2025-08-15T11:11:18Z</updated>
    <id>https://troz.net/post/2025/swiftui-webview/</id>
    <content type="html">&lt;p&gt;At WWDC 2025, Apple announced that SwiftUI would now have its own &lt;code&gt;WebView&lt;/code&gt;. I touched on this briefly in my &lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#web-view&quot;&gt;SwiftUI for Mac 2025&lt;/a&gt; article, but this view has a lot of features that I wanted to explore and document.&lt;/p&gt;
&lt;p&gt;My primary source was the WWDC video: &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2025/231&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Meet WebKit for SwiftUI&lt;/a&gt; but as usual, there is a lot of detail hidden in the video and some of the sample code doesn&#39;t work in the later betas. I&#39;m currently using macOS Tahoe 26 beta 7 and Xcode 26 beta 6.&lt;/p&gt;

&lt;p&gt;I&#39;ve written a sample app demonstrating various aspects of the new &lt;code&gt;WebView&lt;/code&gt;, which you can download from &lt;a href=&quot;https://github.com/trozware/swiftui-webview&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. This will let you follow along with my descriptions below. The numbered sections in this article correspond to the files in the &lt;strong&gt;WebView Samples&lt;/strong&gt; folder of the project.&lt;/p&gt;
&lt;h3&gt;1. WebURLView&lt;/h3&gt;
&lt;p&gt;The simplest way to use a &lt;code&gt;WebView&lt;/code&gt; is to provide it with a URL:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://www.swift.org&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The URL is optional, so there&#39;s no need to force-unwrap it.&lt;/p&gt;
&lt;p&gt;This is easy to use but doesn&#39;t allow any progress tracking or customization so I think I would rarely choose this option.&lt;/p&gt;
&lt;h3&gt;2. WebPageLoad&lt;/h3&gt;
&lt;p&gt;In the next example, I created a &lt;code&gt;WebPage&lt;/code&gt; and used it to populate the &lt;code&gt;WebView&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Toolbar buttons switch between loading an online page:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://troz.net&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attribution &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user
page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or loading a local HTML string:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;html&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; html&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; baseURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bundle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resourceURL&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Apple&#39;s examples, they always use &lt;code&gt;URL(string: &amp;quot;about:blank&amp;quot;)!&lt;/code&gt; for the &lt;code&gt;baseURL&lt;/code&gt;, but using &lt;code&gt;Bundle.main.resourceURL!&lt;/code&gt; lets me include a link to a stylesheet that&#39;s inside the app bundle:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/web_page.png&quot; alt=&quot;Web page showing local HTML&quot;&gt;&lt;/p&gt;
&lt;h3&gt;3. TrackLoad&lt;/h3&gt;
&lt;p&gt;In the WWDC video, they demonstrated how to track the navigation events. The code in the video does not work - it doesn&#39;t even compile. But after some trial and error, I worked out how to track these events.&lt;/p&gt;
&lt;p&gt;When the view first appears, a &lt;code&gt;task&lt;/code&gt; starts monitoring the events:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;task &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;startObservingEvents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My event tracker method uses the new &lt;code&gt;Observations&lt;/code&gt; sequence to read an async stream of page navigation events:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;startObservingEvents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; eventStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Observations&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigations &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; observation &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; eventStream &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; event &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; observation &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; event &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startedProvisionalNavigation&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          statusText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Started provisional navigation for &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;absoluteString &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unknown URL&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;receivedServerRedirect&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Received server redirect&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;committed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Committed&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;finished&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Finished&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attribute atrule&quot;&gt;@unknown&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Unknown navigation event&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NavigationError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;failedProvisionalNavigation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error: Failed provisional navigation: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NavigationError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pageClosed &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error: Page closed&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NavigationError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webContentProcessTerminated &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error: Web content process terminated&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      statusText &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Unknown error: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#92;n&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This detects the &lt;code&gt;WebPage.NavigationEvent&lt;/code&gt; types: &lt;code&gt;startedProvisionalNavigation&lt;/code&gt;, &lt;code&gt;receivedServerRedirect&lt;/code&gt;, &lt;code&gt;committed&lt;/code&gt; and &lt;code&gt;finished&lt;/code&gt;. Each of the possible thrown &lt;code&gt;WebPage.NavigationError&lt;/code&gt; events is also monitored.&lt;/p&gt;
&lt;p&gt;This seems like overkill for most use cases, but this code shows how to set it up. The key is that &lt;code&gt;page.navigations&lt;/code&gt; is an &lt;code&gt;AsyncSequence&amp;lt;WebPage.NavigationEvent, any Error&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/web_track.png&quot; alt=&quot;Tracking events&quot;&gt;&lt;/p&gt;
&lt;p&gt;As an added extra, this example uses the value of &lt;code&gt;page.isLoading&lt;/code&gt; to show or hide a &lt;code&gt;ProgressView&lt;/code&gt;. This is what I&#39;ll mostly use for tracking loads and providing user feedback during a load. If you want to get fancy, you can provide the &lt;code&gt;ProgressView&lt;/code&gt; with a &lt;code&gt;value&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isLoading &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;ProgressView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;estimatedProgress&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This changes it from an indeterminate spinner to a progress bar.&lt;/p&gt;
&lt;h3&gt;4. CustomScheme&lt;/h3&gt;
&lt;p&gt;If you want your &lt;code&gt;WebView&lt;/code&gt; to load custom pages, create a custom scheme. In my &lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader&lt;/a&gt; app, I use a custom scheme to load HTML versions of man pages, so I decided to try something similar here.&lt;/p&gt;
&lt;p&gt;First, you create your scheme and then a scheme handler that actually provides the data for the web view to display. The web page&#39;s configuration ties these two together.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; scheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;manpage&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; handler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ManPageSchemeHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; configuration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
configuration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;urlSchemeHandlers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;scheme&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; handler

page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  configuration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; configuration
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My scheme handler struct conforms to &lt;code&gt;URLSchemeHandler&lt;/code&gt; and has the required &lt;code&gt;reply(for:)&lt;/code&gt; method to process the request. It tries to read the relevant file from the app bundle and if the file exists, uses its data to create a &lt;code&gt;URLResponse&lt;/code&gt; and then to emit the response and the file data or any error in an &lt;code&gt;AsyncSequence&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ManPageSchemeHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSchemeHandler&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLRequest&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AsyncSequence&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;URLSchemeTaskResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; any &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;AsyncThrowingStream&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; continuation &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bundleURL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bundle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forResource&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;host&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; withExtension&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentsOf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bundleURL&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        continuation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;throwing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;badURL&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        mimeType&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text/html&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        expectedContentLength&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pageData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        textEncodingName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      continuation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      continuation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pageData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      continuation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, when I try to open &lt;code&gt;manpage://cal.html&lt;/code&gt;, this reads the &lt;strong&gt;cal.html&lt;/strong&gt; file from the bundle, uses it&#39;s URL and length to create a &lt;code&gt;URLResponse&lt;/code&gt; and then yields the response and the file data before finishing the stream.&lt;/p&gt;
&lt;p&gt;If there&#39;s a problem with the file, the stream finishes by throwing an error.&lt;/p&gt;
&lt;p&gt;This example also uses &lt;code&gt;WebPage.NavigationDeciding&lt;/code&gt; to work out what to do with other schemes. Back in the initial setup phase, I created a decider and provided it to the &lt;code&gt;WebPage&lt;/code&gt;, along with the configuration.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; navigationDecider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NavigationDecider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  configuration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; configuration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  navigationDecider&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; navigationDecider
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My decider class conforms to &lt;code&gt;WebPage.NavigationDeciding&lt;/code&gt; and has a &lt;code&gt;decidePolicy&lt;/code&gt; method that checks the supplied URL and works out what to do with it:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NavigationDecider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NavigationDeciding&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;decidePolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NavigationAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    preferences&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;inout&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NavigationPreferences&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WKNavigationActionPolicy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;No URL supplied for decision&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cancel
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scheme &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;manpage&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Opening man page for &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allow
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Opening &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; in default browser&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NSWorkspace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cancel
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, I first check that the &lt;code&gt;WebPage.NavigationAction&lt;/code&gt; has a request with a URL. I can&#39;t imagine when this would ever be nil, but it&#39;s optional, so I check and cancel the navigation if it&#39;s missing. Then I test for my custom scheme and allow those pages to load. In this example, all other URLs open in the default browser so I return &lt;code&gt;cancel&lt;/code&gt; to stop them opening in the &lt;code&gt;WebView&lt;/code&gt;. To test this, open one of the man pages using a toolbar button and scroll to the end of the page where I added an external link.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In macOS Tahoe 26 beta 7 and Xcode 26 beta 6, this external navigation prints what appears to be a crash log in the console, but the app does not crash.&lt;/p&gt;
&lt;p&gt;This example demonstrates two other &lt;code&gt;WebView&lt;/code&gt; features:&lt;/p&gt;
&lt;p&gt;By default, a &lt;code&gt;WebView&lt;/code&gt; allows bouncing so the page appears to scroll sideways even though it all fits. This is really obvious is you use a track pad and swipe sideways. To turn off this behavior, add this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollBounceBehavior&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;basedOnSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; axes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;horizontal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vertical scrolling still works and bounces, but horizontal does not, unless the content doesn&#39;t fit.&lt;/p&gt;
&lt;p&gt;The other feature is searching in the page. Presenting the find interface works much like presenting a sheet - add a Boolean and connect it up:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; findNavigatorIsPresented &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replaceDisabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// doesn&#39;t work yet&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findNavigator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $findNavigatorIsPresented&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added a toolbar button to toggle &lt;code&gt;findNavigatorIsPresented&lt;/code&gt; for showing and hiding the interface. I have included the &lt;code&gt;replaceDisabled(true)&lt;/code&gt; modifier but it doesn&#39;t work in a &lt;code&gt;WebView&lt;/code&gt;. The modifier doesn&#39;t stop the replace interface from appearing, but replacing doesn&#39;t actually work:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/web_find.png&quot; alt=&quot;Find navigator&quot;&gt;&lt;/p&gt;
&lt;p&gt;Interestingly, in a &lt;code&gt;TextEditor&lt;/code&gt;, it is possible to hide the replace button by adding the &lt;code&gt;replaceDisabled(true)&lt;/code&gt; modifier, but the number of matches doesn&#39;t appear.&lt;/p&gt;
&lt;h3&gt;5. JavaScripting&lt;/h3&gt;
&lt;p&gt;SwiftUI&#39;s WebView provides an asynchronous method for calling Javascript on the page. In this example, I load a page from my web site and once it&#39;s loaded, I send a JavaScript command to gather all the &lt;strong&gt;H3&lt;/strong&gt; headers so I can make them into a navigation menu in the toolbar.&lt;/p&gt;
&lt;p&gt;While I could use navigation event tracking to detect when the page has finished loading, instead I use a loop containing &lt;code&gt;Task.sleep&lt;/code&gt; to wait until &lt;code&gt;page.isLoading&lt;/code&gt; becomes false:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;loadPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    sections &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pageAddress&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;URLRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isLoading &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;milliseconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;listHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I have the list of headers, I add them to a toolbar menu for jumping around the page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/web_js.png&quot; alt=&quot;JavaScript populated sections menu&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the WWDC video, the presenter demonstrated a similar technique, although without sharing the actual JavaScript. When testing, I found it easier to write and debug my JavaScript functions directly in the browser, so the project repo includes my &lt;strong&gt;jsTests.js&lt;/strong&gt; file.&lt;/p&gt;
&lt;p&gt;The methods that use JavaScript are gathered into an extension on the &lt;code&gt;JavaScripting&lt;/code&gt; view. I start by using multi-line strings to create the scripts, interpolating data from the view as needed. Then a &lt;code&gt;Task&lt;/code&gt; uses &lt;code&gt;try await page.callJavaScript(js)&lt;/code&gt; to call the JavaScript and get the result.&lt;/p&gt;
&lt;p&gt;The result is of type &lt;code&gt;Any?&lt;/code&gt; so then I try to cast it into the type I want. Here&#39;s the full method for listing the section headers:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;listHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; js &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&quot;
    const headers = document.querySelectorAll(&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;headerElement&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;)
    return [...headers].map(header =&gt; ({
      id: header.textContent.replaceAll(&quot; &quot;, &quot;-&quot;).toLowerCase(),
      title: header.textContent
    }))
    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;callJavaScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;js&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; headers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jsResult &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pageSections &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;SectionLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jsEntry&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MainActor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          sections &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageSections
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SectionLink&lt;/code&gt; is a struct that contains the section title and ID and has a custom &lt;code&gt;init&lt;/code&gt; that takes a &lt;code&gt;[String: Any]&lt;/code&gt; dictionary.&lt;/p&gt;
&lt;p&gt;In this case, I interpolated &lt;code&gt;headerElement&lt;/code&gt; directly into the JavaScript string, but you can also pass it to &lt;code&gt;callJavaScript&lt;/code&gt; using the &lt;code&gt;arguments&lt;/code&gt; parameter. Here&#39;s how I pass in a title for working out how to scroll to its section:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;callJavaScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  js&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  arguments&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sectionTitle&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; section&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The JavaScript now has access to the &lt;code&gt;sectionTitle&lt;/code&gt; variable, which contains the title of the section to scroll to. This returns the vertical offset of the section, which I then use to scroll the web view to that position.&lt;/p&gt;
&lt;p&gt;Managing the web view&#39;s scrolling has several components. First, an &lt;code&gt;@State&lt;/code&gt; property holds a &lt;code&gt;ScrollPosition&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; currentScroll &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ScrollPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, bind this property to the &lt;code&gt;WebView&lt;/code&gt; using &lt;code&gt;webViewScrollPosition&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;webViewScrollPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$currentScroll&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, when the JavaScript returns a value for the section offset, use this property to adjust the scroll:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token function&quot;&gt;withAnimation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;easeInOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  currentScroll&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The animation makes the scroll look better, instead of jumping suddenly to the new location.&lt;/p&gt;
&lt;p&gt;Another interesting part of the WWDC demo was detecting the scroll and setting the selected section to match. I had started by putting the sections into a &lt;code&gt;Picker&lt;/code&gt; with a selection parameter. When the selected section changed, I scrolled the web view to that section.&lt;/p&gt;
&lt;p&gt;That worked fine until I started detecting the scroll, which also set the selected section, which reset the scroll to the top of the current section every time. To get around this, I made the toolbar item into a menu with a set of buttons with scroll actions. They did not set the selected section, but their titles showed a leading checkmark for the selected section. The scroll detector worked out the visible section and set the selected section without altering the scroll.&lt;/p&gt;
&lt;p&gt;To monitor the scroll position, I added this modifier to the &lt;code&gt;WebView&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;webViewOnScrollGeometryChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentOffset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;adjustSelectionTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scrollPosition&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; newValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;adjustSelectionTo(scrollPosition:)&lt;/code&gt; method uses another chunk of JavaScript to work out which section is now visible and the result of this is used to update the selected section property.&lt;/p&gt;
&lt;p&gt;I had an issue with the longer section names which were truncated when another section was checked. This was fixed by using string interpolation instead of the string itself when showing the title without the checkmark. I&#39;m not sure why this works, but I assume string interpolation forces the view to re-calculate its width each time.&lt;/p&gt;
&lt;h3&gt;6. Browser&lt;/h3&gt;
&lt;p&gt;The final example attempts to emulate a browser. There is a URL entry &lt;code&gt;TextField&lt;/code&gt; at the top and a &lt;code&gt;WebView&lt;/code&gt; below it.&lt;/p&gt;
&lt;p&gt;The new part here is tracking the browser history and adding back and forward buttons. These use the &lt;code&gt;page.backForwardList&lt;/code&gt; to create their menus with a primary action that goes back one or forward one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/web_browser.png&quot; alt=&quot;Browser history&quot;&gt;&lt;/p&gt;
&lt;p&gt;At first, I assumed that &lt;code&gt;page.load()&lt;/code&gt; required a &lt;code&gt;URL&lt;/code&gt; or a &lt;code&gt;URLRequest&lt;/code&gt;, but then I realized that I could also ask it to load a &lt;code&gt;WebPage.BackForwardList.Item&lt;/code&gt; directly.&lt;/p&gt;
&lt;p&gt;I had a problem with the back and forward menus which made me think that the history was not updating correctly. After temporarily re-purposing the refresh button to list the history items, I worked out that history list was correct but the menus were not being updated when the list changed.&lt;/p&gt;
&lt;p&gt;To solve this, I added an &lt;code&gt;id&lt;/code&gt; to each menu that would trigger an update when its value changed. I needed something hashable that changed on every load, so I chose &lt;code&gt;page.isLoading&lt;/code&gt;. I realize that this is updating the menus twice as often as needed, but it works and I don&#39;t think it is too inefficient.&lt;/p&gt;
&lt;p&gt;The other feature here is the refresh button. If you&#39;ve done any web dev, you&#39;ll be familiar with the technique of reloading from origin so that you get all the latest files instead of any cached versions. Hold down the Option key to switch the refresh button to reload from origin mode.&lt;/p&gt;
&lt;p&gt;The only issue that I haven&#39;t tackled is opening links in new tabs or windows. On this site, all external links are set to open in a new tab and they just fail to load. I wonder is this something that a navigation decider could handle?&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;After waiting several years for this view to arrive, I am not disappointed. The SwiftUI team have done a great job of integrating SwiftUI and WebKit with some modernizations that WebKit is still missing.&lt;/p&gt;
&lt;p&gt;For my personal use, I&#39;ll be updating Man Reader to use this web view. The find-in-page, custom scheme handling and scroll detection will be great. I also have to re-write the sections in my books that use &lt;code&gt;WKWebView&lt;/code&gt; but that will be part of the general macOS/Xcode 26 update cycle.&lt;/p&gt;
&lt;p&gt;All the sample code from this article is on &lt;a href=&quot;https://github.com/trozware/swiftui-webview&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; so I encourage you to download it and see how it works. Make changes, and please let me know if you find any errors or can suggest improvements using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac 2025</title>
    <link href="https://troz.net/post/2025/swiftui-mac-2025/" />
    <updated>2025-08-08T13:15:00Z</updated>
    <id>https://troz.net/post/2025/swiftui-mac-2025/</id>
    <content type="html">&lt;p&gt;Almost every year, I write an article and a sample app, exploring the new features of SwiftUI, with particular emphasis on Mac app development. This year, it feels like the operating systems are becoming more uniform, so there is not a lot that&#39;s Mac-specific, but there are several new features that I am keen to explore.&lt;/p&gt;
&lt;p&gt;Usually, I write an app that downloads images from &lt;a href=&quot;https://http.cat&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;HTTP Cats&lt;/a&gt; as well as demonstrating other features. This year, navigation hasn&#39;t really changed, so the HTTP Cats app didn&#39;t seem relevant. Instead, I&#39;ve created a sample app that features some of the new aspects of SwiftUI for macOS 26.&lt;/p&gt;

&lt;p&gt;Right now, I&#39;m using Beta 4: macOS 26.0 Beta (25A5316i) and Xcode 26.0 beta 4 (17A5285i). And by the way, I love the new year numbering system so that all the operating systems are easily identifiable and even Xcode is in line with everything else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATES for Beta 5: 8th August 2025&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The new icon format is now fully backwards compatible. I&#39;ve tested going back to macOS 11 and it still shows the new &lt;strong&gt;Icon Composer&lt;/strong&gt; icon, even though I have an old-style icon in &lt;strong&gt;Assets.xcassets&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;glassProminent&lt;/code&gt; button no longer has a graphical glitch.&lt;/li&gt;
&lt;li&gt;In the menus and main toolbar, I&#39;m now using buttons with titles and system images instead of labels.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Here is a list of the topics I&#39;m going to cover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#app-icons&quot;&gt;App icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#new-look-controls&quot;&gt;New look controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#web-view&quot;&gt;Web view&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#rich-text&quot;&gt;Rich text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#long-lists&quot;&gt;Long lists&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#menu-item-icons&quot;&gt;Menu item icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#concurrency&quot;&gt;Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2025/swiftui-mac-2025/#toolbars&quot;&gt;Toolbars&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a id=&quot;app-icons&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App icons&lt;/h3&gt;
&lt;p&gt;When you create a new project in Xcode 26, the old &lt;strong&gt;Assets.xcassets&lt;/strong&gt; folder is still there and it still contains an &lt;strong&gt;AppIcon&lt;/strong&gt; entry, but this is no longer the only way to create an app icon. The new icon format applies to all platforms, but this is an area where macOS has lagged behind iOS, so it&#39;s great to see all the systems using the same tool and format now.&lt;/p&gt;
&lt;p&gt;Right-click on the Xcode icon in your Dock and choose &lt;strong&gt;Open Developer Tool → Icon Composer&lt;/strong&gt; from the popup menu. This opens a new app for creating icon files. Create a new document and start by using the Document tab in the inspector to turn off watchOS and select macOS only:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/icon_doc_settings.png&quot; alt=&quot;Icon document settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then switch back to the paint brush inspector to set the background. The app supplies a few fill options, so play around with them to see what you like. After that, the recommendation is to add one or two layers using SVGs.&lt;/p&gt;
&lt;p&gt;Being graphically challenged, I searched for a free cat icon in SVG format and found this: &lt;a href=&quot;https://iconscout.com/icon/cat-9608559&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;https://iconscout.com/icon/cat-9608559&lt;/a&gt;. I dragged the file on to my icon and started playing around with it. The most useful controls in the main portion of the app are the one at the top for toggling on the grid - you can use the dropdown button on the right to switch between light and dark modes - and the three preview options at the bottom for toggling between default, dark and mono modes.&lt;/p&gt;
&lt;p&gt;In the sidebar, select &lt;strong&gt;Icon&lt;/strong&gt; to edit the background and select your image to change its properties. When working with the image, you can select whether options apply to all the versions or just the currently selected variant. This is useful as I found that the Liquid Glass effect looked great in dark mode but not in default mode with my gradient. I wasn&#39;t pleased with the mono version, but I&#39;ll see how it looks in real life. I can&#39;t imagine myself ever using mono icons on my Mac, but maybe that&#39;s just me.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/icon_composer.png&quot; alt=&quot;Icon composer&quot;&gt;&lt;/p&gt;
&lt;p&gt;Anyway, with my icon composed, I saved it as &lt;strong&gt;Cat.icon&lt;/strong&gt; and then dragged it in to the project folder, copying it to my app target. Then, in General settings for the app target, I set &lt;strong&gt;App Icon&lt;/strong&gt; to &lt;strong&gt;Cat&lt;/strong&gt; without the extension. After running the app, I right-clicked on its icon in the Dock and selected &lt;strong&gt;Options → Show in Finder&lt;/strong&gt; so I could see the icon fully. It looked pretty good in default mode, but when I used &lt;strong&gt;System Settings → Appearance&lt;/strong&gt; to alter the icon style, it was clear that some of the options would require more work. But still, this was a good start.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/icon_appearance.png&quot; alt=&quot;Icon appearance&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;If you want to support older systems and have an AppIcon set in &lt;strong&gt;Assets.xcassets&lt;/strong&gt;, set the name of your Icon Composer file to AppIcon and your app will use the new one and fall back to the old one if required.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: It appears that the new icon format is now fully backwards compatible. I&#39;ve tested going back to macOS 11 and it still shows the new Icon Composer icon, even though I have an old-style icon in &lt;strong&gt;Assets.xcassets&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;One nice touch is that if you select the icon file in the Xcode Project navigator, you get a button under the preview for opening the file in Icon Composer directly.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;new-look-controls&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;New look controls&lt;/h3&gt;
&lt;p&gt;With the introduction of Liquid Glass, many controls have a new look. In the sample app, I added a &lt;code&gt;TabView&lt;/code&gt; to group the controls I wanted to test. Tabs have a title and an icon, as they did last year. The default style groups them in the toolbar, although since Beta 3, there is not a lot of transparency:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/default_tabs.png&quot; alt=&quot;Default tabs&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notice how this has hidden the window title too.&lt;/p&gt;
&lt;p&gt;Setting the &lt;code&gt;tabViewStyle&lt;/code&gt; to &lt;code&gt;sidebarAdaptable&lt;/code&gt; moved the tabs to a sidebar overlay and displays the window title. This looks much more like a standard sidebar and shows the tab icons too. I didn&#39;t like the way this looked last year, but I think it fits well with the new UI design. It&#39;s like a navigation sidebar but for a static list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/sidebar_tabs.png&quot; alt=&quot;Sidebar tabs&quot;&gt;&lt;/p&gt;
&lt;p&gt;Not only does this style display the window title, but it also allows for a window subtitle using the &lt;code&gt;navigationSubtitle&lt;/code&gt; modifier. And it displays a sidebar toggle button. There is a way to remove the sidebar toggle button if it&#39;s part of a navigation view, but that technique didn&#39;t work here. Despite the style name indicating that this style is adaptable, it only adapts to suit the window size in an iPad app.&lt;/p&gt;
&lt;h4&gt;Buttons&lt;/h4&gt;
&lt;p&gt;The main change for buttons is the two new options for &lt;code&gt;buttonStyle&lt;/code&gt;: &lt;code&gt;glass&lt;/code&gt; and &lt;code&gt;glassProminent&lt;/code&gt;. Both styles can have a &lt;code&gt;tint&lt;/code&gt; applied. I found that with certain tints, the &lt;code&gt;glassProminent&lt;/code&gt; style button doesn&#39;t show the mouse down effect very distinctly. By default, the &lt;code&gt;glassProminent&lt;/code&gt; style is tinted using the &lt;code&gt;accentColor&lt;/code&gt; or the user&#39;s preferred theme color if chosen.&lt;/p&gt;
&lt;p&gt;Toggles have a slightly new look. The button style gives a much more prominent indicator of the selected state, and the switch style uses the new Liquid Glass style which you can really see if you drag the switch instead of clicking on it.&lt;/p&gt;
&lt;p&gt;In this screenshot, I&#39;ve added a background to the view and tinted the &lt;code&gt;glassProminent&lt;/code&gt; button. The background looks great with the tab sidebar but the title bar looks a bit jarring:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/buttons.png&quot; alt=&quot;Buttons&quot;&gt;&lt;/p&gt;
&lt;p&gt;I wrapped the &lt;code&gt;VStack&lt;/code&gt; containing the sample buttons in a &lt;code&gt;ScrollView&lt;/code&gt; and that made the title bar transparent and allowed the content to scroll up behind the title bar. Toggle the background on and shrink the window to see this clearly:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/liquid_scroll.png&quot; alt=&quot;Hidden title bar&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;Sometimes, this caused a graphical glitch in the &lt;code&gt;glassProminent&lt;/code&gt; button, but I expect this is a beta issue.&lt;/s&gt; This is fixed in beta 5.&lt;/p&gt;
&lt;h4&gt;Numbers&lt;/h4&gt;
&lt;p&gt;For entering numbers, I tested the &lt;code&gt;Slider&lt;/code&gt; and &lt;code&gt;Stepper&lt;/code&gt; controls. The &lt;code&gt;Slider&lt;/code&gt; has the new Liquid Glass style thumb and a cute little bounce of the icons when you reach either end. The &lt;code&gt;step&lt;/code&gt; parameter displays tick marks along the slider.&lt;/p&gt;
&lt;p&gt;For the stepper, the up and down arrows are larger, which I think is a good idea - they were very small before. There&#39;s also a format parameter which has probably been there for years but I&#39;ve never used it. What I like about it is that it displays the selected value in the format you specify and it&#39;s editable! I&#39;m using &lt;code&gt;percent&lt;/code&gt; in this example:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/number_entry.png&quot; alt=&quot;Numbers&quot;&gt;&lt;/p&gt;
&lt;p&gt;One slight oddity of the stepper showing an editable value is that it lets you enter a value that doesn&#39;t match the step size or is inside the range. In this example, I set the step size to 0.05 (5%) and then entered 13%. The arrow buttons still go up and down in 5% increments, so I got 18% or 8% depending on which way I clicked. Also, you can enter a non-numeric value, but it is ignored: tabbing out of the entry resets it, and the arrow buttons operate on your previous entry.&lt;/p&gt;
&lt;p&gt;I added a view to test the various &lt;code&gt;Picker&lt;/code&gt; styles, but they seem to be the same as last year, so for fun, I added two &lt;code&gt;Text&lt;/code&gt; views with glass effects applied:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Glass Effect - regular&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;glassEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;regular&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorForTint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Glass Effect - clear&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;glassEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clear&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorForTint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They take their tint color from the picker selection. The &lt;code&gt;clear&lt;/code&gt; style is slightly more translucent than the default &lt;code&gt;regular&lt;/code&gt; style but doesn&#39;t have as glassy a border. Neither are as glassy as I expected, so I added a &lt;code&gt;ZStack&lt;/code&gt; to apply a background of the same color only much less opaque and with a &lt;code&gt;backgroundExtensionEffect&lt;/code&gt; to bring the color under the side tab  bar:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ZStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  colorForTint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;backgroundExtensionEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I expect it would look even more obvious with an image behind it. Actually, leaving it un-tinted on a color background is a probably the best way to see the effect:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/glass_effect.png&quot; alt=&quot;Glass effect&quot;&gt;&lt;/p&gt;
&lt;p&gt;And if you really want to see something unusual, put the app into the background. The three glass effects are all very different.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;web-view&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Web view&lt;/h3&gt;
&lt;p&gt;One of the most exciting additions for me was the new &lt;code&gt;WebView&lt;/code&gt;. So many apps need to display web content, and until now, the only option has been to use &lt;code&gt;NSViewRepresentable&lt;/code&gt; or &lt;code&gt;UIViewRepresentable&lt;/code&gt; to bring in a &lt;code&gt;WKWebView&lt;/code&gt;. The SwiftUI team has exceed my expectations with the features in the new &lt;code&gt;WebView&lt;/code&gt; and it&#39;s going to need its own article, but I&#39;ll cover the two main ways to use it here.&lt;/p&gt;
&lt;p&gt;Before working with any web view in a Mac app, you first need to turn on &lt;strong&gt;Outgoing Connections (Client)&lt;/strong&gt; in the &lt;strong&gt;Signing &amp;amp; Capabilities&lt;/strong&gt; for the target. Also, any SwiftUI view that needs to use &lt;code&gt;WebView&lt;/code&gt; must import &lt;code&gt;WebKit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With those in place, here&#39;s my code for displaying my web site in a SwiftUI WebView:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebKit&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebDemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; myPage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://troz.net&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; myPage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have found that the only way to create an invalid URL from a string is to provide an empty string, so I am now happy to force-unwrap URLs. After that, I added a &lt;code&gt;WebView&lt;/code&gt; and set its &lt;code&gt;url&lt;/code&gt; to &lt;code&gt;myPage&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/webview_url.png&quot; alt=&quot;Web view&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, a contextual menu offers basic navigation options. So to display any web page off the internet, all you need is the URL.&lt;/p&gt;
&lt;p&gt;To gain more control, initialize a &lt;code&gt;WebView&lt;/code&gt; with a &lt;code&gt;WebPage&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebDemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; blank &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;about:blank&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;WebView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onAppear &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;html&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; html&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; baseURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blank&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; html &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&quot;
      HTML goes here (truncated for reading)
    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added a &lt;code&gt;Picker&lt;/code&gt; to the toolbar for switching between the two web view sources. Here&#39;s the one showing a local HTML string:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/local_web.png&quot; alt=&quot;Local web&quot;&gt;&lt;/p&gt;
&lt;p&gt;Using a &lt;code&gt;WebPage&lt;/code&gt; gives a lot of different options, but this shows the basic idea. I will tend to use a &lt;code&gt;WebPage&lt;/code&gt; as that lets me track the loading state so I can show a progress indicator.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;rich-text&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Rich text&lt;/h3&gt;
&lt;p&gt;When I&#39;m asked if I recommend starting a Mac app with SwiftUI or with AppKit, I used to say SwiftUI unless your app uses lists with thousands of entries or long form text editing. SwiftUI lists have improved a lot in macOS this year, but I&#39;m still not sure about that, however it looks like text editing can come off my AppKit list.&lt;/p&gt;
&lt;p&gt;The key is to use a &lt;code&gt;TextEditor&lt;/code&gt; but to link it to an &lt;code&gt;AttributedString&lt;/code&gt; instead of a plain &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AttributedString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;TextEditor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With an &lt;code&gt;AttributedString&lt;/code&gt;, you get a lot of formatting options, although not as many as I expected. Setting bold, italic or underline is easy and so is changing the font size. I have not yet worked out how to change the font, except by pasting in text in a different font. At that point, the newly pasted font becomes one of the document&#39;s styles and can be applied to other portions of the text, but this can&#39;t be the only way. There is a &lt;strong&gt;Show Fonts&lt;/strong&gt; menu item in &lt;strong&gt;Format → Font&lt;/strong&gt;, but it is always disabled. Spell checking is also a bit erratic - it works really well if you open the spell checker dialog, but it doesn&#39;t work as you type.&lt;/p&gt;
&lt;p&gt;I added &lt;code&gt;TextEditingCommands&lt;/code&gt; and &lt;code&gt;TextFormattingCommands&lt;/code&gt; to the menus, but they all seemed to be available through the contextual menu anyway. The Apple Intelligence Writing Tools are also available either through the &lt;strong&gt;Edit&lt;/strong&gt; menu or the contextual menu.&lt;/p&gt;
&lt;p&gt;For saving and loading the formatted text, I realized that &lt;code&gt;AttributedString&lt;/code&gt; is a &lt;code&gt;Codable&lt;/code&gt; type, so I used &lt;code&gt;JSONEncoder&lt;/code&gt; and &lt;code&gt;JSONDecoder&lt;/code&gt; to save and load as the view appears and disappears. This worked really well, whether I closed the editor window manually or just quit the app. This is just a quick hack for a single editor, but it could be the basis for a more complex app.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/text_editing.png&quot; alt=&quot;Text editing&quot;&gt;&lt;/p&gt;
&lt;p&gt;I think there will be more to discover with this, but it&#39;s already a lot better than before. And I haven&#39;t even started to get into tracking the selection or adding custom formatting.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;long-lists&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Long lists&lt;/h3&gt;
&lt;p&gt;SwiftUI lists on iOS have always performed better than on macOS but this year, macOS is catching up. I added a &lt;strong&gt;List Demo&lt;/strong&gt; window to test out various lengths of list. Before, I found that more than about 3,000 items in a list would cause performance issues, but now I can have 10,000 items and it still feels snappy, both for drawing, scrolling and selecting. With 50,000 items, it&#39;s still usable but selection is a bit slow. Interestingly, clearing the list is a big performance hit, but I&#39;m not sure why. 500,000 items takes a very long time to draw. Scrolling is not too bad but selecting a new item takes several seconds. Clearing a list this big takes more than a minute.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/long_list.png&quot; alt=&quot;Long list&quot;&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m still not convinced that SwiftUI lists are the best way to display long lists, but I&#39;m glad to see some performance improvements. I think my limit would be around 20,000 items, but I&#39;d need to test on various machines.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;menu-item-icons&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Menu item icons&lt;/h3&gt;
&lt;p&gt;A new thing in macOS 26 and iPadOS 26 is icons attached to menu items. Most of the default menu items have icons, but what about my own?&lt;/p&gt;
&lt;p&gt;Starting with the &lt;strong&gt;Window&lt;/strong&gt; menu, I see the entries for my three demo windows and the main window:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/window_menu.png&quot; alt=&quot;Window menu&quot;&gt;&lt;/p&gt;
&lt;p&gt;Interestingly, the main window shows the title and its subtitle. I can&#39;t see how to add an icon to any of my windows in this menu, but the standard system items all have icons.&lt;/p&gt;
&lt;p&gt;What about a menu with items to mimic the tab and toolbar buttons? I added a new &lt;strong&gt;Show&lt;/strong&gt; menu and used a &lt;code&gt;Picker&lt;/code&gt; to switch between the main window tab and buttons to open the demo windows. For each one, I used a &lt;code&gt;Label&lt;/code&gt; instead of a &lt;code&gt;Text&lt;/code&gt; view or a plain title. This let me assign an system image to each. I realize that this means the demo windows each have two menu items, but I wanted to experiment. And anyway, Xcode has two menu items to open the docs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/new_menu.png&quot; alt=&quot;New menu&quot;&gt;&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;Picker&lt;/code&gt; items, I set the &lt;code&gt;pickerStyle&lt;/code&gt; to &lt;code&gt;inline&lt;/code&gt; and set &lt;code&gt;labelIsHidden&lt;/code&gt; to true. I like this style as it gives me a picker at the top level of the menu, instead of in a submenu. For passing the selection to the window, I used &lt;code&gt;@AppStorage&lt;/code&gt; which is not a great choice as it makes the choice apply to every open instance of the main window, but I didn&#39;t want to get bogged down here. If you&#39;re interested in a more complete solution, check out my article on &lt;a href=&quot;https://troz.net/post/2025/mac_menu_data/&quot;&gt;The Mac Menubar and SwiftUI
&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As you can see, I was able to show icons, check marks and keyboard shortcuts. I like this, but it can be over done. The WWDC video on the topic said that if you have related menu items like multiple &lt;strong&gt;New&lt;/strong&gt; or &lt;strong&gt;Open&lt;/strong&gt; options, you should only add an icon to the first.&lt;/p&gt;
&lt;p&gt;On a side note, the &lt;strong&gt;+&lt;/strong&gt; button for opening the Library has disappeared from Xcode&#39;s toolbar. I&#39;m not sure if this is a bug or a feature but you have to use the &lt;strong&gt;View&lt;/strong&gt; menu now, or the &lt;strong&gt;Shift-Command-L&lt;/strong&gt; shortcut.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;concurrency&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Concurrency&lt;/h3&gt;
&lt;p&gt;This is another thing that is not Mac-specific, but I feel it is important.&lt;/p&gt;
&lt;p&gt;Last year, I sat out the change to Swift 6 and its strict concurrency checking. While I could get it to work by following Xcode&#39;s suggestions, I was left feeling that I was adding code to make the compiler happy, instead of code that actually made my apps better. This year, things are different. For starters, a new Xcode project has &lt;strong&gt;Approachable Concurrency&lt;/strong&gt; turned on and has &lt;strong&gt;Default Actor Isolation&lt;/strong&gt; set to &lt;strong&gt;MainActor&lt;/strong&gt;. This means that everything is isolated to the main thread by default, but you can step out into a background thread if required.&lt;/p&gt;
&lt;p&gt;The default Swift version is still set to 5, but I changed it to 6 for this sample app and had no compile-time warnings or errors. I did use &lt;code&gt;@concurrent&lt;/code&gt; when generating the items for the list demo, but it turned out that it was the UI updates on the main thread that were slowing things down, not the data generation. Even so, I prefer the new approach and I&#39;ll probably be using Swift 6 from now on.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;toolbars&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Toolbars&lt;/h3&gt;
&lt;p&gt;As you will have seen in the screenshots, I added a toolbar to &lt;code&gt;ContentView&lt;/code&gt; with buttons for navigating to various demo windows. I wanted to try out the new floating toolbar features with spacers, so I copied the format from Apple&#39;s Landmarks app. This didn&#39;t work - all the toolbar items were still grouped together. This turned out to be a &lt;em&gt;feature&lt;/em&gt; of using the &lt;code&gt;sidebarAdaptable&lt;/code&gt; tab style. When I reverted to a standard tab style, the toolbar appeared with multiple groups as I expected:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/toolbar_spacers.png&quot; alt=&quot;Toolbar with spacers&quot;&gt;&lt;/p&gt;
&lt;p&gt;There was no logical reason for separating these buttons, but I wanted to test using &lt;code&gt;ToolbarSpacer(.fixed)&lt;/code&gt; and &lt;code&gt;ToolbarSpacer(.flexible)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are a lot of new toolbar positioning options that are not available in macOS. It used to be possible to identify a Catalyst app from its missing Settings menu item. I think that in the future, we&#39;ll identify them from toolbars floating in unexpected places!&lt;/p&gt;
&lt;p&gt;Looking back at the screenshots from the web view section, the window without a toolbar already looks old-fashioned. Adding the toolbar made it look more modern.&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;The big thing this year is Liquid Glass. I have always advocated using default controls wherever possible and for apps that did this, most of the new look will be applied by re-compiling in Xcode 26. I&#39;m not convinced that having content blurring behind a toolbar or title bar is a good idea - it ends up looking like a layout error much of the time. Of the new features, I&#39;m most excited about the &lt;code&gt;WebView&lt;/code&gt; so expect a separate article on that soon.&lt;/p&gt;
&lt;p&gt;The sample app is available on GitHub: &lt;a href=&quot;https://github.com/trozware/swiftui-mac-2025&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;https://github.com/trozware/swiftui-mac-2025&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have any feedback about this article, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>WWDC 2025</title>
    <link href="https://troz.net/post/2025/wwdc_2025/" />
    <updated>2025-06-23T00:00:00Z</updated>
    <id>https://troz.net/post/2025/wwdc_2025/</id>
    <content type="html">&lt;p&gt;Apple&#39;s Worldwide Developers Conference is over for another year. As I wrote in an &lt;a href=&quot;https://troz.net/post/2025/apple_dev_rel/&quot;&gt;earlier post&lt;/a&gt;, I am not happy about Apple&#39;s management and their attitude to third-party developers, but there is no doubt that their own developers are doing some amazing work. I&#39;ve been away and was then busy launching my &lt;a href=&quot;https://troz.net/books/escape/&quot;&gt;new book&lt;/a&gt;, so I have not caught up with all the new announcements yet, but here are my initial impressions...&lt;/p&gt;

&lt;h2&gt;Version Numbering&lt;/h2&gt;
&lt;p&gt;This is great! Now I can finally tell how old a system is without having to look it up. iOS has always had numerical versioning but macOS, with its clever names picked by the crack marketing team, has been impossible to remember. And with Xcode adding a slightly different number, it was all just too confusing.&lt;/p&gt;
&lt;p&gt;As regards using next year as the number, we developers need to remember that we live in a bubble of pre-releases. Most people won&#39;t get these systems until late in 2025 and they need to feel like they are getting a new system, not one that&#39;s already nearly out-of-date.&lt;/p&gt;
&lt;h2&gt;Liquid Glass&lt;/h2&gt;
&lt;p&gt;I&#39;ve installed the first versions of the beta operating systems on a Mac and an iPad and so far, I&#39;m really enjoying the new look. There are some accessibility issues that need fixing, but Apple has a good track record on accessibility, so I am sure they&#39;ll get there. Once we get used to the new look, I think we will find the look of old apps and icons jarring, but once they have all been updated, our screens will become more cohesive.&lt;/p&gt;
&lt;p&gt;It&#39;s obvious that Apple is trying to make all their operating systems look and feel similar, and despite its lack of traction, visionOS has exerted a huge influence on the new design.&lt;/p&gt;
&lt;h2&gt;Xcode&lt;/h2&gt;
&lt;p&gt;Apart from the fact that &lt;strong&gt;ALL&lt;/strong&gt; my screenshots are now wrong, I like the new look for Xcode. I&#39;m a bit confused by the tabbing system and I haven&#39;t discovered a fast way to open two files side by side yet, but I&#39;m sure I&#39;ll get used to it. As regards the new Settings window, I don&#39;t have a problem with it becoming more like the System Settings app - this seems a logical extension of Apple&#39;s increasingly uniform design. The only pane I find difficult to use is the Themes pane, which only shows 4.5 lines of samples, regardless of the height of the window.&lt;/p&gt;
&lt;p&gt;I love the new &lt;code&gt;#Playground&lt;/code&gt; feature! It gives a much clearer view of the data than an actual playground and seems to run faster. It&#39;s making me re-think how I&#39;ll approach some of my books and projects where I often start with experiments in a playground before creating the project itself. I was surprised when Xcode removed the &lt;strong&gt;Create New Playground&lt;/strong&gt; option from the Welcome window a couple of versions ago, but now I see why.&lt;/p&gt;
&lt;p&gt;I haven&#39;t explored the Coding Assistant much yet. Basically, Apple is admitting that their models are not good enough, so they&#39;ve given us the ability to connect external models into Xcode. I&#39;ll have to see how well this works and how far I can get without yet another subscription. I&#39;m not sure how much I&#39;ll use it, but it&#39;s nice to have some AI facilities incorporated into Xcode.&lt;/p&gt;
&lt;h2&gt;SwiftUI&lt;/h2&gt;
&lt;p&gt;There weren&#39;t a huge number of changes in SwiftUI, but there were some really good ones. I have been expecting a SwiftUI web view for years now, and it has finally arrived. After watching the video, I am very impressed and can&#39;t wait to start using it in several of my projects. They&#39;ve done a great job of making a very complete and powerful web view.&lt;/p&gt;
&lt;p&gt;Whenever I&#39;ve been asked if I would recommend basing a Mac app on SwiftUI or AppKit, I always said SwiftUI unless you need long form text editing or have to display long lists. It looks like the text editing is now much better, although I haven&#39;t tried it yet. It was good to see acknowledgement of the fact that lists in macOS SwiftUI have not been as performant as in iOS SwiftUI and they appear to have improved this a lot. My initial tests were promising, but I need to do more in a real project to be sure.&lt;/p&gt;
&lt;h2&gt;Swift&lt;/h2&gt;
&lt;p&gt;Last year, Swift 6 introduced strict concurrency checking. I tried it out, but I felt like I was making changes that I didn&#39;t really understand in order to make the compiler happy. This is not a good way to produce code, so I dropped back to Swift 5 mode - which was still Xcode&#39;s default - and waited. It appears that concurrency is better implemented this year. I&#39;m waiting to see how this pans out in the later betas, but this may be the year that I finally adopt Swift 6.&lt;/p&gt;
&lt;p&gt;I&#39;m also excited to try the new Foundation Models framework which gives us on-device AI that we can call in our apps.&lt;/p&gt;
&lt;h2&gt;App Icons&lt;/h2&gt;
&lt;p&gt;The new Icon Composer is fun to play with and it&#39;s going to be great not to have to make so many different sizes of icon. As someone who is artistically challenged, I now need to find a simple editor that will allow me to create simple shapes as SVGs for adding to the icons.&lt;/p&gt;
&lt;h2&gt;iPad Windowing&lt;/h2&gt;
&lt;p&gt;I&#39;m still getting used to this. I have a smaller iPad so I don&#39;t tend to open more than one window at a time. I also prefer to use my iPad in portrait mode most of the time, which I don&#39;t think is what Apple&#39;s designers expect. Several apps end up with a series of skinny columns in this mode and Safari shows the iPhone version of web pages when I have the sidebar open. In Safari, I&#39;m confused as to the best way to close the last tab. There used to be an &lt;strong&gt;X&lt;/strong&gt; button that closed the tab and took me back to my Start Page. Now I either pull down the menu bar and select &lt;strong&gt;Close Tab&lt;/strong&gt; or I open the sidebar, expand the Tabs entry and tap the weirdly offset &lt;strong&gt;X&lt;/strong&gt; beside my tab name. Neither is as convenient as the old way, but maybe I&#39;m missing something.&lt;/p&gt;
&lt;h2&gt;Developer Relations&lt;/h2&gt;
&lt;p&gt;Without doing anything to improve matters, it appears that Apple paid some attention to the dissatisfaction in the developer community. In the week before WWDC, they published two articles: one reporting how much money they give to developers and the other saying how safe the App Store keeps us.&lt;/p&gt;
&lt;p&gt;This meant that they didn&#39;t start WWDC with the usual rave about how much they love their developers, which would have rung hollow this year. I also note that Tim Cook played a very minor role in the keynote, before handing it over to Craig Federighi. Is this a passing of the baton? Will we see Cook&#39;s resignation and Federighi&#39;s promotion in the near future?&lt;/p&gt;
&lt;p&gt;Then we had the refusal of any Apple execs to be part of John Gruber&#39;s Talk Show Live. At first I assumed it was because they didn&#39;t want to have to face any questions about Tim Cook&#39;s political decisions or about Apple&#39;s AI failures. But then the execs did interviews with people who are far more likely to be hostile than Gruber, so it became apparent that this was just spite to punish Gruber for his single Apple-critical article in a sea of positive articles. This is petty and should be beneath them. I would be fascinated to know who made this decision.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I think there are exciting times ahead for us as developers. We need to adopt the new interface design - a task that is simpler for apps that use standard controls instead of custom views. Then there are new APIs to explore and further tests of AI coding to be done. As we progress through the beta cycle, I will experiment more and report on my findings.&lt;/p&gt;
&lt;p&gt;If you have any feedback about this article, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>New Book: Escape from Tutorial Hell</title>
    <link href="https://troz.net/post/2025/new-book:-escape-from-tutorial-hell/" />
    <updated>2025-06-18T10:30:00Z</updated>
    <id>https://troz.net/post/2025/new-book:-escape-from-tutorial-hell/</id>
    <content type="html">&lt;p&gt;I&#39;ve written a new book, and this one is quite different from my previous books. Instead of being a coding guide, this one is a guide to designing, building, shipping and maintaining real apps. It does include some coding, but the emphasis is on the process, not the code. As usual, I concentrate on macOS but the core principles apply to all Apple platforms.&lt;/p&gt;
&lt;p&gt;I wrote this book for people who have done some tutorials, read books, watched videos, and who know some Swift and SwiftUI but are finding it difficult to make the leap from there to working on their own apps. It&#39;s also for more experienced developers who want to learn how to design and structure their projects to make them easier to maintain.&lt;/p&gt;
&lt;p&gt;You can buy the book from &lt;a href=&quot;https://sarahreichelt.gumroad.com/l/iqdry&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gumroad&lt;/a&gt; and you can read the introduction and first chapter for free at &lt;a href=&quot;https://troz.net/books/escape_sample.html&quot;&gt;Escape from Tutorial Hell Sample&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/escape_cover_square.png&quot; alt=&quot;Book cover&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Go Beyond Coding: Build, Ship, and Maintain Real Apps&lt;/h4&gt;
&lt;p&gt;When learning to code, everyone hits a block at a certain point. You&#39;ve read books and articles, watched videos, done tutorials and courses. You feel like you know what&#39;s being taught - the code is familiar and you can write small functions when told what the functions should do, but now what? How do you take that knowledge and use it to write a complete app? You&#39;re stuck in &lt;strong&gt;tutorial hell&lt;/strong&gt; so how do you &lt;strong&gt;escape&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;This book aims to fill in that gap and take you from a person who knows the basics of Swift and SwiftUI to a developer with an app that&#39;s designed, built, shipped and ready to be maintained.&lt;/p&gt;
&lt;h3&gt;Contents&lt;/h3&gt;
&lt;h4&gt;Section I: Configuring Your Tools&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Set up Xcode&lt;/li&gt;
&lt;li&gt;Set up Git &amp;amp; GitHub&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Section II: Designing the App&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;What will we build together?&lt;/li&gt;
&lt;li&gt;Explore MapKit&lt;/li&gt;
&lt;li&gt;Use test data to layout the UI&lt;/li&gt;
&lt;li&gt;Include some Mac-specific features: icons, about, help&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Section III: Setting up the Data Flow&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use a playground to test the API&lt;/li&gt;
&lt;li&gt;Work out the data models&lt;/li&gt;
&lt;li&gt;Import the models into the project&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Section IV: Working With Xcode&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Structure the project&lt;/li&gt;
&lt;li&gt;Format the files consistently&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Using a 3rd party library&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Section V: Coding with AI&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Code completion&lt;/li&gt;
&lt;li&gt;Using AI chat&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Section VI: Distributing the App&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Distribution - App Store&lt;/li&gt;
&lt;li&gt;Distribution - external&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All the code and extra materials for the book can be downloaded from &lt;a href=&quot;https://github.com/trozware/escape_book_code&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Thanks to...&lt;/h3&gt;
&lt;p&gt;Peter, my Language Engineer. Despite being in the middle of a huge move, he has managed to squeeze in the time to fix my commas and other grammatical blunders.&lt;/p&gt;
&lt;p&gt;And of course, Tim, my constant source of support, advice, inspiration and many, many cups of tea and coffee.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;I would love to hear from anyone who has read the book, loved it, hated it, found an error or just wanted to say hi.&lt;/p&gt;
&lt;p&gt;If you find any errors in the code, please let me know by &lt;a href=&quot;https://github.com/trozware/escape_book_code/issues&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;opening an issue&lt;/a&gt; at the GitHub page.&lt;/p&gt;
&lt;p&gt;You can contact me directly using any of the contact links at the bottom of this page or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Apple Developer Relations</title>
    <link href="https://troz.net/post/2025/apple_dev_rel/" />
    <updated>2025-05-20T23:53:52Z</updated>
    <id>https://troz.net/post/2025/apple_dev_rel/</id>
    <content type="html">&lt;p&gt;Apple&#39;s Worldwide Developer Conference is just weeks away, but I&#39;m sensing a lot of apathy in the community. The company&#39;s relationship with third-party developers is at a low point.&lt;/p&gt;
&lt;p&gt;We all know that Tim Cook and his senior people will stand up at WWDC and say how much they value their developers and boast about how much money they&#39;ve paid out to them. Being so enthusiastic about the money is very strange - it&#39;s like a rent collector bragging about how much money he has given to the landlord when all he&#39;s doing is collecting the rent and taking his cut. And it&#39;s difficult to take Apple&#39;s apparent enthusiasm for their developers seriously given their behavior over the rest of the year.&lt;/p&gt;
&lt;p&gt;Trust is a hard thing to gain. Apple used to have the developers&#39; trust but now they&#39;ve lost it. It&#39;s much more difficult to regain lost trust than it is to gain it in the first place. I have read many reports of talented developers leaving the Apple ecosystem because they can&#39;t take it any more. This is bad for all of us, but particularly bad for Apple.&lt;/p&gt;
&lt;p&gt;I don&#39;t imagine that anyone at Apple reads my blog, but I have thought of some things I think they could do to improve their relationship with their developers.&lt;/p&gt;

&lt;h3&gt;1. Open up iOS and all other systems to be like macOS&lt;/h3&gt;
&lt;p&gt;macOS has the Gatekeeper system which allows you to run apps from the App Store and from known developers. If you want to distribute a macOS app outside the App Store, you submit your app to Apple for notarization. This is not app review by a person, but an automated check for malware.&lt;/p&gt;
&lt;p&gt;Apple states that iOS needs to be kept locked down to protect us, but macOS is not full of viruses and malware despite it&#39;s greater openness. Apple also states that the App Store and its review process protects users, but the most cursory glance through the iOS App Store shows that it&#39;s full of scam apps, copycat apps and apps with fake reviews.&lt;/p&gt;
&lt;p&gt;If Apple were to open up iOS in the same way that macOS is open, it would immediately end a vast array of court cases against them, as well as avoiding the constant stream of fines from the EU which does not take kindly to Apple&#39;s habit of malicious compliance with their regulations. As a side-note, isn&#39;t it odd how Apple fights the regulations enacted by the democratically elected EU parliament, but falls over itself to comply instantly with the orders of the Russian and Chinese governments?&lt;/p&gt;
&lt;h3&gt;2. Give registered developers access to feedback reports&lt;/h3&gt;
&lt;p&gt;Every year, Apple releases betas of their operating systems to registered developers. They ask for all bugs to be reported using the Feedback Assistant app. This is a time-consuming process for developers and we are becoming increasingly reluctant to provide Apple with unpaid quality control services.&lt;/p&gt;
&lt;p&gt;When you encounter a bug, first you have to make sure it&#39;s not in your code. You analyze it and then try to create a stripped down project that demonstrates it. Next, you file a report with as much detail as you can. This usually includes a system log, which used to be fine but now shows a dialog saying that although this may contain confidential information, it will be used to train AI! So much for confidential.&lt;/p&gt;
&lt;p&gt;Once you submit the bug, the usual response is nothing. Even if the bug is fixed, you get no acknowledgement. It seems to be your responsibility to keep testing and close the bug once you can prove it&#39;s fixed. Very occasionally, you get a response. Sometimes, this is asking for more information. Other times, it&#39;s saying that the bug is closed as a duplicate of some other bug. The problem is that you have no access to the other bug, so now you&#39;re cut out of the loop entirely. Only once, in all my years of filing bugs, have I received a real human response. Filing bug reports feels like a giant waste of my time.&lt;/p&gt;
&lt;p&gt;Now imagine that the bug database was open to all registered developers. When you encounter a bug, you could immediately search to see if anyone else had reported it. If so, you could mark it as &amp;quot;I see this too&amp;quot; and maybe add more information. This would be much faster and more efficient.&lt;/p&gt;
&lt;p&gt;From Apple&#39;s point of view, it would also be vastly more efficient. Instead of having to correlate many reports about the same issue but worded and tagged differently, they could have a much smaller and more manageable database of reported bugs, with a better indication of how widespread each bug was.&lt;/p&gt;
&lt;p&gt;To me, this seems like a win-win situation. I have trouble coming up with a reason for Apple to keep the bug reports secret. Do they think the sheer number of them is embarrassing? Are they afraid of people exploiting the bugs? Either way, having a more efficient system would be better.&lt;/p&gt;
&lt;h3&gt;3. Improve the app review process&lt;/h3&gt;
&lt;p&gt;This has got to be the most frustrating part of an Apple developer&#39;s life. Apple&#39;s app review process is a capricious mess and every time you click &lt;strong&gt;Submit for Review&lt;/strong&gt;, it feels like spinning a roulette wheel. Even apps that have been on the App Store for years are not immune to this. Some apps get blocked for the most bizarre reasons, but the same app will be approved without issue for a different platform or on a different day.&lt;/p&gt;
&lt;p&gt;I think the app review team is probably under-staffed and without clear guidelines. It also appears that the larger development companies are allowed more latitude than the independent developers.&lt;/p&gt;
&lt;p&gt;I would suggest that app review be mainly a check for malware and to confirm that the app doesn&#39;t crash on various hardware and software combinations. After that, there should be checks to see if the app is a scam or an obvious copycat.&lt;/p&gt;
&lt;p&gt;Apple&#39;s reviewers are not familiar with everything that can be done on an iPhone, iPad or Mac and I don&#39;t expect them to be. The problem is when they reject apps purely because they don&#39;t understand them.&lt;/p&gt;
&lt;p&gt;And while I&#39;m on the topic of app review, how about some validation of App Store reviews before they go online? It wouldn&#39;t take a very sophisticated system to detect when an app suddenly receives hundreds of reviews, all virtually identical.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I could go on and on, but these are my top three suggestions for Apple to improve developer relations. What are yours? If you could speak to the Apple board for one meeting, what would you say?&lt;/p&gt;
&lt;p&gt;Let me know using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article interesting, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Man Reader 2.1</title>
    <link href="https://troz.net/post/2025/man-reader-2.1/" />
    <updated>2025-04-28T23:23:53Z</updated>
    <id>https://troz.net/post/2025/man-reader-2.1/</id>
    <content type="html">&lt;p&gt;I&#39;ve just released Man Reader 2.1. Visually, this is very similar to the previous version, but there are a lot of quality of life improvements under the hood.&lt;/p&gt;
&lt;p&gt;The most important feature of this release is that the app no longer crashes when typing fast into the search field. Sorry it took me so long to fix this, which was due to the way the app communicated between the AppKit list in the sidebar and the SwiftUI search field. SwiftUI lists have become a lot more performant, so I put a lot of effort into trying to make the entire app SwiftUI but in the end, it was just too sluggish so I reverted to AppKit, but with added traps to avoid crashing.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/app/id522583774&quot; class=&quot;mac_app_button&quot; title=&quot;Download on the Mac App Store&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is a list of the other improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Command-L&lt;/strong&gt; to focus on the man page list at any time. &lt;strong&gt;Shift-Tab&lt;/strong&gt; will focus on the main search field. &lt;strong&gt;Tab&lt;/strong&gt; cycles through everything including the man page itself, so if it looks like nothing has focus, you can use the arrow keys to scroll the page.&lt;/li&gt;
&lt;li&gt;The menus correctly identify and target the front most window and tab - tabs were not always correctly identified before.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pages with Notes&lt;/strong&gt; is a new category in the Sections menus.&lt;/li&gt;
&lt;li&gt;The full path to the selected man page is displayed in the bottom status bar.&lt;/li&gt;
&lt;li&gt;In-page searching is more efficient and searches for 2 characters instead of waiting for 3.&lt;/li&gt;
&lt;li&gt;Accessibility support is improved with more complete VoiceOver descriptions of controls.&lt;/li&gt;
&lt;li&gt;The selected section for each window or tab is now remembered between launches.&lt;/li&gt;
&lt;li&gt;The app quits when the last window is closed. I&#39;m not sure if I should make this an option, so if you have strong feelings about this, please contact me.&lt;/li&gt;
&lt;li&gt;Pages from non-standard locations now have the correct bookmarked scopes to appear.&lt;/li&gt;
&lt;li&gt;The man page display loads faster and no longer scrolls sideways or flickers.&lt;/li&gt;
&lt;li&gt;The list of man pages has the option to use alternating row backgrounds.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;My experiments with using SwiftUI lists were interesting. They have definitely improved a lot over the last year, but they still can&#39;t compete with AppKit lists once you get more than about 5,000 entries. Maybe this years updates will change that as I love the simplicity of using SwiftUI.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Swift Measurements</title>
    <link href="https://troz.net/post/2025/swift-measurements/" />
    <updated>2025-04-16T09:47:17Z</updated>
    <id>https://troz.net/post/2025/swift-measurements/</id>
    <content type="html">&lt;p&gt;Recently, I was working with units and unit conversions in Swift. After a while, I then remembered that Swift has a built-in structure for doing this: &lt;code&gt;Measurement&lt;/code&gt;. This article is an introduction to the power and usage of &lt;code&gt;Measurement&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To use a &lt;code&gt;Measurement&lt;/code&gt;, you initialize it with a value and a unit type. The value must be a &lt;code&gt;Double&lt;/code&gt; and the unit type specifies the type of measurement and the scale of the units. Here&#39;s an example of specifying a length in centimeters:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; length &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1234.56&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lengthMeasurement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Measurement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; length&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; unit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UnitLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;centimeters&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are many supported unit types. They all begin with &lt;code&gt;Unit&lt;/code&gt;, so start typing and see what you get. Once you have the unit type, enter a period and code completion will suggest all the possible units for that type. There&#39;s a huge range, including some very obscure ones:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/measure_init.png&quot; alt=&quot;Measurement init&quot;&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m running this in a playground and when I print &lt;code&gt;lengthMeasurement&lt;/code&gt; I get &lt;code&gt;1234.56 cm&lt;/code&gt; as you&#39;d expect.&lt;/p&gt;
&lt;p&gt;The magic happens when you start formatting the &lt;code&gt;Measurement&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; formattedLength &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lengthMeasurement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;formattedLength&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running this code gave me &lt;strong&gt;12 m&lt;/strong&gt; which is a much more readable version.&lt;/p&gt;
&lt;p&gt;Playing around with larger and smaller values, I got it to show &lt;strong&gt;cm&lt;/strong&gt;, &lt;strong&gt;m&lt;/strong&gt; or &lt;strong&gt;km&lt;/strong&gt;. It even added in the thousands separator when I went to a really big number. Strangely, I couldn&#39;t get it to use a smaller unit than the one I started with. Even with 0.00001 cm, it still didn&#39;t use &lt;strong&gt;mm&lt;/strong&gt;, so I guess the lesson there is to start with the smallest units you want to show.&lt;/p&gt;
&lt;p&gt;Now for the super cool bit. I went into &lt;strong&gt;System Settings -&amp;gt; General -&amp;gt; Language &amp;amp; Region&lt;/strong&gt; and changed &lt;strong&gt;Measurement system&lt;/strong&gt; from &lt;strong&gt;Metric&lt;/strong&gt; to &lt;strong&gt;US&lt;/strong&gt;. Running the playground now, my original value of 1234.56 cm gave a formatted value of &lt;strong&gt;41 ft&lt;/strong&gt;! A larger value gave me &lt;strong&gt;mi&lt;/strong&gt; (miles) and a smaller one showed &lt;strong&gt;in&lt;/strong&gt; (inches). So the formatting uses the system setting to choose what units to show. There&#39;s no need to convert measurements to suit your user&#39;s locale - a formatted &lt;code&gt;Measurement&lt;/code&gt; does that for you!&lt;/p&gt;
&lt;h3&gt;Converting&lt;/h3&gt;
&lt;p&gt;But what if you want to show the conversion? I like to cook and most recipes online assume the everyone uses Fahrenheit for their ovens. My thanks to all the recipe writers who show Celsius as well, it&#39;s greatly appreciated.&lt;/p&gt;
&lt;p&gt;As you can imagine, I spend some time in the kitchen talking to my wrist so that my Apple Watch and Siri can do the conversion for me:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/watch_convert.png&quot; alt=&quot;Temperature conversion&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this screenshot, the result of my query shows both Fahrenheit and Celsius. Let&#39;s try that in the playground.&lt;/p&gt;
&lt;p&gt;Firstly, &lt;code&gt;Measurement&lt;/code&gt; can do conversions, like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fahrenheit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400.0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fahrenheitMeasurement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Measurement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fahrenheit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  unit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UnitTemperature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fahrenheit
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fahrenheitMeasurement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; celsiusMeasurement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fahrenheitMeasurement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;converted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;celsius&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;celsiusMeasurement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prints &lt;strong&gt;400.0 °F&lt;/strong&gt; and &lt;strong&gt;204.44444444444832 °C&lt;/strong&gt;. If you need to access the numeric result of the conversion, you can get the &lt;code&gt;Double&lt;/code&gt; using &lt;code&gt;celsiusMeasurement.value&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But as you&#39;d expect, if you use &lt;code&gt;formatted&lt;/code&gt;, both versions are shown in Celsius because that&#39;s my system setting:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;fahrenheitMeasurement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;celsiusMeasurement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prints &lt;strong&gt;204.444444°C = 204.444444°C&lt;/strong&gt; which while factually indisputable, is not at all helpful. And it shows far too many decimal places - my oven is not that accurate!.&lt;/p&gt;
&lt;p&gt;Here&#39;s where we can use a &lt;code&gt;MeasurementFormatter&lt;/code&gt; which allows us to specify several parameters:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MeasurementFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unitOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;providedUnit
formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;numberFormatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maximumFractionDigits &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unitStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, I tried:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; formattedFahrenheit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fahrenheitMeasurement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; formattedCelsius &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; celsiusMeasurement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;formattedFahrenheit&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;formattedCelsius&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And got &lt;strong&gt;400°F = 204°C&lt;/strong&gt; which is even more useful for cooking than the display my watch showed.&lt;/p&gt;
&lt;p&gt;The key for showing both temperatures was to set the &lt;code&gt;unitOptions&lt;/code&gt; to &lt;code&gt;providedUnit&lt;/code&gt; which made it use the unit specified in the &lt;code&gt;Measurement&lt;/code&gt;.
After that, &lt;code&gt;numberFormatter&lt;/code&gt; is a standard &lt;code&gt;NumberFormatter&lt;/code&gt; with all its options. &lt;code&gt;unitStyle&lt;/code&gt; can be &lt;code&gt;short&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt; or &lt;code&gt;long&lt;/code&gt;. The default is &lt;code&gt;medium&lt;/code&gt; but I specified it so I could experiment with the different styles.&lt;/p&gt;
&lt;p&gt;So any time you find yourself working with units and conversions, give &lt;code&gt;Measurement&lt;/code&gt; a try. It handles localization, conversion and formatting for you.&lt;/p&gt;
&lt;p&gt;If you have any feedback about this article, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Moving to Eleventy</title>
    <link href="https://troz.net/post/2025/eleventy/" />
    <updated>2025-03-19T04:15:33Z</updated>
    <id>https://troz.net/post/2025/eleventy/</id>
    <content type="html">&lt;p&gt;I have been running this blog since 2014 and it&#39;s seen several major changes along the way. I started with WordPress but it felt slow and clumsy, so in 2015 I entered the world of static site generators and &lt;a href=&quot;https://troz.net/post/2015/new-site-for-trozware/&quot;&gt;transferred to Jekyll&lt;/a&gt;. That worked for a couple of years until Jekyll was updated from version 2 to version 3 which broke my setup. At that point I &lt;a href=&quot;https://troz.net/post/2017/moving-to-hugo/&quot;&gt;converted to Hugo&lt;/a&gt; which has worked fine for more than six years.&lt;/p&gt;
&lt;p&gt;But as with &lt;a href=&quot;https://jekyllrb.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jekyll&lt;/a&gt;, updates broke my site and I didn&#39;t know enough about how it all worked to fix it. As a temporary measure, I reverted to an old version of &lt;a href=&quot;https://gohugo.io&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hugo&lt;/a&gt; which got everything working again, but this was not a long term solution.&lt;/p&gt;
&lt;p&gt;After considering a number of options, I decided to try &lt;a href=&quot;https://www.11ty.dev&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Eleventy&lt;/a&gt; and that&#39;s what you&#39;re looking at right now.&lt;/p&gt;

&lt;h2&gt;What Were My Criteria?&lt;/h2&gt;
&lt;p&gt;My requirements for the site haven&#39;t changed much over the years. I still want a simple, lightweight site that is easy to maintain and easy to update:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Responsive&lt;/li&gt;
&lt;li&gt;Fast with minimal client-side Javascript&lt;/li&gt;
&lt;li&gt;Blog style:
&lt;ul&gt;
&lt;li&gt;front page with recent posts&lt;/li&gt;
&lt;li&gt;tags&lt;/li&gt;
&lt;li&gt;archives&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Searching within the site&lt;/li&gt;
&lt;li&gt;Written using Markdown&lt;/li&gt;
&lt;li&gt;Static pages for books and apps&lt;/li&gt;
&lt;li&gt;Contact form&lt;/li&gt;
&lt;li&gt;Syntax highlighting&lt;/li&gt;
&lt;li&gt;Light and dark themes&lt;/li&gt;
&lt;li&gt;Social links&lt;/li&gt;
&lt;li&gt;JSON Feeds&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why Did I Choose Eleventy?&lt;/h2&gt;
&lt;p&gt;At first, I was looking for an option like Jekyll or Hugo that provided themes that I could plug my content into. After a while, I realized that this was what had caused my problems with the previous generators: they were black boxes, written and configured in languages that I didn&#39;t know, so I didn&#39;t know how to fix them when things went wrong.&lt;/p&gt;
&lt;p&gt;While I was daunted by the prospect of having to create my own theme, I was very attracted by the idea of using a generator that I understood, with a site structure that I could control completely.&lt;/p&gt;
&lt;p&gt;Given my language preferences, I was looking for a generator that used Swift, Python or Javascript. I asked for advice on Mastodon and got a number of recommendations, but in the end I decided to try Eleventy for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s written in JavaScript, which is a language that I&#39;m familiar with.&lt;/li&gt;
&lt;li&gt;It has a large and active community, with lots of articles, tutorials and videos available.&lt;/li&gt;
&lt;li&gt;There are a lot of plugins available both from Eleventy and from third parties.&lt;/li&gt;
&lt;li&gt;I can deploy it automatically from Github using CloudFlare Pages, just like I did with Hugo.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How Did I Learn Eleventy?&lt;/h2&gt;
&lt;p&gt;I started on the &lt;a href=&quot;https://www.11ty.dev&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Eleventy website&lt;/a&gt; and then watched the &lt;a href=&quot;https://www.youtube.com/watch?v=BKdQEXqfFA0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Build an 11ty Site in 3 Minutes&lt;/a&gt; video for a quick start guide. This was a bit like the tutorials on how to draw an owl: draw a circle, add another circle, now draw the rest of the owl.&lt;/p&gt;
&lt;p&gt;After that, I found a great guide at &lt;a href=&quot;https://learn-eleventy.pages.dev&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Learn Eleventy&lt;/a&gt;. I got stuck at lesson 18 when I couldn&#39;t get &lt;code&gt;gulp&lt;/code&gt; to work, but by that stage I had learned enough to be able to set up my site, configure the folders and files and use the templates to create the pages that I needed.&lt;/p&gt;
&lt;p&gt;The key for me was to accept that my first draft was going to be HTML only, with no styling. This was completely different to Jekyll or Hugo where you start by choosing a theme and then adding your content, so it felt a bit strange at first, but following the example at &lt;a href=&quot;https://learn-eleventy.pages.dev&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Learn Eleventy&lt;/a&gt;, my first goal was to get my content appearing with my preferred navigation structure.&lt;/p&gt;
&lt;p&gt;Here&#39;s how my files and folders are organized:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/folders_files.png&quot; alt=&quot;Folders and files&quot;&gt;&lt;/p&gt;
&lt;p&gt;At the top level, I have these folders and files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;node_modules: where &lt;code&gt;npm&lt;/code&gt; stores all the required modules, including &lt;code&gt;eleventy&lt;/code&gt; and all of its dependencies.&lt;/li&gt;
&lt;li&gt;public: where the generated files are stored.&lt;/li&gt;
&lt;li&gt;scripts: where I keep my custom Python scripts for bulk edits and configuration.&lt;/li&gt;
&lt;li&gt;src: where the source files are stored - posts, scripts, images, etc.&lt;/li&gt;
&lt;li&gt;notes.md: a file to help me remember what I still need to do.&lt;/li&gt;
&lt;li&gt;package-lock.json: a file that records the exact versions of installed modules.&lt;/li&gt;
&lt;li&gt;package.json: the configuration file for &lt;code&gt;npm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;README.md: for display on the GitHub page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main action is in the &lt;code&gt;src&lt;/code&gt; folder, where I have these folders:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;_data: JSON files with data to fill in the navigation menu, footer links, books and apps pages.&lt;/li&gt;
&lt;li&gt;_includes:
&lt;ul&gt;
&lt;li&gt;layouts: Nunjucks templates that are used to create the pages.&lt;/li&gt;
&lt;li&gt;partials: Nunjucks templates that are used for sections of the pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;apps: Markdown files for each app page.&lt;/li&gt;
&lt;li&gt;books: Markdown files for book pages and samples.&lt;/li&gt;
&lt;li&gt;feeds: Nunjucks templates for the RSS feeds: XML, Atom and JSON.&lt;/li&gt;
&lt;li&gt;filters: Javascript helper functions.&lt;/li&gt;
&lt;li&gt;images: all the images except the favicon.&lt;/li&gt;
&lt;li&gt;js: client-side Javascript files.&lt;/li&gt;
&lt;li&gt;post: Markdown files for each post, with a subfolder for each year.&lt;/li&gt;
&lt;li&gt;scss: the SCSS files, which are compiled to CSS.&lt;/li&gt;
&lt;li&gt;styles: the CSS files, generated from the SCSS files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;src&lt;/code&gt; directory contains a Markdown file or Nunjucks template for each page, the favicon images and the invisible &lt;code&gt;.eleventy.js&lt;/code&gt; file that ties everything together. The Markdown files contain nothing but front matter that points to the relevant layout and adds pagination information if required. Eleventy can use many different templating engines, but the tutorial I followed used &lt;a href=&quot;https://mozilla.github.io/nunjucks/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Nunjucks&lt;/a&gt; and having used &lt;a href=&quot;https://handlebarsjs.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Handlebars&lt;/a&gt; for another project, this was reasonably familiar territory.&lt;/p&gt;
&lt;p&gt;One key concept for me was to realize that a &lt;code&gt;filter&lt;/code&gt; in eleventy is not the same as the &lt;code&gt;filter&lt;/code&gt; function in Javascript or Swift. It&#39;s easier to think of it as a formatter that takes data and returns all or part of it in a different format. For example, the info at the top of each post shows the date, the number of words and an estimated reading time. That&#39;s all produced by a filter written in JavaScript.&lt;/p&gt;
&lt;p&gt;Collections are an incredibly powerful feature of Eleventy, especially when combined with pagination. I&#39;m still learning how to use them effectively, but they allow me to assemble the posts for the main page and for the archives, as well as the list of unique tags for the tags page.&lt;/p&gt;
&lt;p&gt;I&#39;m particularly pleased with the &lt;a href=&quot;https://troz.net/archives/&quot;&gt;Archives&lt;/a&gt; page where I wanted to add a year as the header for each year&#39;s posts. In the template, I set a variable to hold the year, then use a filter to get the year of each post. If the year is different from the previous post, I add a new header to the list.&lt;/p&gt;
&lt;p&gt;I&#39;m not so thrilled with the &lt;a href=&quot;https://troz.net/tags/&quot;&gt;Tags&lt;/a&gt; page, but hopefully I can improve it. The problem is that Eleventy is excellent at collections, but not at collections of collections. I wanted a collection of tags, then a collection of posts for each tag. I haven&#39;t worked out how to do this yet, but I have an interim solution that works.&lt;/p&gt;
&lt;h2&gt;Plugins&lt;/h2&gt;
&lt;p&gt;I&#39;ve used a few plugins although I tried to keep the number down and use my own JavaScript where possible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@11ty/eleventy-plugin-syntaxhighlight&lt;/code&gt; to highlight the code using Prism.js&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@aloskutov/eleventy-plugin-external-links&lt;/code&gt; to make external links open in a new tab.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@11ty/eleventy-plugin-rss&lt;/code&gt; to generate the RSS feeds.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pagefind&lt;/code&gt; to add search to the site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If anyone uses an RSS reader for this site, I&#39;d love to know if the formatting works for you. The various links are in the footer.&lt;/p&gt;
&lt;h2&gt;What Do I Miss From Hugo?&lt;/h2&gt;
&lt;p&gt;In Hugo, I could run the command to add a new post and it would fill in the template for me, with the basic front matter, a header and a &lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&gt; marker to separate the summary from the full post. For Eleventy, I wrote a Python script to create a new post. For displaying the first few paragraphs of a post on the home page, I wrote a filter that looks for the &lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&gt; marker and displays the text up to that point. If there is no marker, it displays the first few paragraphs, up to a certain word count.&lt;/p&gt;
&lt;p&gt;Tags were better handled in Hugo, but I haven&#39;t finished exploring different ways to do them in Eleventy.&lt;/p&gt;
&lt;p&gt;Hugo has a draft server mode that includes posts marked as draft. My collections exclude draft posts so I have to toggle this setting when writing, but I expect I can get around this using environment settings.&lt;/p&gt;
&lt;p&gt;It was easy to generate a table of contents for a post in Hugo as it automatically made all headers into anchor links. I had to fix this manually for the existing posts with a ToC.&lt;/p&gt;
&lt;h2&gt;My Tips for Eleventy Beginners&lt;/h2&gt;
&lt;p&gt;If things don&#39;t appear to be working, stop the eleventy development server and re-start it. I think if you make too many mistakes, it gets lost and gives up reporting them. When it is showing errors, they are usually quite helpful, pointing to the exact line of code that is causing the problem.&lt;/p&gt;
&lt;p&gt;Don&#39;t be afraid to trash the output folder and start again at any time. This is especially important if you&#39;ve deleted or moved any files as Eleventy will not automatically delete the files in the output folder.&lt;/p&gt;
&lt;p&gt;If you&#39;re using SASS or SCSS, compile the CSS into a folder inside the &lt;code&gt;src&lt;/code&gt; folder and then use &lt;code&gt;addPassthroughCopy&lt;/code&gt; to copy these files to the output folder. I made the mistake of compiling the SCSS files directly into the output folder, but when I uploaded the site to CloudFlare, it ran the eleventy command to re-generate the site but didn&#39;t re-compile the SCSS files.&lt;/p&gt;
&lt;p&gt;Sometimes, the live server refresh doesn&#39;t update the styles, so refresh the page manually before panicking.&lt;/p&gt;
&lt;h2&gt;The Plain HTML Site&lt;/h2&gt;
&lt;p&gt;With the structure in place, and all my old posts imported, here&#39;s what the home page looked like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/home_html.png&quot; alt=&quot;home page&quot;&gt;&lt;/p&gt;
&lt;p&gt;And here&#39;s what a post looked like, using Prism.js for syntax highlighting:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/post_html.png&quot; alt=&quot;post page&quot;&gt;&lt;/p&gt;
&lt;p&gt;But now I was stuck. I&#39;m not a complete beginner when it comes to CSS and I knew the sort of look that I wanted, but I knew it would take a lot of my time and effort to get there. However, I use &lt;a href=&quot;https://www.cursor.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Cursor - The AI Code Editor&lt;/a&gt; as my editor so I decided to see how its integrated AI could help me.&lt;/p&gt;
&lt;h2&gt;Styling With AI&lt;/h2&gt;
&lt;p&gt;Here was my first prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a responsive css theme with light and dark modes. It should have a header with the navigation and a footer with links and copyright notices. The home page has a list of blog posts summaries and each post has its own page. The site is mainly text with some images. Use a sans serif font and a maximum width.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As context, I pointed it at the entire &lt;code&gt;src&lt;/code&gt; folder and I added web search to help it find the right information.&lt;/p&gt;
&lt;p&gt;This was amazingly successful, although it took a lot of tweaks and edits. What I liked was that the AI - claude-3.5-sonnet - told me what it was doing at each step. At the beginning, it listed some web sites that it had consulted about responsive design and then it generated a lot of SCSS. Then it told me what it had done and what I needed to do to finish the job.&lt;/p&gt;
&lt;p&gt;One of the tasks listed was:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Navigation should use the nav element with proper structure&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I didn&#39;t know what the proper structure was, so I asked the AI to do it.&lt;/p&gt;
&lt;p&gt;After that, I spent 3 - 4 hours talking to the AI, tweaking the styles and getting the layout just right. It was fascinating. I felt like a designer leaning over the shoulder of a CSS expert, although a real-life expert would have become quickly exasperated by my demands!&lt;/p&gt;
&lt;p&gt;At times, I would ask it to revert the previous change and try something different. Other times I would accept the changes but ask for a modification.&lt;/p&gt;
&lt;p&gt;At one point it got a bit cheeky about my existing code:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&#39;ll modify the &lt;code&gt;apps.njk&lt;/code&gt; template to match the side-by-side layout style we created for books. I&#39;ll also fix the unclosed &lt;code&gt;&amp;lt;/li&amp;gt;&lt;/code&gt; tags in the current file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once I had the basic styling, here are some examples of the more interesting things I asked the AI to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a capsule shape for the tag buttons.&lt;/li&gt;
&lt;li&gt;Generate Prism.js styles for the code blocks, so that they looked similar to Xcode&#39;s default dark and light themes.&lt;/li&gt;
&lt;li&gt;Add a rotation effect to the dark/light toggle with animation.&lt;/li&gt;
&lt;li&gt;Implement a copy button for the code blocks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Importantly, I was able to understand all the SCSS the AI generated.&lt;/p&gt;
&lt;p&gt;Context is really important. In order to get the best results out of any AI, you have to tell it where it should start. As an example, I got my books page styled in a way that I liked, and then gave the AI the books page layout file and the app page layout file and asked it to apply a similar style to the apps page.&lt;/p&gt;
&lt;p&gt;Also, in many cases, it was faster and easier for me to edit the SCSS manually, than to ask the AI.&lt;/p&gt;
&lt;p&gt;I&#39;m sure that I&#39;ll continue to refine the styles over time, but I&#39;m very happy with what I have right now. Using AI to generate the styles was a lot of fun and really demonstrated to me how I can use AI to help me with my work.&lt;/p&gt;
&lt;h2&gt;The Result&lt;/h2&gt;
&lt;p&gt;You&#39;re probably reading this page on my site, so you can see how it looks now. If you&#39;re at &lt;a href=&quot;https://dev.to/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;dev.to&lt;/a&gt; or some other location, you can see my version at &lt;a href=&quot;https://troz.net&quot;&gt;https://troz.net&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I used &lt;a href=&quot;https://lighthouse-metrics.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Lighthouse Metrics&lt;/a&gt; to check the performance of both the old site and the new site. Here are the results:&lt;/p&gt;
&lt;p&gt;For the old site, I got the following scores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance: 69%&lt;/li&gt;
&lt;li&gt;Accessibility: 95%&lt;/li&gt;
&lt;li&gt;Best Practices: 96%&lt;/li&gt;
&lt;li&gt;SEO: 100%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the new site, I got these scores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance: 100%&lt;/li&gt;
&lt;li&gt;Accessibility: 100%&lt;/li&gt;
&lt;li&gt;Best Practices: 96%&lt;/li&gt;
&lt;li&gt;SEO: 100%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;Best Practices&lt;/strong&gt; score is less than 100% because apparently some errors are being logged to the console, but I&#39;m not seeing them so I&#39;m not sure what&#39;s causing that.&lt;/p&gt;
&lt;p&gt;But I am thrilled with the improvement in every other metric.&lt;/p&gt;
&lt;p&gt;Deploying to Cloudflare is also faster - about 35 seconds compared to 48 seconds for Hugo. I think this is probably due to having to download two repos for Hugo - mine and the theme&#39;s.&lt;/p&gt;
&lt;p&gt;One aspect that I&#39;m really pleased with is the site search. Eleventy uses &lt;a href=&quot;https://pagefind.app&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Pagefind&lt;/a&gt; so I started there and found it easy to set up. This is much better than the custom DuckDuckGo search I had embedded on the old site. I&#39;m using the default Pagefind styling for the search results, but I may change that later.&lt;/p&gt;
&lt;p&gt;If you find any bugs or any styling errors, please let me know. And if you would like any further information about how I built the site, please ask. You can contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>macOS Apprentice Update</title>
    <link href="https://troz.net/post/2025/macos-apprentice-update/" />
    <updated>2025-03-06T11:22:21Z</updated>
    <id>https://troz.net/post/2025/macos-apprentice-update/</id>
    <content type="html">&lt;p&gt;The second edition of &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS Apprentice&lt;/a&gt; has been released!&lt;/p&gt;
&lt;p&gt;If you&#39;re a beginner or near-beginner who wants to start learning Swift, SwiftUI and AppKit for building Mac apps, then this is the book for you.&lt;/p&gt;
&lt;p&gt;This edition has been updated for Swift 5.9, macOS 15 and Xcode 16.2.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/maca_cover.png&quot; alt=&quot;Book Cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;macOS Apprentice is a series of multi-chapter tutorials where you&#39;ll learn about developing native macOS apps in Swift, using both SwiftUI — Apple&#39;s newest user interface technology — and AppKit — the venerable UI framework. Along the way, you&#39;ll learn several ways to execute Swift code and you&#39;ll build two fully featured apps from scratch.&lt;/p&gt;
&lt;p&gt;If you&#39;re new to macOS and Swift, or to programming in general, learning how to write an app can seem incredibly overwhelming.&lt;/p&gt;
&lt;p&gt;That&#39;s why you need a guide that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shows you how to write an app step-by-step.&lt;/li&gt;
&lt;li&gt;Uses tons of illustrations and screenshots to make everything clear.&lt;/li&gt;
&lt;li&gt;Guides you in a fun and easy-going manner.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&#39;ll start at the very beginning. The first section assumes you have little to no knowledge of programming in Swift. It walks you through installing Xcode and then teaches you the basics of the Swift programming language. Along the way, you&#39;ll explore several different ways to run Swift code, taking advantage of the fact that you&#39;re developing natively on macOS.&lt;/p&gt;
&lt;p&gt;macOS Apprentice doesn&#39;t cover every single feature of macOS; it focuses on the absolutely essential ones. Instead of just covering a list of features, macOS Apprentice does something much more important: It explains how all the building blocks fit together and what is involved in building real apps. You&#39;re not going to create quick example programs that demonstrate how to accomplish a single feature. Instead, you&#39;ll develop complete, fully-formed apps, while exploring many of the complexities and joys of programming macOS.&lt;/p&gt;
&lt;h3&gt;Contents&lt;/h3&gt;
&lt;p&gt;The book consists of four sections:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 1&lt;/strong&gt;: Install Xcode and learn the basics of programming in Swift. Experiment with several different ways to execute Swift code on your Mac.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 2&lt;/strong&gt;: Use SwiftUI to develop a word-guessing game called Snowman. Learn about data flow in SwiftUI, managing multiple windows, using charts and adding macOS-specific features such as toolbars and menus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 3&lt;/strong&gt;: There are still a number of tasks where AppKit works better than SwiftUI. In this section, build an AppKit app to browse movie data from IMDb, the online movie database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 4&lt;/strong&gt;: Add AppKit to your SwiftUI app and add SwiftUI to your AppKit app in order to add some finishing touches to both of the apps from the previous sections.&lt;/p&gt;
&lt;p&gt;You can read more details of the book contents in the &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/v2.0/chapters/v-introduction&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Introduction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are a more experienced iOS developer who wants to branch out into macOS development, then my previous book - &lt;a href=&quot;https://troz.net/books/macos_tutorials/&quot;&gt;macOS by Tutorials&lt;/a&gt; - might be a better fit, although I think you could still learn a lot of Mac app tips and tricks from this one.&lt;/p&gt;
&lt;h3&gt;Where Can You Find the Resources?&lt;/h3&gt;
&lt;p&gt;You can read the book online at &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;kodeco.com&lt;/a&gt; as part of a Kodeco subscription. The introduction and the first chapter are free to read.&lt;/p&gt;
&lt;p&gt;All the code and extra materials for the book can be downloaded or cloned from &lt;a href=&quot;https://github.com/kodecocodes/maca-materials/tree/editions/2.0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For support, to ask questions or to report any errors, please go to the &lt;a href=&quot;https://forums.kodeco.com/c/books/macos-apprentice/107&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;forum&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Thanks to...&lt;/h3&gt;
&lt;p&gt;This book was made possible by an awesome team. There were a lot of people involved, but I want to give special thanks to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kodeco.com/u/rcritz&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Richard Critz&lt;/a&gt;, the wonderful editor who had the unenviable task of fixing my grammar.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kodeco.com/u/audrey&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Audrey Tam&lt;/a&gt; and &lt;a href=&quot;https://www.kodeco.com/u/ehabamer&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ehab Amer&lt;/a&gt; were the amazing tech editors who had to make sure that it all worked. Audrey also contributed the first chapter on installing and setting up Xcode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole team at &lt;a href=&quot;https://www.kodeco.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco&lt;/a&gt; was great, so a huge thanks to them all. I hope they enjoyed working on the book as much as I did! It&#39;s wonderful to have such a supportive group working to make my content as good as it can be.&lt;/p&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;I would love to hear from anyone who read the book, loved it, hated it, found an error or just wanted to say hello.&lt;/p&gt;
&lt;p&gt;You can contact me directly using any of the contact links at the bottom of this page or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The Mac Menubar and SwiftUI</title>
    <link href="https://troz.net/post/2025/mac_menu_data/" />
    <updated>2025-02-17T07:16:34Z</updated>
    <id>https://troz.net/post/2025/mac_menu_data/</id>
    <content type="html">&lt;p&gt;When you create a Mac app using SwiftUI, you get the standard Mac menubar by default. The &lt;code&gt;commands&lt;/code&gt; modifier lets you customize the menu bar, either by adding, replacing or removing items and menus. You can even add some presets which give a consistent way to add groups of common items.&lt;/p&gt;
&lt;p&gt;The problem comes when you want to communicate back to the SwiftUI views from the menubar. How can you direct your menubar commands to the correct destination? AppKit uses the responder chain, so it effectively broadcasts any menubar message until something handles it. This might be an edit field, a view, a window or even the app itself. SwiftUI doesn&#39;t work like this, but I&#39;ve explored multiple possibilities for passing messages from the menubar to the active window.&lt;/p&gt;

&lt;p&gt;My usual approach is to use &lt;code&gt;NotificationCenter&lt;/code&gt; to send messages. This takes a few steps to set up.&lt;/p&gt;
&lt;h2&gt;NotificationCenter&lt;/h2&gt;
&lt;p&gt;Start by defining a name for your custom notification, like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; menuSelected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;menuSelected&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in your menu item&#39;s action, &lt;code&gt;post&lt;/code&gt; the notification like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;menuSelected&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add an object if you want the notification to be more specific. This allows you to combine several menu items into a single notification name.&lt;/p&gt;
&lt;p&gt;Now, you need to add a listener as a property of your SwiftUI view, receiving it on the main run loop so that UI updates are handled correctly:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; menuSelectedNotification &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;menuSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunLoop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, add an &lt;code&gt;onReceive&lt;/code&gt; modifier to your view, with a closure to process the notification:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;menuSelectedNotification&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; notification &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// process the notification, checking for the object if you added one&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; stringObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do something with the string&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do something else&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main issue with this is that it broadcasts the message to all instances of the view, so if you have multiple windows open and displaying this view, they&#39;ll all receive and process the notification. There are cases where this will be valid - maybe changing the theme of the complete app or setting something else which effects every open window. But mostly, you only want to send the message to the active window.&lt;/p&gt;
&lt;p&gt;You can improve this by using the &lt;code&gt;appearsActive&lt;/code&gt; environment value to see if the current view is active. Truncated for ease of reading, &lt;code&gt;ContentView&lt;/code&gt; now looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appearsActive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; appearsActive

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; menuSelectedNotification &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;menuSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunLoop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// UI defined here&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;menuSelectedNotification&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; notification &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; appearsActive &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; stringObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// process the new string&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;onReceive&lt;/code&gt; closure starts with a &lt;code&gt;guard&lt;/code&gt; to check that the view is active. If it&#39;s not, the closure returns immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You may have used &lt;code&gt;controlActiveState&lt;/code&gt; for this in the past, but it has been deprecated in favor of &lt;code&gt;appearsActive&lt;/code&gt; which is easier to use as it&#39;s a Boolean instead of an enum.&lt;/p&gt;
&lt;p&gt;This looks like exactly what we need, but there&#39;s a problem. If you merge multiple windows into tabs, the &lt;code&gt;appearsActive&lt;/code&gt; property will be the same for all the tabs in the window.&lt;/p&gt;
&lt;p&gt;So basically, this only works if you disallow tabbing for your windows, so it&#39;s not a complete solution.&lt;/p&gt;
&lt;h2&gt;Failed Attempts&lt;/h2&gt;
&lt;p&gt;My next attempt was to step into AppKit and have &lt;code&gt;NSApp&lt;/code&gt; send a selector through the responder chain. This looked like it should work, but I could never get it to, and it looked clunky&lt;/p&gt;
&lt;p&gt;Looking though an Apple tutorial, it appears that they prefer to use &lt;code&gt;@FocusedBinding&lt;/code&gt; and &lt;code&gt;focusedValue&lt;/code&gt;. This worked in their sample app, but there was something different about my data structure that meant it never worked for me, even after extracting my data into a separate data type.&lt;/p&gt;
&lt;p&gt;Then I came across &lt;code&gt;focusedSceneObject&lt;/code&gt;. This required me to create an &lt;code&gt;@ObservableObject&lt;/code&gt; data type, but then it worked perfectly, even in tabbed windows. But this is not viable going forward, as I have switched to using &lt;code&gt;Observation&lt;/code&gt;. However the docs did point me in the right direction.&lt;/p&gt;
&lt;h2&gt;Success at Last&lt;/h2&gt;
&lt;p&gt;After a bit of research and a &lt;em&gt;lot&lt;/em&gt; of testing, I finally came up with a complete solution, using &lt;code&gt;@FocusedBinding&lt;/code&gt; and &lt;code&gt;focusedSceneValue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, you need a data object to handle your menu messages. This can be a &lt;code&gt;struct&lt;/code&gt; or an observable &lt;code&gt;class&lt;/code&gt;. My sample app has both in &lt;strong&gt;Symbol.swift&lt;/strong&gt; with one commented out, so you can test this. The data model has properties for an icon and a color and you can also have it choose a random icon and color. For convenience, it has two static arrays to provide the names and colors for the menus and pickers.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Symbol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;globe&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue

  &lt;span class=&quot;token keyword&quot;&gt;mutating&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;chooseRandomSymbolAndColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// choose random values&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or as a class:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@Observable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Symbol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;globe&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue

  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;chooseRandomSymbolAndColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// choose random values&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you need to extend &lt;code&gt;FocusedValues&lt;/code&gt; so it has a key to your data type. This has become a lot less verbose with the new &lt;code&gt;@Entry&lt;/code&gt; macro. The binding must be to an optional:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FocusedValues&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Entry&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedSymbol&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Symbol&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In your &lt;code&gt;ContentView&lt;/code&gt; (or wherever you&#39;re using this property), set the data property as the &lt;code&gt;focusedSceneValue&lt;/code&gt; for this key:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; symbol &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Symbol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// UI defined here&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focusedSceneValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedSymbol&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $symbol&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last step is to use this in the menu (again, truncated for brevity):&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MenuDataApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@FocusedBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedSymbol&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedSymbol

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;commands &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;CommandMenu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Symbol&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Menu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Symbol&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Symbol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; selectedSymbol&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;✔︎ &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              selectedSymbol&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedSymbol &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Random&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          selectedSymbol&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chooseRandomSymbolAndColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyboardShortcut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedSymbol &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important features here are the &lt;code&gt;@FocusedBinding&lt;/code&gt; property at the top and the fact that the menu items use optional chaining to access this property if it exists. If you have no open windows, this will be nil, otherwise it will point to the instance of the data property in the active window.&lt;/p&gt;
&lt;p&gt;To improve the user experience, I added a &lt;code&gt;disabled&lt;/code&gt; modifier to the menus and the button so that the menus are not active unless there&#39;s a valid &lt;code&gt;selectedSymbol&lt;/code&gt;. Unfortunately, you can&#39;t disable an entire &lt;code&gt;CommandMenu&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The wonderful part of this is that it works with windows and tabbed windows. Hurray!&lt;/p&gt;
&lt;h3&gt;Showing the Current Selections in the Menu&lt;/h3&gt;
&lt;p&gt;The ugly part is that the menu item indicating the current choice is set to show a check mark manually, instead of using the standard menu checkmark. But you can&#39;t bind a &lt;code&gt;@FocusedBinding&lt;/code&gt; property to a &lt;code&gt;Picker&lt;/code&gt; as it&#39;s selection.&lt;/p&gt;
&lt;p&gt;&lt;s&gt;My solution was to use a &lt;code&gt;Picker&lt;/code&gt; with a local state property. Then I track for changes to the &lt;code&gt;@FocusedBinding&lt;/code&gt; property and to the local property. When either changes, the other is set to match, remembering that the &lt;code&gt;@FocusedBinding&lt;/code&gt; property may be nil:
It was important to add the &lt;code&gt;onChange&lt;/code&gt; modifiers to the &lt;code&gt;ContentView&lt;/code&gt;. I tried them on the &lt;code&gt;Picker&lt;/code&gt; first, but they only got updated as the menu opened.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve updated the project, thanks to &lt;a href=&quot;https://github.com/malhal&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Malcolm Hall&lt;/a&gt; who added this comment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;onChange is just for external actions. For linking states its Binding and it has an init that handles conversion from optional, that allows you to access the keypath binding to the symbol&#39;s colour and name you were missing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, the picker code looks like this (I omitted the color picker to make for a shorter and more readable code block):&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; selectedSymbol &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$selectedSymbol&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Symbol&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; selectedSymbol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Symbol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pickerStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;inline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Symbol&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also made the pickers inline for better visibility. With this version, I really like the way the menus use a &lt;code&gt;Text&lt;/code&gt; view to display the header with no possible selections, when there&#39;s no &lt;code&gt;selectedSymbol&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2025/mac_menu_data.png&quot; alt=&quot;Final app&quot;&gt;&lt;/p&gt;
&lt;p&gt;So this is my new technique for communications between the menubar and SwiftUI. It takes a bit of setting up, but then it works really well. For a more complex app, I might need more bindings, or I could merge the relevant data objects in a larger struct or class.&lt;/p&gt;
&lt;p&gt;Going back to Apple&#39;s example which used &lt;code&gt;@FocusedBinding&lt;/code&gt; and &lt;code&gt;focusedValue&lt;/code&gt;, I think this worked because they had a &lt;code&gt;NavigationSplitView&lt;/code&gt; and the focused value was the selected item, so there was an object to focus on. From the Apple&#39;s docs for &lt;code&gt;focusedSceneValue&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use this method instead of focusedValue(&lt;em&gt;:&lt;/em&gt;:) for values that must be visible regardless of where focus is located in the active scene.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is what solved it for me because there was no real focus element in my view. I was able to go back to the Apple sample app and get it working using  &lt;code&gt;focusedSceneValue&lt;/code&gt; instead of &lt;code&gt;focusedValue&lt;/code&gt;, so I think this is a solid technique.&lt;/p&gt;
&lt;p&gt;I&#39;m sure there are other ways to do this, so if you have an alternative method or can suggest any improvements to my technique, I&#39;d love to hear about it.&lt;/p&gt;
&lt;h2&gt;The Code&lt;/h2&gt;
&lt;p&gt;The sample project is available &lt;a href=&quot;https://github.com/trozware/MacMenuComms&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;on GitHub&lt;/a&gt;. Check out the &lt;code&gt;main&lt;/code&gt; branch for the final version of the code, but explore the other branches to see my experiments along the way.&lt;/p&gt;
&lt;p&gt;Here they are, in order of progress:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;basic_ui&lt;/strong&gt;: The starter version of the app with the UI set up for selecting an icon and a color.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;menu&lt;/strong&gt;: Menus added but not working.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;notifications&lt;/strong&gt;: Using &lt;code&gt;NotificationCenter&lt;/code&gt; to send menu messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;focusedSceneObject&lt;/strong&gt;: Works but only with &lt;code&gt;@ObservableObject&lt;/code&gt; data types.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;main&lt;/strong&gt;: The technique I&#39;ve settled on with &lt;code&gt;@FocusedBinding&lt;/code&gt; and &lt;code&gt;focusedSceneValue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>macOS by Tutorials 3.0</title>
    <link href="https://troz.net/post/2024/macos_tutorials_3/" />
    <updated>2024-12-23T04:22:15Z</updated>
    <id>https://troz.net/post/2024/macos_tutorials_3/</id>
    <content type="html">&lt;p&gt;macOS by Tutorials Edition 3.0 is now available!&lt;/p&gt;
&lt;p&gt;The book is available for purchase or update at &lt;a href=&quot;https://sarahreichelt.gumroad.com/l/oximx&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gumroad&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you previously bought the first edition of this book from either Kodeco or Amazon, please &lt;a href=&quot;mailto:books@troz.net?subject=macOS%20by%20Tutorials%20Discount&quot;&gt;email me&lt;/a&gt; for a 50% discount code.&lt;/p&gt;
&lt;p&gt;If you already bought the second edition from me via Gumroad, this is a free update that you can download from your Gumroad library.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://troz.net/books/macos_tutorials/&quot;&gt;&lt;img src=&quot;https://troz.net/images/2024/mos_cover_horizontal.png&quot; alt=&quot;Book cover&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click the image for more details, and if you’d like to check out the start of the book including the table of contents and the first chapter, you can read it online at &lt;a href=&quot;https://troz.net/books/mos_sample.html&quot;&gt;macOS by Tutorials Sample&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The major changes in this edition include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Updated projects to include new features introduced in macOS 15 Sequoia and Xcode 16.&lt;/li&gt;
&lt;li&gt;More granular chapter organization. The table of contents is now more of an index so you can find the sections you need, with each chapter being more focussed on a single topic.&lt;/li&gt;
&lt;li&gt;All projects use Xcode&#39;s folder structure.&lt;/li&gt;
&lt;li&gt;Tabs use the new syntax.&lt;/li&gt;
&lt;li&gt;Previews use the new &lt;code&gt;@Previewable&lt;/code&gt; macro.&lt;/li&gt;
&lt;li&gt;Tracking the active window uses the &lt;code&gt;@Entry&lt;/code&gt; macro.&lt;/li&gt;
&lt;li&gt;The app in section 4 demonstrates the new windowing options.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you already own the book and have enjoyed it or found it useful, I&#39;d really appreciate it if you could leave a rating or review at the book&#39;s page in your Gumroad library.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Swift Format in Xcode</title>
    <link href="https://troz.net/post/2024/swift_format/" />
    <updated>2024-11-06T06:29:20Z</updated>
    <id>https://troz.net/post/2024/swift_format/</id>
    <content type="html">&lt;p&gt;In Xcode 16, Apple quietly introduced the ability to format your Swift files using Swift Format. I&#39;m a long-time user of SwiftLint, but having such a tool built into Xcode would be a great convenience, so I decided to give it a try. Here is my description of why I use such a tool, how well it works compared to the alternatives, and how I configured it for my own purposes.&lt;/p&gt;

&lt;h2&gt;Why Format Your Code?&lt;/h2&gt;
&lt;p&gt;I have always been a big fan of consistent code formatting. As I wrote years ago in &lt;a href=&quot;https://troz.net/post/2018/swiftlint/&quot;&gt;Consistent Swift Style&lt;/a&gt;, we read code far more often than we write it, so anything that improves readability is great for productivity, even for a solo developer. If you work as part of a team, consistency is even more important as you don&#39;t want other people to have to spend their valuable time trying to understand your code, or the other way round.&lt;/p&gt;
&lt;p&gt;I have been an editor and writer for &lt;a href=&quot;https://www.kodeco.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco&lt;/a&gt; (formerly raywenderlich.com) for many years and with so many different authors on the team, it was crucial for there to be a consistent style. This is why I always use SwiftLint in my Kodeco projects, using the configuration file found here: &lt;a href=&quot;https://github.com/kodecocodes/swift-style-guide/blob/main/SWIFTLINT.markdown&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco Swift Style Guide&lt;/a&gt;. For my own projects, I vary this configuration slightly, but I always use SwiftLint. Similarly, when doing web development, I always use &lt;a href=&quot;https://prettier.io&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Prettier&lt;/a&gt; which integrates beautifully with Visual Studio Code so that my files are automatically prettified on save.&lt;/p&gt;
&lt;h2&gt;Swift Format&lt;/h2&gt;
&lt;p&gt;The name Apple chose is logical but confusing. I had experimented with Nick Lockwood&#39;s &lt;a href=&quot;https://github.com/nicklockwood/SwiftFormat&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftFormat&lt;/a&gt; a few years ago, but found that I preferred SwiftLint, so I stuck with the latter. When I saw that Xcode included Swift Format, I assumed that they had acquired Nick&#39;s formatter but it appears to be a different tool, officially called &lt;a href=&quot;https://github.com/swiftlang/swift-format&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;swift-format&lt;/a&gt;. It&#39;s downloaded as part of the toolchain when you install Xcode 16, so no further installation is required.&lt;/p&gt;
&lt;h2&gt;Test Code&lt;/h2&gt;
&lt;p&gt;I created a test project in Xcode 16 and wrote a chunk of Swift and SwiftUI for testing. I deliberately formatted it badly so as to see which formatter did what to it, so don&#39;t hate me for this nonsensical code:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;NavigationSplitView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sidebar&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; row &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; detail&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someComputedProperty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onAppear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;perform&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; createTestData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;createTestData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Row #&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;downloadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://zenquotes.io/api/quotes&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; address&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; task &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dataTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; encoding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    task&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; someComputedProperty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&quot;
    This is a multiline string,
    that is a computed property which can be tricky to format.

    It has nothing to do with the list on the side, but I wanted to see how swift-format would handle it.
    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;longFunctionNameThatDoesSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is a long function name that does something&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;FunctionwithLotsOfArguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg3&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg4&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg5&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg6&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg7&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg8&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg9&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg10&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Yes, this deliberately starts with an uppercase letter&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;FunctionwithLotsOfArguments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg3&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg4&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg5&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg6&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg7&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg8&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg9&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arg10&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is a function with a lot of arguments&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token other-directive property&quot;&gt;#Preview&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some comments on this code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I use 2 space indentation, but Xcode creates files with 4 spaces, so indentation is a mess.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;NavigationSplitView&lt;/code&gt; is not using multiple trailing closure syntax.&lt;/li&gt;
&lt;li&gt;I&#39;ve left unnecessary blank lines in the code.&lt;/li&gt;
&lt;li&gt;The networking code is not what I would write, but it was suggested by GitHub Copilot.&lt;/li&gt;
&lt;li&gt;Multi-line strings are often difficult to format.&lt;/li&gt;
&lt;li&gt;The two functions at the end really need to spread over more lines and the last one should start with a lowercase letter.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using Other Formatters&lt;/h2&gt;
&lt;p&gt;Before checking out Xcode&#39;s &lt;code&gt;swift-format&lt;/code&gt;, I want to try &lt;strong&gt;SwiftLint&lt;/strong&gt; and &lt;strong&gt;Prettier&lt;/strong&gt;. I have SwiftLint installed already, so I can run it from the Terminal. After I used &lt;code&gt;cd&lt;/code&gt; to step into the project&#39;s code folder, I ran:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;swiftlint ContentView.swift&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is using the default SwiftLint configuration and gave this result (I deleted the folder path but you&#39;ll still have to scroll sideways to read this.):&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Linting Swift files at paths ContentView.swift
Linting &#39;ContentView.swift&#39; (1/1)
ContentView.swift:28:25: warning: Comma Spacing Violation: There should be no space before and one after any comma (comma)
ContentView.swift:51:3: error: Function Parameter Count Violation: Function should have 5 parameters or less: it currently has 10 (function_parameter_count)
ContentView.swift:51:8: error: Identifier Name Violation: Function name &#39;FunctionwithLotsOfArguments(arg1:arg2:arg3:arg4:arg5:arg6:arg7:arg8:arg9:arg10:)&#39; should start with a lowercase character (identifier_name)
ContentView.swift:47:1: warning: Line Length Violation: Line should be 120 characters or less; currently it has 123 characters (line_length)
ContentView.swift:51:1: warning: Line Length Violation: Line should be 120 characters or less; currently it has 147 characters (line_length)
ContentView.swift:28:39: warning: Non-Optional String &lt;-&gt; Data Conversion Violation: Prefer using UTF-8 encoded strings when converting between `String` and `Data` (non_optional_string_data_conversion)
ContentView.swift:59:1: warning: Trailing Newline Violation: Files should have a single trailing newline (trailing_newline)
ContentView.swift:40:1: warning: Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
ContentView.swift:26:62: warning: Unused Closure Parameter Violation: Unused parameter in a closure should be replaced with _ (unused_closure_parameter)
ContentView.swift:26:72: warning: Unused Closure Parameter Violation: Unused parameter in a closure should be replaced with _ (unused_closure_parameter)
ContentView.swift:20:1: warning: Vertical Whitespace Violation: Limit vertical whitespace to a single empty line; currently 2 (vertical_whitespace)
Done linting! Found 11 violations, 2 serious in 1 file.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SwiftLint has the ability to fix some issues, so I ran:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;swiftlint &lt;span class=&quot;token parameter variable&quot;&gt;--fix&lt;/span&gt; ContentView.swift&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result was:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Correcting Swift files at paths ContentView.swift
Correcting &#39;ContentView.swift&#39; (1/1)
ContentView.swift:28:25 Corrected Comma Spacing
ContentView.swift:58:1 Corrected Trailing Newline
ContentView.swift:40:1 Corrected Trailing Whitespace
ContentView.swift:26:62 Corrected Unused Closure Parameter
ContentView.swift:26:72 Corrected Unused Closure Parameter
ContentView.swift:19:1 Corrected Vertical Whitespace
Done correcting 1 file!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This reduced the number of problems to 5, with 2 serious. What it fixed was mostly whitespace issues, but it also removed the two used parameters from the networking closure. It did not fix the indentation.&lt;/p&gt;
&lt;p&gt;Next on my list was Prettier, so I reverted to the badly formatted code and opened the project folder in Visual Studio Code. I had already installed the Swift and Prettier extensions, so I opened and re-saved the file to make Prettier do its thing. This did a slightly better job than SwiftLint: it fixed the indentation and spread the long function call and definition over three lines. It also removed the two parameters from the networking closure and added some line feeds to make the code more readable.&lt;/p&gt;
&lt;h2&gt;swift-format&lt;/h2&gt;
&lt;p&gt;Finally, it was time to see what &lt;code&gt;swift-format&lt;/code&gt; can do. After reverting to the original code again, I chose &lt;strong&gt;Editor -&amp;gt; Structure -&amp;gt; Format File with &#39;swift-format&#39;&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/swift-format.png&quot; alt=&quot;Swift Format&quot;&gt;&lt;/p&gt;
&lt;p&gt;This fixed the indentation and the whitespace issues. Interestingly, it split the long function call and definition over multiple lines, but still with more than one argument per line. It did not remove the two parameters from the networking closure. I think I may be running into the difference between a formatter and a linter.&lt;/p&gt;
&lt;p&gt;While I was impressed overall, I disliked the way it removed spaces around the range operator. I prefer:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Row #&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But after using swift-format, it was:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Row #&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Configuring swift-format&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;swift-format&lt;/code&gt; tool is installed as part of the Xcode toolchain, so the first step in configuring it was to locate it, using the instructions found &lt;a href=&quot;https://github.com/swiftlang/swift-format&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; under &lt;strong&gt;Included in the Swift Toolchain&lt;/strong&gt;. In Terminal, I used:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;xcrun &lt;span class=&quot;token parameter variable&quot;&gt;--find&lt;/span&gt; swift-format&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which gave me: &lt;strong&gt;/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-format&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Using this path, I was able to export the default configuration file (I included the full path to the tool in the command, but faked it here for ease of reading):&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;path-to/swift-format dump-configuration &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; swift-format-default-config.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Opening the file, it has a bunch of settings and a long list of rules. I&#39;m not going to go through them, but the file is in the project folder, so you can see it if you download the project from &lt;a href=&quot;https://github.com/trozware/swift-format-tests&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; and switch to the &lt;code&gt;swift-format&lt;/code&gt; branch.&lt;/p&gt;
&lt;p&gt;The indentation is set to use 2 spaces. I assume that was picked up from my Xcode settings, since it&#39;s different to the default. The setting I want to change is:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;spacesAroundRangeFormationOperators&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before making any changes, I need to save the file in a location and with a name that &lt;code&gt;swift-format&lt;/code&gt; can find. In my tests, I found that saving it as &lt;strong&gt;.swift-format&lt;/strong&gt; in my home directory meant that it applied to every Xcode project. To do this, I followed this sequence in Finder:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open my &lt;strong&gt;Home&lt;/strong&gt; directory.&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Command-Shift-Period&lt;/strong&gt; to show hidden files.&lt;/li&gt;
&lt;li&gt;Option-drag &lt;strong&gt;swift-format-default-config.json&lt;/strong&gt; to the Home directory.&lt;/li&gt;
&lt;li&gt;Rename the file to &lt;strong&gt;.swift-format&lt;/strong&gt; agreeing to the warning about making it invisible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I edited it, changing the setting to:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;spacesAroundRangeFormationOperators&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back in Xcode, I pressed &lt;strong&gt;Shift-Control-I&lt;/strong&gt; to reformat the file. This time, the range operator was formatted with spaces as I wanted.&lt;/p&gt;
&lt;p&gt;To make future configuration changes easier, I created an alias to &lt;strong&gt;.swift-format&lt;/strong&gt; in my Home folder and called it &lt;strong&gt;swift-format alias.json&lt;/strong&gt;. This gives me a visible link that will open in a JSON editor. With that in place, I pressed &lt;strong&gt;Shift-Command-Period&lt;/strong&gt; again to hide invisible files.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;swift-format&lt;/code&gt; has a lint option, so in Terminal, I ran:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;swift-format lint ContentView.swift&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which reported:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ContentView.swift:55:8: warning: [AlwaysUseLowerCamelCase] rename the function &#39;FunctionwithLotsOfArguments&#39; using lowerCamelCase&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I was going to do this on a regular basis, I would want to add the path to the&lt;code&gt; swift-format&lt;/code&gt; tool to my &lt;code&gt;PATH&lt;/code&gt; variable or create an alias to it in my &lt;strong&gt;.zshrc&lt;/strong&gt; file, but it was valid information.&lt;/p&gt;
&lt;p&gt;One extra tip: use &lt;strong&gt;Editor -&amp;gt; Structure -&amp;gt; Format to Multiple Lines&lt;/strong&gt; or &lt;strong&gt;Control-M&lt;/strong&gt; to split long lines into multiples. This is a much more consistent way of spreading out long function calls and definitions. You&#39;d think that &lt;strong&gt;Editor -&amp;gt; Structure -&amp;gt; Reformat to Width&lt;/strong&gt; would do the opposite, but in my tests, it did either nothing, or the same thing as &lt;strong&gt;Format to Multiple Lines&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I am going to use &lt;code&gt;swift-format&lt;/code&gt; in my projects from now on. It&#39;s less intrusive than SwiftLint and I like that it&#39;s built into the Xcode toolchain. I am going to assign a different keyboard shortcut as &lt;strong&gt;Shift-Control-I&lt;/strong&gt; is an awkward combination to reach on my Ergodox Moonlander keyboard.&lt;/p&gt;
&lt;p&gt;Conveniently, the command applies to the entire active file, regardless of selection. I&#39;m used to using &lt;strong&gt;Command-A -&amp;gt; Control-I&lt;/strong&gt; to fix indentation issues, but this method only requires a single key command.&lt;/p&gt;
&lt;p&gt;For future reference, be aware of this note from the &lt;a href=&quot;https://github.com/swiftlang/swift-format&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;swift-format GitHub page&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: No default Swift code style guidelines have yet been proposed. The style that is currently applied by swift-format is just one possibility, and the code is provided so that it can be tested on real-world code and experiments can be made by modifying it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am hoping that Apple or the Swift team does develop a style guide while still allowing for customization, but in the meantime, I will continue to tweak the settings to suit myself. I presume that if I include a &lt;code&gt;.swift-format&lt;/code&gt; file in my project, it will override any other settings. This would be great for distributing tutorial projects with a consistent style. I&#39;d also love to be able to set the formatter to run on save, like Prettier does in Visual Studio Code.&lt;/p&gt;
&lt;p&gt;The test project is on GitHub at &lt;a href=&quot;https://github.com/trozware/swift-format-tests&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;trozware/swift-format-tests&lt;/a&gt; if you want to try it out for yourself. There&#39;s a separate branch for each formatter, with &lt;code&gt;main&lt;/code&gt; holding the original, badly formatted code.&lt;/p&gt;
&lt;p&gt;If you have any other thoughts or suggestions, I&#39;d love to hear them. You can contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this, or any of my articles useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>App Permissions on macOS Sequoia</title>
    <link href="https://troz.net/post/2024/sequoia_app_permissions/" />
    <updated>2024-10-02T01:33:26Z</updated>
    <id>https://troz.net/post/2024/sequoia_app_permissions/</id>
    <content type="html">&lt;p&gt;In 2012, with OS X Mountain Lion, Apple added a feature called &lt;a href=&quot;https://en.wikipedia.org/wiki/Gatekeeper_(macOS)&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gatekeeper&lt;/a&gt;. It had been available earlier as a command line utility, but this was the first time they made it accessible through System Preferences. Gatekeeper allowed users to control which apps could be installed on their Macs by offering three options: allow apps from &lt;strong&gt;App Store&lt;/strong&gt;, &lt;strong&gt;App Store and identified developers&lt;/strong&gt; or &lt;strong&gt;Anywhere&lt;/strong&gt;. This was the start of Apple trying to lock Macs down, similarly to how iOS devices are locked down, but it allowed power users to install any apps they wanted.&lt;/p&gt;
&lt;p&gt;In macOS Sierra (2019), the &lt;strong&gt;Anywhere&lt;/strong&gt; option was removed. It was still possible to open any app by right-clicking and selecting &lt;strong&gt;Open&lt;/strong&gt;. You had to get past a couple of warning dialogs, but it worked. Now, in macOS Sequoia, even that has gone. So how can you open an app that isn&#39;t signed by an identified developer?&lt;/p&gt;
&lt;p&gt;TL;DR: You can still run unsigned apps, but it&#39;s a bit more difficult. After trying once, you have to go to &lt;strong&gt;System Settings -&amp;gt; Privacy &amp;amp; Security&lt;/strong&gt;, scroll to the end and click &lt;strong&gt;Open Anyway&lt;/strong&gt; for that app.&lt;/p&gt;

&lt;h3&gt;Testing on my Computer&lt;/h3&gt;
&lt;p&gt;I created a test app called &lt;strong&gt;UnsignedApp&lt;/strong&gt;, and made sure to leave the developer team set to &lt;strong&gt;None&lt;/strong&gt; so the app could not be code signed. I archived the app in Xcode and clicked &lt;strong&gt;Distribute App&lt;/strong&gt; in the Organizer window. Instead of selecting one of the standard options, I chose &lt;strong&gt;Custom&lt;/strong&gt; and clicked &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/custom_distrib.png&quot; alt=&quot;Custom distribution&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then I selected &lt;strong&gt;Copy App&lt;/strong&gt; as it was the only option that wasn&#39;t going to sign the app.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/copy_app_option.png&quot; alt=&quot;Copy App option&quot;&gt;&lt;/p&gt;
&lt;p&gt;I saved the app to my desktop and opened it without any problems. Next, I moved it into my &lt;strong&gt;Applications&lt;/strong&gt; folder and tried again. Still no problems. So presumably, even though it was unsigned, it was OK because I had created it. Next, I tried running it when logged in as a different user on my computer. This still worked, rather to my surprise. So I assume that my computer is registered to my developer account and any apps I create are allowed to run on it, regardless of user.&lt;/p&gt;
&lt;h3&gt;Testing on Someone Else&#39;s Computer&lt;/h3&gt;
&lt;p&gt;The problems started when I tried running the app on someone else&#39;s Mac. This dialog appeared (presumably the default button says Move to Trash where appropriate):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/unsigned_app_1.png&quot; alt=&quot;Unsigned app warning&quot;&gt;&lt;/p&gt;
&lt;p&gt;I clicked &lt;strong&gt;Done&lt;/strong&gt; to close the dialog and tried the old right-click and &lt;strong&gt;Open&lt;/strong&gt; trick. No good - this showed the identical dialog.&lt;/p&gt;
&lt;p&gt;Finally, I remembered reading something about this on Mastodon. I forget who posted it, so if it was you, thank you very much because Apple certainly wasn&#39;t helping here. I opened &lt;strong&gt;System Settings&lt;/strong&gt; and went to &lt;strong&gt;Privacy &amp;amp; Security&lt;/strong&gt;. After scrolling all the way down to the end, past the sign marked &lt;a href=&quot;https://www.azquotes.com/quote/354892&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Beware of the Leopard&lt;/strong&gt;&lt;/a&gt; (with apologies to the late, great Douglas Adams), I found this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/unsigned_app_2.png&quot; alt=&quot;System Settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is the Gatekeeper setting with the remaining two options, but added on is a message about my app being blocked. I clicked &lt;strong&gt;Open Anyway&lt;/strong&gt; and got this dialog:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/unsigned_app_3.png&quot; alt=&quot;Open Anyway dialog&quot;&gt;&lt;/p&gt;
&lt;p&gt;At least this gave me an option to proceed. I clicked &lt;strong&gt;Open Anyway&lt;/strong&gt; and got this scary (and badly written) message:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/unsigned_app_4.png&quot; alt=&quot;Final warning&quot;&gt;&lt;/p&gt;
&lt;p&gt;I authenticated and finally, the app ran. But there&#39;s one more interesting twist. The app has the ability to show its running location. When I ran the app from the &lt;strong&gt;Applications&lt;/strong&gt; folder, it was in /Applications as expected, but when I deleted it from there and tried running it from the &lt;strong&gt;Downloads&lt;/strong&gt; folder, macOS moved it into a hidden AppTranslocation folder and ran it from there:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/unsigned_app_5.png&quot; alt=&quot;Running from secure location&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Using Terminal&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;spctl&lt;/code&gt; command line utility used to allow full manual control of Gatekeeper. In macOS Sequoia, it has lost most of its power, but you can still use it to re-enable the &lt;strong&gt;Anywhere&lt;/strong&gt; option in &lt;strong&gt;System Settings -&amp;gt; Privacy &amp;amp; Security -&amp;gt; Allow applications from&lt;/strong&gt; using this command:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;spctl --global-disable&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/unsigned_app_6.png&quot; alt=&quot;After running spctl command&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you&#39;re already in &lt;strong&gt;System Settings -&amp;gt; Privacy &amp;amp; Security&lt;/strong&gt;, go to a different settings page and back again to see the change.&lt;/p&gt;
&lt;p&gt;If you choose &lt;strong&gt;Anywhere&lt;/strong&gt;, you have to authenticate, but then you can run any app. I wouldn&#39;t bet on this staying around forever, but for the moment, it&#39;s nice to see it&#39;s still there. The extra choice disappears after about 8 minutes if you don&#39;t select it.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;You can work around this limitation, but proceed with caution. There are bad Mac apps out there, so don&#39;t use these workarounds unless you&#39;re confident of the source. Be particularly wary of any app that asks you to authenticate.&lt;/p&gt;
&lt;p&gt;Apple has always been very keen to keep iOS devices locked down. They maintain that this is essential for security but given some of the scams in the App Store, that is a debatable point. The Mac has historically always been open and allowed users to do what they wanted. Over the past few years we have seen Apple gradually closing down the Mac to bring it more in line with the other devices.&lt;/p&gt;
&lt;p&gt;With my tech support hat on I can see the benefits of this, but as a power user, I want to have the tools to work around it if necessary.&lt;/p&gt;
&lt;p&gt;As a developer, I realize that it is now virtually impossible to release any Mac apps without having a developer account. Mac apps must be notarized by Apple so that they fall into the &lt;strong&gt;Known Developers&lt;/strong&gt; category, regardless of whether they are distributed through the App Store or by other means.&lt;/p&gt;
&lt;p&gt;If you have any thoughts or suggestions about this, contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Xcode Folders &amp; Groups</title>
    <link href="https://troz.net/post/2024/xcode_folders_groups/" />
    <updated>2024-09-17T04:12:42Z</updated>
    <id>https://troz.net/post/2024/xcode_folders_groups/</id>
    <content type="html">&lt;p&gt;In Xcode 16, project files and folders are arranged differently in the Project navigator. What used to be a &lt;strong&gt;group&lt;/strong&gt; is now a &lt;strong&gt;folder&lt;/strong&gt;, and this simple change has some interesting effects. At first, I was against the new scheme - in fact in my &lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/&quot;&gt;SwiftUI for Mac 2024&lt;/a&gt; article, I specifically recommended reverting back to the old group method. But after doing some more reading and testing, I think the answer is not so clear cut.&lt;/p&gt;
&lt;p&gt;Since Xcode 15 doesn&#39;t work on macOS Sequoia, now&#39;s my last chance to create sample apps with both Xcode 15 and Xcode 16 in macOS Sonoma, and compare the two.&lt;/p&gt;

&lt;p&gt;Using Xcode 15.4 and Xcode 16.0, I created a macOS app using Swift and SwiftUI. Both projects were placed under &lt;code&gt;git&lt;/code&gt; control - you&#39;ll see why this is important soon. Both projects have identical files with identical contents, but take a look at the Project navigators:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/xcode_diffs.png&quot; alt=&quot;Xcode 15 &amp;amp; 16 Project navigators&quot;&gt;&lt;/p&gt;
&lt;p&gt;At a first glance, there are two differences: the order of the files &amp;amp; folders and the color of the folder icons. A less obvious difference is that in Xcode 15, I can drag files around to arrange them in the order I prefer. In Xcode 16, I can&#39;t do that. I can drag files into or out of folders, but I can&#39;t move them around at the same level.&lt;/p&gt;
&lt;p&gt;I like my Project navigator to be organized in a certain way, so I have to confess that I do find this annoying, especially since the folders at the top are ones I use infrequently. I did wonder whether the folders sorting to the top was due to the fact that I have that option turned on for Finder, but when I turned it off in Finder and made a new project, it was still the same.&lt;/p&gt;
&lt;h3&gt;Adding a new file&lt;/h3&gt;
&lt;p&gt;What happens when you add a new file? There are many different ways to do this in Xcode 16, but I&#39;ll stick to the same old method that works for both. With &lt;strong&gt;ContentView.swift&lt;/strong&gt; selected, press &lt;strong&gt;Command-N&lt;/strong&gt; to open the file template chooser, select a new SwiftUI View file, and click &lt;strong&gt;Next&lt;/strong&gt;. Name it &lt;strong&gt;DetailView.swift&lt;/strong&gt; and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is why I created &lt;code&gt;git&lt;/code&gt; repositories for both projects - I wanted to see what source control changes were detected. In Xcode 16, the new &lt;strong&gt;DetailView.swift&lt;/strong&gt; file is marked with the &lt;strong&gt;A&lt;/strong&gt; that indicates an added file. But the top item in the Project navigator - the project itself - has no change marker. Going to the Source Control navigator confirms that there is only one change in the repository: the new file.&lt;/p&gt;
&lt;p&gt;In Xcode 15, the new &lt;strong&gt;DetailView.swift&lt;/strong&gt; file is marked with the &lt;strong&gt;A&lt;/strong&gt;, but the project file is also marked with an &lt;strong&gt;M&lt;/strong&gt; for a modified file. The Changes tab in the Source control navigator shows a lot of unreadable changes to the &lt;strong&gt;project.pbxproj&lt;/strong&gt; file, which is one of the files hidden inside the &lt;strong&gt;.xcodeproj&lt;/strong&gt; bundle:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/group_changes.png&quot; alt=&quot;Xcode 15 Changes tab&quot;&gt;&lt;/p&gt;
&lt;p&gt;This points out the fundamental difference between the two approaches: &lt;strong&gt;groups&lt;/strong&gt; are an artificial construct that is stored in the project files. Usually, this mirrors the file and folder structure in Finder, but it doesn&#39;t have to. When you use &lt;strong&gt;folders&lt;/strong&gt;, Xcode is reading the file and folder structure directly from the Finder.&lt;/p&gt;
&lt;p&gt;One other difference: in Xcode 15, the new file is added to the Project navigator directly after the selected file. In Xcode 16, there appears to be a preset sort order, the logic of which escapes me for now. If I add a file with no other file selected, it&#39;s added to the root of the project, outside the main folder. Otherwise, it&#39;s added after &lt;strong&gt;ContentView.swift&lt;/strong&gt;, no matter which file was selected.&lt;/p&gt;
&lt;h3&gt;Importing files&lt;/h3&gt;
&lt;p&gt;So how does this affect importing files? For both projects, I used &lt;strong&gt;Integrate -&amp;gt; Discard All Changes...&lt;/strong&gt; to revert them to their original states. Then in Xcode 15, I dragged my folder of sample files into the Project navigator, selecting where I wanted it to appear:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/import_folder_15.png&quot; alt=&quot;Import folder in Xcode 15&quot;&gt;&lt;/p&gt;
&lt;p&gt;This led to the dialog that I&#39;ve always found slightly confusing and far too large, but I chose my standard settings.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/import_settings_15.png&quot; alt=&quot;Import settings in Xcode 15&quot;&gt;&lt;/p&gt;
&lt;p&gt;The source control markers showed three added files and a modified project file. Checking the project folder in Finder, I could see the new folder and its files in the correct place.&lt;/p&gt;
&lt;p&gt;With Xcode 16, I wasn&#39;t able to select a precise location for the dropped folder, but I was able to choose the destination folder:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/import_folder_16.png&quot; alt=&quot;Import folder in Xcode 16&quot;&gt;&lt;/p&gt;
&lt;p&gt;The import dialog was less confusing - the only real option was whether to copy the files or move them.&lt;/p&gt;
&lt;p&gt;The Utilities folder appeared below the Preview Content folder, the three files in it got the &lt;strong&gt;A&lt;/strong&gt; marker, and the project file showed no changes.&lt;/p&gt;
&lt;p&gt;But since Xcode is reading the file and folder structure directly from the Finder, I can add and move files in Finder as well as in Xcode. Still in Xcode 16, I discarded all changes again to revert to the starting app, then I right-clicked on &lt;strong&gt;ContentView.swift&lt;/strong&gt; and selected &lt;strong&gt;Show in Finder&lt;/strong&gt;. Then I dragged my Utilities folder into the project folder in Finder:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/finder_import.png&quot; alt=&quot;Finder import&quot;&gt;&lt;/p&gt;
&lt;p&gt;Back in Xcode, there was the folder and its files, but with no &lt;strong&gt;A&lt;/strong&gt; markers on the files. The Source Control navigator showed only the addition of the Utilities folder and not the files inside it. I made a change in &lt;strong&gt;ContentView.swift&lt;/strong&gt;, then went to &lt;strong&gt;Source Control navigator -&amp;gt; Changes&lt;/strong&gt; and clicked &lt;strong&gt;Stage All&lt;/strong&gt;. This added the three new files to the commit. So that worked, just not very smoothly, but again, there were no changes to the project file.&lt;/p&gt;
&lt;h3&gt;Changing the Folder Structure&lt;/h3&gt;
&lt;p&gt;What else can I do in Finder that Xcode 16 will pick up? For my next test, I dragged a SwiftUI view file into the project folder in Finder. Then, still in Finder, I selected both the &lt;strong&gt;...View.swift&lt;/strong&gt; files, right-clicked, and selected &lt;strong&gt;New Folder with Selection&lt;/strong&gt;. I named the folder &lt;strong&gt;Views&lt;/strong&gt; and sure enough, Xcode picked up the changes. The folders in the project appear to sort alphabetically, so the &lt;strong&gt;Views&lt;/strong&gt; folder appeared after the &lt;strong&gt;Utilities&lt;/strong&gt; folder. So I guess that while I don&#39;t have total control over the arrangement of the files in the Project navigator, I can organize the files into folders to suit.&lt;/p&gt;
&lt;p&gt;Then I got to thinking about assets - can I add them to &lt;strong&gt;Assets.xcassets&lt;/strong&gt; using Finder? The assets I add most frequently are images and app icons. I tried dragging an image into the &lt;strong&gt;Assets.xcassets&lt;/strong&gt; folder but it didn&#39;t appear in Xcode - I had to import the image as usual which creates an &lt;strong&gt;imageset&lt;/strong&gt; folder with the image and a &lt;strong&gt;Contents.json&lt;/strong&gt; file. If I took an &lt;strong&gt;imageset&lt;/strong&gt; folder from another project and added it using Finder, that worked as expected. Similarly, if I dragged an &lt;strong&gt;AppIcon.appiconset&lt;/strong&gt; folder from another project into &lt;strong&gt;Assets.xcassets&lt;/strong&gt;, that worked too, with Finder offering to merge or replace the existing folder. I chose replace, and my icon was there.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Which style will I use going forward? With &lt;strong&gt;groups&lt;/strong&gt;, I get the total flexibility that I&#39;m used to, so I can arrange my files and folders exactly as I like. But with &lt;strong&gt;folders&lt;/strong&gt;, I can use Finder to import and organize my files and folders. The big difference is in source control, especially if you&#39;re working with other people. When every file addition, deletion or move also changes the project file, you have a much greater chance of getting a merge conflict.&lt;/p&gt;
&lt;p&gt;Finally, Apple has obviously decided that folders are the way to go - they are the default for new projects in Xcode. Over the years, I have seen Apple make many changes which seemed nonsensical or irritating at first, but nearly every time, it turned out that these changes were the precursor to more significant changes and if you had accepted the initial changes, you were in much better shape to handle the larger changes down the track.&lt;/p&gt;
&lt;p&gt;Finally, as an educator, I try to stick to the defaults as much as possible. I don&#39;t want to confuse anyone by requiring them to make changes to their setup to match me. So I&#39;ll be using folders from now on.&lt;/p&gt;
&lt;p&gt;But remember, if you hate it, or if you want to upgrade an older project, you can always swap. Right-click on the project folder in the Project navigator (the second item), and select &lt;strong&gt;Convert to Group&lt;/strong&gt; or &lt;strong&gt;Convert to Folder&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;What do you think? Will you go with the flow and use folders, or do you intend to stick with groups? If you have any other thoughts, ideas, suggestions or questions, I&#39;d love to hear from you.&lt;/p&gt;
&lt;p&gt;You can contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, please &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Converting an App from Obj-C</title>
    <link href="https://troz.net/post/2024/app_conversion/" />
    <updated>2024-09-12T00:28:24Z</updated>
    <id>https://troz.net/post/2024/app_conversion/</id>
    <content type="html">&lt;p&gt;A couple of months ago, I got a message from App Store Connect pointing out that one of my iPhone apps - &lt;a href=&quot;https://troz.net/berio/&quot;&gt;Berio’s Sequenza VII&lt;/a&gt; - had not been updated in 3 years and so would be removed from the App Store unless I updated it within 90 days. I disagree with this policy, but small developers like me can&#39;t fight Apple. Since I want the app to remain available, I then had to consider how to update it. Once I opened the app project, I realized that the app was written in Objective-C, so I decided to take the plunge and convert it to Swift and SwiftUI.&lt;/p&gt;

&lt;p&gt;The app in question is a highly specialized app with an &lt;em&gt;extremely&lt;/em&gt; small market - professional musicians or university-level music students who play the oboe. My eldest son fits this category and in 2013, he started to learn a piece called &lt;a href=&quot;https://en.wikipedia.org/wiki/Sequenza_VII&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sequenza VII by Luciano Berio&lt;/a&gt;. This is a very strange piece because it doesn&#39;t have a standard time signature. Instead each line has 13 bars (measures) with each bar being allocated a certain time span in actual seconds. This means that it&#39;s impossible to practice it with a metronome, so I wrote an iPhone app that counted each bar and played a sound or flashed the screen at the appropriate times for each bar. I allowed for slowing the whole thing down for practice, and since the performance is supposed to be accompanied by a constant drone playing in the background, I added that too. I can&#39;t say it sells well, but every time someone buys it, I get a warm glow of satisfaction.&lt;/p&gt;
&lt;p&gt;I dislike Apple&#39;s policy of removing old apps, just because they are old. After all, the piece of music that this app supports was written in 1969 and Berio hasn&#39;t updated it since!&lt;/p&gt;
&lt;p&gt;There are valid reasons for removing apps from the App Store:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The app doesn&#39;t run on the latest hardware or operating system.&lt;/li&gt;
&lt;li&gt;The app is a scam.&lt;/li&gt;
&lt;li&gt;The app is an obvious clone of another app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But basing a removal purely on age is an easy thing to automate, so Apple can convince themselves that they are keeping the App Store safe, without actually doing anything to make it safe. In fact, the last time I got one of these notices, I updated the version and build numbers and re-submitted the identical app to the App Store. It was accepted and the notice went away. I fail to see how this did anything except waste my time and the App Store reviewer&#39;s time.&lt;/p&gt;
&lt;p&gt;Anyway, this time I considered doing the same again, but when I realized the app was still in Objective-C, which I am rapidly forgetting, I decided now was the time to convert it. So I opened up Xcode and created a new project using Swift and SwiftUI. Don&#39;t you love the smell of a fresh Xcode project?&lt;/p&gt;
&lt;p&gt;There are four main aspects to the app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The main screen that shows the bar length and allows control.&lt;/li&gt;
&lt;li&gt;The settings screens that configure the app - there are a lot of options.&lt;/li&gt;
&lt;li&gt;Playing the drone if required.&lt;/li&gt;
&lt;li&gt;The timing functions that work in the background and update the UI as the bars proceed.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Main Screen&lt;/h3&gt;
&lt;p&gt;For the main screen, the UI was quite simple and I was able to recreate it in SwiftUI with no problems. The only issue I had was with &lt;code&gt;Text&lt;/code&gt; views that are only visible in certain circumstances. I tried hiding them by adjusting &lt;code&gt;opacity&lt;/code&gt; which I expected to work, but it didn&#39;t, presumably because there wasn&#39;t always any text content. I could have solved this using fixed heights, but I didn&#39;t want to do that for accessibility reasons. I ended up keeping the views always visible but clearing or setting the text content. By using &lt;code&gt;Spacers&lt;/code&gt; and &lt;code&gt;VStacks&lt;/code&gt; I was able to keep the layout looking the way I wanted it at all times.&lt;/p&gt;
&lt;p&gt;As is my usual habit, I populated the UI with local variables at this stage. This lets me adjust the preview data quickly to make sure the UI works with a range of different values, without having to worry about the actual data flow. Then I plugged in real data later. For the SwiftUI preview, I selected the &lt;strong&gt;iPhone SE 3rd generation&lt;/strong&gt; as the standard device. This is the smallest screen size so everything has to fit on it. Later, I used the &lt;strong&gt;iPhone 15 Pro Max&lt;/strong&gt; to confirm that it all looks good on a large screen too.&lt;/p&gt;
&lt;p&gt;The only other trick was to use a &lt;code&gt;.monospacedDigit()&lt;/code&gt; for the large counter in the middle of the screen. This stops the numbers jumping around as they change.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/Berio_ContentView.png&quot; alt=&quot;Berio ContentView&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Settings&lt;/h3&gt;
&lt;p&gt;Now on to the settings which take up two screens. In the Objective-C app, I used storyboards. The main screen segued to a tab view with three tabs: two for settings and one for the about screen. The settings views used lots of nested stack views and many, many constraints.&lt;/p&gt;
&lt;p&gt;Getting the basic UI elements into SwiftUI was relatively easy, but I wanted them to look good on large or small iPhones. I didn&#39;t want something that looked good on an SE being all crammed into the top half of a 15 Pro, but that ended up being more-or-less what I got.&lt;/p&gt;
&lt;p&gt;I tried various techniques using nested stacks, spacers, forms, groups and so on. Nothing I tried gave me quite the result I wanted, but I ended up with nested &lt;code&gt;VStacks&lt;/code&gt; which allowed me to group related settings. In hindsight, I probably should have swallowed my &lt;code&gt;GeometryReader&lt;/code&gt; aversion and used it to adjust the layout spacing, based on the screen size. I&#39;ll probably go back and do that later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/berio_settings.png&quot; alt=&quot;Settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;But the most important thing about the setting screens is the settings themselves and here is where SwiftUI really shone. The two view controllers for these pages had masses of code for handling the settings. First, they had to read them in from &lt;code&gt;UserDefaults&lt;/code&gt;, providing logical defaults if needed. Then they had to update the UI to reflect the settings. Since several of the settings depended on other settings, monitoring changes meant potentially adjusting other settings and then saving them all back to &lt;code&gt;UserDefaults&lt;/code&gt; as well as adjusting all the UI elements manually. And finally, reverting the settings to the standard defaults required even more code and UI updates.&lt;/p&gt;
&lt;p&gt;With SwiftUI, I used &lt;code&gt;@AppStorage&lt;/code&gt; extensively. Not only did this remove all the hassle of reading and writing to &lt;code&gt;UserDefaults&lt;/code&gt;, but it also automatically updated the UI when the settings changed. And by setting up &lt;code&gt;disabled&lt;/code&gt; modifiers, the connections between the linked settings were automatic. I was able to remove a lot of code and make the settings screens much simpler and easier to understand. And this flowed on to the main screen when I was adding its data as it used the same &lt;code&gt;@AppStorage&lt;/code&gt; properties.&lt;/p&gt;
&lt;p&gt;Counting the lines of code using &lt;a href=&quot;https://github.com/boyter/scc&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;scc&lt;/a&gt;, I found the following:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;View&lt;/th&gt;
&lt;th&gt;Obj-C&lt;/th&gt;
&lt;th&gt;Swift&lt;/th&gt;
&lt;th&gt;Reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Settings&lt;/td&gt;
&lt;td&gt;173 (13)&lt;/td&gt;
&lt;td&gt;101 (0)&lt;/td&gt;
&lt;td&gt;72 (42%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bar &amp;amp; Sounds&lt;/td&gt;
&lt;td&gt;166 (18)&lt;/td&gt;
&lt;td&gt;124 (8)&lt;/td&gt;
&lt;td&gt;42 (25%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The numbers in brackets after the line count are the complexity ratings. The bar &amp;amp; sounds view is more complex because it has to be able to flash the screen.&lt;/p&gt;
&lt;p&gt;Which reminds me of another thing: in the original, I used a background image with a gradient that stretched to fill whatever screen size was in use. In the new version, SwiftUI makes the background gradient itself. I added the start and end colors to the Assets catalog, and now use a &lt;code&gt;LinearGradient&lt;/code&gt; which I can change programmatically if the user wants the screen to flash at the start of each new bar.&lt;/p&gt;
&lt;h3&gt;The Drone&lt;/h3&gt;
&lt;p&gt;The drone plays a B4 constantly. But some oboists like to adjust the frequency of their basic A from the standard of 440 Hz, so I had to allow for that and adjust the B4 to match.&lt;/p&gt;
&lt;p&gt;I am not a sound expert, so in the original app, I imported some third-party code to do this. It&#39;s very much C code even though it&#39;s written in Objective-C and I do not possess the expertise to convert it to Swift. So in this case, I added the &lt;strong&gt;DronePlayer.h&lt;/strong&gt; and &lt;strong&gt;DronePlayer.m&lt;/strong&gt; files to my new project. When I did this, Xcode kindly offered to create a bridging header for me, which was great because it&#39;s been so long since I did this, that I would not have remembered how to do it manually. I did need to check an older project to remember what to put into the bridging header. The format is:&lt;/p&gt;
&lt;pre class=&quot;language-objc&quot;&gt;&lt;code class=&quot;language-objc&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DronePlayer.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You import the header file (.h), and don&#39;t forget the leading &lt;code&gt;#&lt;/code&gt; or the import won&#39;t work.&lt;/p&gt;
&lt;p&gt;After that, I created a Swift class to act as the intermediary between SwiftUI and this Objective-C class and it works very well. I am concerned that this is very old code that may not always work, but hopefully by the time Apple deprecates it, there will be Swift alternatives I can use.&lt;/p&gt;
&lt;h3&gt;Timing&lt;/h3&gt;
&lt;p&gt;Finally we get to the most crucial part of the app: timing the bars.&lt;/p&gt;
&lt;p&gt;In the original, I used a custom thread with a high priority. While running the main loop, it would compute the time until the next bar and sleep until then. Then it would proceed to the next bar and update the UI. This worked well and when I logged some timing tests, it was accurate to within about 0.01 of a second.&lt;/p&gt;
&lt;p&gt;But it all felt very complicated and perhaps unnecessarily accurate, so I started experimenting with using a &lt;code&gt;Timer&lt;/code&gt;. That didn&#39;t work very reliably, but my next attempt used &lt;code&gt;DispatchQueue.main.asyncAfter&lt;/code&gt; and that worked. At the start of the main loop function, I calculate when the next bar should start and call &lt;code&gt;asyncAfter&lt;/code&gt; with that time. Then I update the UI so that this never slows down the loop.&lt;/p&gt;
&lt;p&gt;This worked quite well but my timing tests showed that the first two bars were always out by about 0.1 seconds. The later bars were more accurate. I can&#39;t explain why the longer bars at the beginning of each line were less accurate than the shorter ones, but maybe the internal code that &lt;code&gt;asyncAfter&lt;/code&gt; uses to schedule the call is inherently less accurate for longer times.&lt;/p&gt;
&lt;p&gt;I decided to have some fun with this inaccuracy and I added routines to track the error of each bar and store that for adjusting the call time the next time round. For example, bar 1 should last 3 seconds. If it took 3.1 seconds, then next time, I apply that 0.1 second error and call it after only 2.9 seconds. After two complete loops, the timings were as accurate as the original, with the added bonus that they would self-adjust to suit if the user&#39;s device was busy doing something else in the background.&lt;/p&gt;
&lt;p&gt;I used &lt;code&gt;OSLog&lt;/code&gt; to track the timings. This takes a bit more effort to set up than &lt;code&gt;print&lt;/code&gt;, but it makes it much easier to filter out the logs you&#39;re interested in. And since Xcode seems to have got more verbose over the years, being able to filter is very useful.&lt;/p&gt;
&lt;p&gt;At the top of my &lt;strong&gt;ContentView.swift&lt;/strong&gt; file, I added:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OSLog&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, where I was declaring properties, I created a logger:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subsystem&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Berio&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ContentView&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It seems to be a standard to set the subsystem to an app identifier and the category to the class you&#39;re logging. You can use these labels when filtering.&lt;/p&gt;
&lt;p&gt;To log my timings, I used:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Bar &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;runState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;barCounter&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; - Elapsed time: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;elapsedTime&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, Error = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though I could run the app in the live preview, that didn&#39;t produce any log entries, but in the simulator, they appeared. I filtered them to show only the &lt;code&gt;ContentView&lt;/code&gt; category and then I was able to track all the timings:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/berio_logging.png&quot; alt=&quot;Logging&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This was an interesting exercise. It is extremely impressive that 11 year old Objective-C code can still build and run with the current Xcode and on current devices. In the Swift world, we do not have such a luxury, but I still feel more comfortable moving forward with Swift. I&#39;m left with the drone code being in Objective-C, but that&#39;s a good reminder to me that it is still possible to mix the two languages.&lt;/p&gt;
&lt;p&gt;The remaining items on my to-do list are to implement a better layout for the settings pages so that they fill the screen no matter what the screen size. And to keep an eye out for a Swift way of playing a drone at a set frequency with a particular waveform.&lt;/p&gt;
&lt;p&gt;Anyway, this has got Apple off my back for another 3 years, at least as far as this app is concerned :-)&lt;/p&gt;
&lt;p&gt;And always, I&#39;d love to hear from you if you have any suggestions, corrections or improvements. If you are an oboe student, let me know which music school you are at and I&#39;ll send you a discount code for the app.&lt;/p&gt;
&lt;p&gt;You can contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, I&#39;d love you to &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac 2024</title>
    <link href="https://troz.net/post/2024/swiftui-mac-2024/" />
    <updated>2024-08-22T07:21:54Z</updated>
    <id>https://troz.net/post/2024/swiftui-mac-2024/</id>
    <content type="html">&lt;p&gt;Over the years, I have written articles and sample apps to demonstrate the new features of each year&#39;s SwiftUI updates with particular emphasis on macOS app development. Last year, the major update to SwiftUI was the new data flow system using the Observation framework. I covered that in my article &lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/&quot;&gt;SwiftUI Data Flow 2023&lt;/a&gt; but I didn&#39;t feel there were sufficient UI changes to warrant an update to my usual sample app.&lt;/p&gt;
&lt;p&gt;This year, the &lt;a href=&quot;https://http.cat/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;HTTP Cats&lt;/a&gt; app is back! And I&#39;ll cover new features from both WWDC 2023 and WWDC 2024.&lt;/p&gt;

&lt;p&gt;It&#39;s currently August 2024 and I am running macOS Sequoia 15.1 Beta (24B5024e) with Xcode 16.1 beta (16B5001e).&lt;/p&gt;
&lt;p&gt;Download the project from &lt;a href=&quot;https://github.com/trozware/swiftui-mac-2024&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; and, if you&#39;re running the correct versions of macOS and Xcode, you can run the app and follow along. If not, you can still look at the code, but it won&#39;t build.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/http_teapot.png&quot; alt=&quot;HTTP Cats&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: there&#39;s a bug right now that crashes the app in macOS 15.1. If you scroll down the list in the sidebar and then select a status that is more than 8 or 9 rows below the previous selection, the app crashes with this error: &lt;code&gt;Row index -1 out of row range (numberOfRows: 60)&lt;/code&gt;. I&#39;ve tested this with Xcode 16.0 beta 6 and Xcode 16.1 and they both act the same. But on a computer running macOS 15.0 beta 7 and Xcode 16.0 beta 6, it works perfectly. So this is a bug in macOS 15.1.&lt;/p&gt;
&lt;p&gt;Here are the areas that I&#39;m going to discuss:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#xcode&quot;&gt;Xcode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#previews&quot;&gt;Previews&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#windows&quot;&gt;Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#tabs&quot;&gt;Tabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#charts&quot;&gt;Charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#colors-and-gradients&quot;&gt;Colors and gradients&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#sf-symbols&quot;&gt;SF Symbols&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a id=&quot;xcode&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Xcode&lt;/h3&gt;
&lt;p&gt;When you create a new project in Xcode 16.1, the second item in the project navigator is now a folder instead of a group. This sounds trivial, since it really is a folder in Finder but I find it really annoying because it doesn&#39;t allow me to arrange my other files and folders as I want them. To fix this, right-click on the folder and select &lt;strong&gt;Convert to Group&lt;/strong&gt;. The icon switches from blue to gray and you can once again drag files and folders to re-order them.&lt;/p&gt;
&lt;p&gt;The advantage to the folder option is that your project is read directly from the file system, so you can use Finder to move files around and Xcode will reflect the changes. To import a file into the project, you can drag it into the project&#39;s Finder window and Xcode will see it. Similarly, to delete a file, you can use Finder directly. With the group option, you have to import files into the Xcode Project navigator, and when you do, source control marks the &lt;strong&gt;.xcodeproj&lt;/strong&gt; file as modified, making source control messier. Using the folder option, only the new file itself gets marked as a change. So use which option works for you, but I much prefer the clarity of being able to organize my files in the Project navigator.&lt;/p&gt;
&lt;p&gt;The next thing to look at in Xcode is &lt;strong&gt;Predictive Code Completion&lt;/strong&gt;. While the Xcode betas run on macOS Sonoma, predictive code completion requires Sequoia and I think it also requires a minimum of 16GB of memory. We don&#39;t yet have the promised &lt;strong&gt;Swift Assist&lt;/strong&gt; which will allow for a more detailed way of asking Xcode to write code, but the code completion is supposed to be much better.&lt;/p&gt;
&lt;p&gt;I&#39;ve used &lt;a href=&quot;https://github.com/github-copilot-resources&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub Copilot&lt;/a&gt; for writing JavaScript for a couple of years now and overall, it works well. I use it as a form of super autocomplete and targeted search rather than asking it to write large chunks of code for me, and that&#39;s what I&#39;m hoping to get for Swift now. Xcode has a way to go to catch up yet. Surprisingly, it suffers from one of the same problems as Copilot - it appears to have been trained on old code!&lt;/p&gt;
&lt;p&gt;I know some people are very negative about using any form of AI to write parts of your code, but I see it as just another tool to help us be more efficient. For example, I bet you&#39;ve written code to download and process JSON many, many times. Why not let Xcode write that for you? You still have to do the thinking. What&#39;s the URL? Which properties do I need? How am I going to use the data? But you don&#39;t have to write the boilerplate. It&#39;s no different to using a library or a pre-built function.&lt;/p&gt;
&lt;p&gt;I put predictive code completion to the test by creating a new Swift file called &lt;strong&gt;Networker.swift&lt;/strong&gt;. I set up a &lt;code&gt;Networker&lt;/code&gt; class and gave it a web address. Then I started to write a function to fetch the data:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/predictive_code_1.png&quot; alt=&quot;Predictive Code Completion 1&quot;&gt;&lt;/p&gt;
&lt;p&gt;The faded text is the suggestion. I had only typed &lt;code&gt;func fet&lt;/code&gt;. It used the address to deduce that I was downloading quotes - not unreasonable. I pressed &lt;strong&gt;Tab&lt;/strong&gt; to accept and got the next chunk:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/predictive_code_2.png&quot; alt=&quot;Predictive Code Completion 2&quot;&gt;&lt;/p&gt;
&lt;p&gt;After pressing &lt;strong&gt;Tab&lt;/strong&gt; a few more times, I ended up with this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/predictive_code_3.png&quot; alt=&quot;Predictive Code Completion 3&quot;&gt;&lt;/p&gt;
&lt;p&gt;This doesn&#39;t build and it uses old-style completion handlers instead of async/await. The biggest problem I see is that it&#39;s fallen into the usual &lt;code&gt;URLSession&lt;/code&gt; trap and not called &lt;code&gt;resume&lt;/code&gt; on the task. So this was never going to download anything.&lt;/p&gt;
&lt;p&gt;I started to define a &lt;code&gt;NetworkError&lt;/code&gt; enum and it filled in the cases it had used, so that was really good. When I started to define a &lt;code&gt;Quote&lt;/code&gt; struct, it also added some reasonable properties, as well as marking it as conforming to &lt;code&gt;Codable&lt;/code&gt; - bonus points there.&lt;/p&gt;
&lt;p&gt;When I tried to build, I got sensible warnings pointing out that I had not used either &lt;code&gt;task&lt;/code&gt; or &lt;code&gt;data&lt;/code&gt;. This would have reminded me to call &lt;code&gt;resume&lt;/code&gt; if I hadn&#39;t already noticed. I also got a Swift 6 concurrency warning about the completion handler.&lt;/p&gt;
&lt;p&gt;So overall, not great yet. Old technology and a flawed implementation. One of the points Apple people made at WWDC 2024 was that these code tools would be trained on the latest Apple APIs. This is not the case here, but I&#39;m still running a beta, so I&#39;ll give them the benefit of the doubt for now. I&#39;ll definitely be keeping an eye on this feature and hope to be able to make good use of it in the future.&lt;/p&gt;
&lt;p&gt;Before I move on from Xcode, there&#39;s one more feature I want to mention and that&#39;s the new options for creating a new file. If you press &lt;strong&gt;Command-N&lt;/strong&gt;, you get the file template chooser, just as before. But now there are other possibilities. You can use the &lt;strong&gt;File&lt;/strong&gt; menu or right-click in the Project navigator to add a new empty file, but the one I like best is the ability to make new files from the clipboard. I don&#39;t know about you, but when I&#39;m working on a SwiftUI project, I&#39;ll often have more than one view in a file, especially if I&#39;ve created views by extracting subviews. Later on, I&#39;ll want the view to have its own file.&lt;/p&gt;
&lt;p&gt;I had a view called &lt;code&gt;MeshBackground&lt;/code&gt; which didn&#39;t have its own file, so I selected the entire struct and pressed &lt;strong&gt;Command-X&lt;/strong&gt; to cut it out. Then both the contextual menu and the &lt;strong&gt;File -&amp;gt; New&lt;/strong&gt; menu offered &lt;strong&gt;New &amp;quot;Meshbackground.swift&amp;quot; from Clipboard&lt;/strong&gt;. When I selected this, it added the file, named it and pasted in my struct. The only thing I had to do was to add the &lt;code&gt;import SwiftUI&lt;/code&gt; statement at the top of the new file. I think it could have been smart enough to work this one out, but maybe later.&lt;/p&gt;
&lt;p&gt;This is a great time-saver and will really encourage me to keep my code well-structured.&lt;/p&gt;
&lt;p&gt;In minor Xcode updates over the last two years, my favorite feature is using &lt;strong&gt;Control-M&lt;/strong&gt; to split long lines of code over multiple lines.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;previews&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Previews&lt;/h3&gt;
&lt;p&gt;Last year, we got the &lt;code&gt;#Preview&lt;/code&gt; macro which made SwiftUI previews a lot neater. We went from:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SidebarRowView_Previews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreviewProvider&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; previews&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;SidebarRowView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;418&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;I&#39;m a teapot&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token other-directive property&quot;&gt;#Preview&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;SidebarRowView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;418&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;I&#39;m a teapot&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Xcode 15 has a converter you could use to switch to the new format, and Xcode 16.0 still has it but Xcode 16.1 does not. I&#39;m not sure why they removed it, but it&#39;s not a big deal to do it manually.&lt;/p&gt;
&lt;p&gt;The new thing this year in previews is the &lt;code&gt;@Previewable&lt;/code&gt; macro which lets us provide data to a preview. This is especially useful for binding properties. In &lt;strong&gt;SidebarView.swift&lt;/strong&gt; in the sample app, I show the old style commented out:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;SidebarView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;httpSections&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpSection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selectedStatus&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;selectedStatus&lt;/code&gt; is a Binding, so I can&#39;t just pass in nil. I had to use &lt;code&gt;.constant&lt;/code&gt; to create a Binding set to nil. With &lt;code&gt;@Previewable&lt;/code&gt;, I can do this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@Previewable&lt;/span&gt; &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedStatus&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;SidebarView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;httpSections&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpSection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selectedStatus&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedStatus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I expected to be able to supply an actual &lt;code&gt;HttpStatus&lt;/code&gt; here and have it start as the selected item in the list, but although it compiled fine, it didn&#39;t show up as selected in the preview initially. But I am now able to change the selection in the preview.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;windows&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Windows&lt;/h3&gt;
&lt;p&gt;The first window feature I want to address is not new, but I&#39;ve been asked about it a few times. How do you make a single window app and get SwiftUI to remove the &lt;strong&gt;File -&amp;gt; New Window&lt;/strong&gt; menu item?&lt;/p&gt;
&lt;p&gt;When you create a new SwiftUI app, the &lt;strong&gt;...App.swift&lt;/strong&gt; file contains something like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NewApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This defines the main scene of the app as a window group which allows for multiple identical but independent windows. You get a &lt;strong&gt;File&lt;/strong&gt; menu with a &lt;strong&gt;New Window&lt;/strong&gt; item and a &lt;strong&gt;Close&lt;/strong&gt; item. If you want a single window app, you need to change the &lt;code&gt;WindowGroup&lt;/code&gt; to a &lt;code&gt;Window&lt;/code&gt;, supplying the window title and an &lt;code&gt;id&lt;/code&gt; string:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NewApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;New App&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mainWindow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; can be anything so long as you don&#39;t use it for any other window in your app. In the default state, this removes the &lt;strong&gt;File&lt;/strong&gt; menu completely. It also changes the app behavior so that closing the window quits the app completely. If you add any new commands to the &lt;strong&gt;File&lt;/strong&gt; menu, you&#39;ll get the &lt;strong&gt;Close&lt;/strong&gt; item back, but using it will still quit the app. You can still open secondary windows in this style of app, but you can&#39;t duplicate the initial window.&lt;/p&gt;
&lt;p&gt;I covered how to open new windows in my previous article &lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#opening-new-windows&quot;&gt;SwiftUI for Mac 2022&lt;/a&gt;. The code is still present in this year&#39;s sample app for reference, but commented out.&lt;/p&gt;
&lt;p&gt;There is a new window scene called &lt;code&gt;UtilityWindow&lt;/code&gt;. This is primarily intended for floating palettes and toolbars. I&#39;ve used it for the &lt;strong&gt;UI Samples&lt;/strong&gt; window just because that was a window I wanted to show, but it&#39;s not really the right design choice for this sort of informational window.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;SwiftUI_Mac_2024App.swift&lt;/strong&gt;, here&#39;s how I set up the utility window:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;UtilityWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;UI Samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui_samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;SamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyboardShortcut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;u&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly to &lt;code&gt;Window&lt;/code&gt;, it needs a window title and an &lt;code&gt;id&lt;/code&gt;. The view to show in the window is in the closure and I&#39;ve added an optional keyboard shortcut.&lt;/p&gt;
&lt;p&gt;If you add a &lt;code&gt;Window&lt;/code&gt; scene that&#39;s not the initial window, it appears in the &lt;strong&gt;Window&lt;/strong&gt; menu and you can open it from there with an optional keyboard shortcut. A &lt;code&gt;UtilityWindow&lt;/code&gt; gets a &lt;strong&gt;Show/Hide&lt;/strong&gt; menu item in the &lt;strong&gt;View&lt;/strong&gt; menu instead, and the keyboard shortcut toggles it.&lt;/p&gt;
&lt;p&gt;A utility window has a few other differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s a floating window that appears above your other windows.&lt;/li&gt;
&lt;li&gt;It doesn&#39;t get focus by default when opened, you have to click in it to make it active.&lt;/li&gt;
&lt;li&gt;When focused, you can close it with the &lt;strong&gt;Escape&lt;/strong&gt; key.&lt;/li&gt;
&lt;li&gt;It&#39;s hidden when your app is no longer the active app.&lt;/li&gt;
&lt;li&gt;It cannot be minimized, so the orange button in the title bar is disabled.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These windows can be opened programmatically in exactly the same way as other windows, using &lt;code&gt;openWindow&lt;/code&gt;. Check out the code in &lt;strong&gt;ToolbarView.swift&lt;/strong&gt; to see how I do this.&lt;/p&gt;
&lt;p&gt;The other window features I want to explore relate to style, initial position, and dragging. In one of the WWDC 2024 videos, the presenters created a custom &lt;strong&gt;About&lt;/strong&gt; window for their app, so I decided to try to do the same.&lt;/p&gt;
&lt;p&gt;First I made a SwiftUI view called &lt;code&gt;AboutView&lt;/code&gt;. I added some titles and buttons, including one to close the window. I also added a mesh gradient background to make it look a bit more interesting. See the &lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#colors-and-gradients&quot;&gt;Colors and gradients&lt;/a&gt; section below for more info on that.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/about_view.png&quot; alt=&quot;About View&quot;&gt;&lt;/p&gt;
&lt;p&gt;With the view created, now I had to work out how to show it instead of the default &lt;strong&gt;About&lt;/strong&gt; window. I started by adding a new window scene to &lt;strong&gt;SwiftUI_Mac_2024App.swift&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;About&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;about&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;AboutView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in &lt;strong&gt;MenuView.swift&lt;/strong&gt;, I added a new &lt;code&gt;CommandGroup&lt;/code&gt;, replacing the existing &lt;code&gt;appInfo&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;CommandGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;replacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;About SwiftUI Mac 2024&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;openWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;about&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When testing this, there were two problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The window was too large for my content.&lt;/li&gt;
&lt;li&gt;There was now an &lt;strong&gt;About&lt;/strong&gt; menu item in the &lt;strong&gt;Window&lt;/strong&gt; menu as well as &lt;strong&gt;About SwiftUI Mac 2024&lt;/strong&gt; in the app menu.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To fix these issues, I added modifiers to the window scene:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;windowResizability&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commandsRemoved&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now my content filled the window although the sizing could still be too small or too big. But the &lt;strong&gt;Window -&amp;gt; About&lt;/strong&gt; menu item was gone, so I was making progress.&lt;/p&gt;
&lt;p&gt;Next up is the new &lt;code&gt;defaultWindowPlacement&lt;/code&gt; modifier, which lets you set the initial position and size of a window. I added this to the &lt;strong&gt;About&lt;/strong&gt; window scene:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultWindowPlacement &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; displayBounds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultDisplay&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bounds
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sizeThatFits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unspecified&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGPoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; displayBounds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;midX &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentSize&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; displayBounds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;midY &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentSize&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WindowPlacement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;context&lt;/code&gt; is a &lt;code&gt;WindowPlacementContext&lt;/code&gt; that provides the display where the window will appear. I&#39;m using its &lt;code&gt;bounds&lt;/code&gt; to determine the screen size.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;content&lt;/code&gt; is a &lt;code&gt;WindowLayoutRoot&lt;/code&gt; that gives the &lt;code&gt;sizeThatFits&lt;/code&gt; for the proposed content allowing for various proposed view sizes. I&#39;m using the &lt;code&gt;unspecified&lt;/code&gt; size to get the ideal size for the content.&lt;/p&gt;
&lt;p&gt;With these two pieces of data, I can calculate the position for the top left of the window so that the center of the window will be at the center of the screen, and combine this with the size into a &lt;code&gt;WindowPlacement&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now when I run the app and open the &lt;strong&gt;About&lt;/strong&gt; box, it&#39;s in the center of the screen and the right size for the content. But if I move the window, then the next time I run the app, it&#39;s where I put it instead of in the center. This is the correct behavior for most windows, but not for the &lt;strong&gt;About&lt;/strong&gt; window. I want it to be centered every time.&lt;/p&gt;
&lt;p&gt;Time for another modifier:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;restorationBehavior&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This stops the window from remembering where it was put when the app re-launches. It remembers inside a single launch, but not on the next launch. If your window still remembers where it was because you moved it before adding this modifier, you may need to delete the app&#39;s container folder in the &lt;strong&gt;Library/Containers&lt;/strong&gt; folder to reset it.&lt;/p&gt;
&lt;p&gt;One thing I noticed was that this always put the &lt;strong&gt;About&lt;/strong&gt; box on my primary monitor, even if the app was running on a secondary monitor. If I re-enable the restoration behavior, the window appears on the correct monitor, but then it doesn&#39;t center.&lt;/p&gt;
&lt;p&gt;Now I have two more requirements: I want the &lt;strong&gt;About&lt;/strong&gt; window to have no title bar and I want to be able to drag it around the screen by clicking anywhere in the window. The first part is accomplished with yet another modifier:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;windowStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the window has no title bar. Good thing I added a &lt;strong&gt;Close&lt;/strong&gt; button! One annoying bug is that I added a keyboard shortcut to the &lt;strong&gt;Close&lt;/strong&gt; button so I could use &lt;strong&gt;Escape&lt;/strong&gt; to close the window. With a plain window style, it no longer works. The console shows this: &lt;code&gt;Warning: -[NSWindow makeKeyWindow] called on SwiftUI.AppKitWindow 0x139365800 which returned NO from -[NSWindow canBecomeKeyWindow]&lt;/code&gt;, so I guess the plain styled window is unable to become the key window and so cannot detect keyboard events.&lt;/p&gt;
&lt;p&gt;For the dragging, open &lt;strong&gt;AboutView.swift&lt;/strong&gt; itself. The simplest way to achieve the required result is to add this modifier to the main &lt;code&gt;VStack&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;gesture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WindowDragGesture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can drag the window around by pressing and dragging from anywhere in the window. But I want more! I want to be able to track when the window is being dragged so I can change it visually. &lt;code&gt;WindowDragGesture&lt;/code&gt; is new but it conforms to the &lt;code&gt;Gesture&lt;/code&gt; protocol so I can use it to detect the state of the drag. I added an &lt;code&gt;@GestureState&lt;/code&gt; property to &lt;code&gt;AboutView&lt;/code&gt; to track it:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@GestureState&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isDraggingWindow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I created a new &lt;code&gt;WindowDragGesture&lt;/code&gt; with an &lt;code&gt;updating&lt;/code&gt; closure to set the gesture state to &lt;code&gt;true&lt;/code&gt; during a drag operation:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dragWindow&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Gesture&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;WindowDragGesture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updating&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$isDraggingWindow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
      state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, I changed the &lt;code&gt;gesture&lt;/code&gt; modifier to use this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;gesture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dragWindow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that I have a property to track the dragging, I added a modifier to the &lt;code&gt;VStack&lt;/code&gt; to change the &lt;code&gt;opacity&lt;/code&gt; while the window is being dragged:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isDraggingWindow &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.8&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And after all this, I finally had what I wanted:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/drag_about.png&quot; alt=&quot;Drag About&quot;&gt;&lt;/p&gt;
&lt;p&gt;One final tweak would be to show an appropriate cursor when the window is being dragged. I tried adding to the &lt;code&gt;dragWindow&lt;/code&gt; gesture, but in the end I switched to using an &lt;code&gt;onChange&lt;/code&gt; modifier:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; isDraggingWindow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; isDraggingWindow &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NSCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;closedHand&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NSCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a id=&quot;tabs&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Tabs&lt;/h3&gt;
&lt;p&gt;The syntax for defining tabs got what appears to be quite a minor upgrade, but it feels so much better. Here&#39;s the old syntax:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;TabView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;ChartSamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tabItem &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chart&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;FormSamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tabItem &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Form&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This always felt backwards to me. The label of the tab is in the closure and the view that&#39;s inside the tab comes first, with a &lt;code&gt;tabItem&lt;/code&gt; modifier.&lt;/p&gt;
&lt;p&gt;The same tab view now looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;TabView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chart&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;chart.bar.doc.horizontal&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ChartSamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Form&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;list.bullet.clipboard&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;FormSamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This suits my brain much better. I define a tab first and then use the closure to say what it will show. The only issue I have with this is that it has not considered Mac tabs which are mostly text only. You must specify an image for the tab, even when it won&#39;t appear. In an app&#39;s Settings view, the tabs are styled differently and will show an image, but not in a conventional Mac window.&lt;/p&gt;
&lt;p&gt;Ok, the image &lt;em&gt;can&lt;/em&gt; appear in a conventional Mac window if you style the tab view differently. If you add a &lt;code&gt;.tabViewStyle&lt;/code&gt; modifier to a tab view, you can set it to either &lt;code&gt;automatic&lt;/code&gt;, &lt;code&gt;grouped&lt;/code&gt;, &lt;code&gt;sidebarAdaptable&lt;/code&gt; or &lt;code&gt;tabBarOnly&lt;/code&gt;. The default is &lt;code&gt;automatic&lt;/code&gt;, which is &lt;code&gt;tabBarOnly&lt;/code&gt; on macOS. The &lt;code&gt;grouped&lt;/code&gt; option is marked as being in beta, but it looks very familiar. It moves the tabs down so they are more like part of the content. The new one is &lt;code&gt;sidebarAdaptable&lt;/code&gt; which moves the tabs to the sidebar and shows the images. I can&#39;t see myself using this in a Mac app, I think it&#39;s designed more for the other OSes. It would only be useful if you had a long list of tabs that wouldn&#39;t fit horizontally.&lt;/p&gt;
&lt;p&gt;In the sample app, open &lt;strong&gt;SamplesView.swift&lt;/strong&gt; and test the different tab styles to see what you think. I&#39;ve added the two non-standard options there commented out so you can try them in the preview.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;charts&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Charts&lt;/h3&gt;
&lt;p&gt;Last year, Swift Charts got a pie or donut option. This year, the charts team added vector and function plotting. In &lt;strong&gt;ChartSamplesView.swift&lt;/strong&gt;, I have three different charts: a bar chart as before, a pie chart and a line chart that plots various sine waves. My understanding is that the various plot types are great for functions or for large data sets but for more comprehensive information, check out the WWDC presentation: &lt;a href=&quot;https://developer.apple.com/wwdc24/10155&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift Charts: Vectorized and function plots&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/line_plot.png&quot; alt=&quot;Line Plot&quot;&gt;&lt;/p&gt;
&lt;p&gt;In my samples, I have a separate computed property for each of the three chart types, with a &lt;code&gt;@ViewBuilder&lt;/code&gt; property to return the correct chart. I&#39;ve also updated the chart image export to use SwiftUI&#39;s &lt;code&gt;fileExporter&lt;/code&gt; instead of the AppKit&#39;s &lt;code&gt;NSSave&lt;/code&gt;. I had a slight issue with &lt;code&gt;fileExporter&lt;/code&gt;. I started off by including the image content type when configuring the exporter. This gave &lt;strong&gt;generic error&lt;/strong&gt; when I tried to save - not the most helpful of messages. I removed the content type and it worked fine. I&#39;m not sure if this is a bug or a feature.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;colors-and-gradients&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Colors and gradients&lt;/h3&gt;
&lt;p&gt;When you specify a &lt;code&gt;Color&lt;/code&gt; in SwiftUI, you have the ability to add a standard gradient:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Capsule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;orange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gradient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a great modifier as it gives us a very simple way to improve the look of a flat block of color.&lt;/p&gt;
&lt;p&gt;This year, we also get the ability to mix colors, like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Capsule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    selectedColor
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mixColor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; by&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mixPercent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gradient
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open the &lt;strong&gt;UI Samples&lt;/strong&gt; window and go to the &lt;strong&gt;Form&lt;/strong&gt; tab to test out some color mixes. This not only allows us to create our own colors, but it also gives us a way to change colors programmatically like I do with the slider in the example. You can even chain &lt;code&gt;mix&lt;/code&gt; modifiers for more variability.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/color_mix.png&quot; alt=&quot;Color Mix&quot;&gt;&lt;/p&gt;
&lt;p&gt;The other big new thing is mesh gradients. As you saw in the &lt;a href=&quot;https://troz.net/post/2024/swiftui-mac-2024/#windows&quot;&gt;Windows&lt;/a&gt; section above, I used a mesh gradient to create a background for the &lt;strong&gt;About&lt;/strong&gt; window. The basic technique for creating a mesh gradient is to make a grid of points and assign a color to each point. The points are defined as &lt;code&gt;SIMD2&amp;lt;Float&amp;gt;&lt;/code&gt; so you initialize each point with &lt;code&gt;SIMD2(x, y)&lt;/code&gt;. Then you supply an array of colors for each point. The colors expand out from each point, meshing with their neighbors.&lt;/p&gt;
&lt;p&gt;In my example, I set most of the colors to white but changed the top left, middle and bottom right points to different colors so that I got a diagonal gradient. I also used even spacing for the points, which is not necessary. Check out the code in &lt;strong&gt;MeshBackground.swift&lt;/strong&gt; to see how I did this.&lt;/p&gt;
&lt;p&gt;If you want to know more about mesh gradients, I highly recommend Stewart Lynch&#39;s video: &lt;a href=&quot;https://www.youtube.com/watch?v=s_eQZ8rRV8Y&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;MeshGradients in iOS 18 and Xcode 15&lt;/a&gt;. Although the title says iOS 18, it&#39;s all valid for macOS 15 too.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;sf-symbols&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SF Symbols&lt;/h3&gt;
&lt;p&gt;My final topic is the ability to add animated effects to &lt;a href=&quot;https://developer.apple.com/sf-symbols/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SF Symbols&lt;/a&gt;. You can test this in the &lt;strong&gt;FormSamplesView.swift&lt;/strong&gt; preview, which only works because I used &lt;code&gt;@Previewable&lt;/code&gt; to add the menu selection to the preview. Before that, the preview worked but ignored any menu changes. The demo is a bit strange because it will often show the previous animation before using the new one, but this is not the way I would use these effects in an app and it does allow you to see what&#39;s happening. Just keep clicking &lt;strong&gt;Show Effect&lt;/strong&gt; to see your selected animation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/symbol_effects.png&quot; alt=&quot;Symbol Effects&quot;&gt;&lt;/p&gt;
&lt;p&gt;I wasn&#39;t able to get the &lt;code&gt;appear&lt;/code&gt; or &lt;code&gt;scale&lt;/code&gt; effects to work, but maybe that&#39;s an Xcode beta issue. They work in the SF Symbols app. The syntax for applying an effect looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rainbow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;symbolEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bounce&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; showEffect&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specify a symbol as usual, sizing it as needed. Then add the &lt;code&gt;symbolEffect&lt;/code&gt; modifier with the effect you want and a &lt;code&gt;value&lt;/code&gt;. The effect is triggered whenever the value changes.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So those are my favorite SwiftUI additions from WWDC 2024. As we have seen in recent years, Apple is gradually converging the OSes, especially with SwiftUI, which means that a lot of code is no longer platform-specific. But as developers, we still have the responsibility to make our apps look, feel and behave in a way that suits the platform, whatever it is.&lt;/p&gt;
&lt;p&gt;One other point is that a lot of resources will state that they are for iOS, but many of them are totally valid for macOS too. Don&#39;t skip an article or video just because it doesn&#39;t label itself as specifically for macOS.&lt;/p&gt;
&lt;p&gt;If you want more Mac-specific content, check out my recently updated book: &lt;a href=&quot;https://troz.net/books/macos_tutorials/&quot;&gt;macOS by Tutorials&lt;/a&gt;. It&#39;s designed to help iOS developers extend into macOS development.&lt;/p&gt;
&lt;p&gt;The project from this article is available on &lt;a href=&quot;https://github.com/trozware/swiftui-mac-2024&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. And as usual, I&#39;d be thrilled to hear any suggestions, corrections or improvements. Please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, I&#39;d love you to &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Book Building</title>
    <link href="https://troz.net/post/2024/book_building/" />
    <updated>2024-08-06T01:29:44Z</updated>
    <id>https://troz.net/post/2024/book_building/</id>
    <content type="html">&lt;p&gt;I started writing books for Kodeco when I was asked to contribute some Mac content to &lt;a href=&quot;https://www.kodeco.com/books/swiftui-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftUI by Tutorials&lt;/a&gt;. I quickly learned that I really enjoyed long form technical writing but I wanted to create more Mac-specific content. After some negotiations, Kodeco agreed to my plan to write &lt;a href=&quot;https://troz.net/books/macos_tutorials&quot;&gt;macOS by Tutorials&lt;/a&gt; and the first edition was published in April 2022.&lt;/p&gt;
&lt;p&gt;As I have written elsewhere, Kodeco has pivoted away from books and into providing multi-modal courses. They agreed to transfer the rights of the macOS by Tutorials book to me as it was out-of-catalog and since then, I have released an update.&lt;/p&gt;
&lt;p&gt;When writing for Kodeco, all I had to do was write the text and the accompanying code and images. The people at Kodeco took care of the rest. Many thanks to all involved - I greatly underestimated the work that this entailed! Now that I am self-publishing, I have had to learn how to create the various formats myself. This post is a summary of how I did that.&lt;/p&gt;

&lt;h3&gt;Table of Contents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#project-structure&quot;&gt;Project Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#formatting&quot;&gt;Formatting&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#html&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#pdf&quot;&gt;PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#epub&quot;&gt;ePub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#writing-tools&quot;&gt;Writing Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2024/book_building/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a id=&quot;introduction&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Since I always used Markdown when writing for Kodeco, that&#39;s where I started. I wanted to produce the book as an ePub and a PDF, so I needed to convert the Markdown to those formats. I also wanted to produce a web version, so that required a Markdown to HTML conversion. After some research, I started off with &lt;a href=&quot;https://pandoc.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;pandoc&lt;/a&gt;. This is a command-line tool that can convert between many different formats, so it seemed like a good choice.&lt;/p&gt;
&lt;p&gt;I found it very difficult to use and to configure to give the results I wanted. While looking for examples online, I discovered &lt;a href=&quot;https://github.com/progit/progit&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Pro Git&lt;/a&gt;. This looked like it was doing what I was trying to do, but even after following their examples, I was still having problems. Then I discovered that &lt;a href=&quot;https://github.com/progit/progit2&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Pro Git 2nd Edition&lt;/a&gt; was written in AsciiDoc, not Markdown. I had never heard of AsciiDoc but it looked like it might make generating various book formats easier, so I decided to give it a try.&lt;/p&gt;
&lt;p&gt;AsciiDoc is a markup language very similar to Markdown. This quick reference guide shows the syntax: &lt;a href=&quot;https://docs.asciidoctor.org/asciidoc/latest/syntax-quick-reference/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;AsciiDoc Syntax Quick Reference&lt;/a&gt;. The secret sauce was using AsciiDoctor to convert AsciiDoc files into HTML, ePub and PDF. This was much easier to use than pandoc but it took a bit of tweaking to get the results I wanted for each format.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;project-structure&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Project Structure&lt;/h2&gt;
&lt;p&gt;I started by copying the file and folder structure used for Pro Git. I&#39;ve since modified it to suit my needs but the basic structure is much the same. In the root folder, I have the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rakefile&lt;/strong&gt;: The Ruby file that contains the commands to generate the various formats.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gemfile&lt;/strong&gt; &amp;amp; &lt;strong&gt;Gemfile.lock&lt;/strong&gt;: These files handle the Ruby libraries used to create the various formats.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;book.adoc&lt;/strong&gt;: The main file that assembles the contents of the book and is used for the PDFs and ePub versions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;book-html.adoc&lt;/strong&gt;: The same as book.adoc but with an added image, since the HTML version doesn&#39;t have a cover.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;theme folder&lt;/strong&gt;: The files used to style the different versions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;images folder&lt;/strong&gt;: All the images used in the book.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fonts folder&lt;/strong&gt;: The custom fonts used in the book.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;components folder&lt;/strong&gt;: The adoc files referenced by book.adoc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;book folder&lt;/strong&gt;: The adoc files for each chapter as well as preface, introduction, conclusion etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AsciiDoc files can import other AsciiDoc files, so I have a separate file for each chapter in the book as well as files for the non-chapter content. The &lt;strong&gt;book.adoc&lt;/strong&gt; file includes &lt;strong&gt;components/headers.adoc&lt;/strong&gt;, a link to the cover image and &lt;strong&gt;components/contents.adoc&lt;/strong&gt;. &lt;strong&gt;book-html.adoc&lt;/strong&gt; is the same except for including the cover as an image file.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;headers.adoc&lt;/strong&gt; file is where AsciiDoctor reads the metadata for the book:&lt;/p&gt;
&lt;pre class=&quot;language-asciidoc&quot;&gt;&lt;code class=&quot;language-asciidoc&quot;&gt;&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; macOS by Tutorials&lt;/span&gt;
Sarah Reichelt
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:doctype: book&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:doctitle: macOS by Tutorials&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:notitle:&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:toc: macro&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:toclevels: 2&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:figure-caption!:&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:description: macOS App Development for iOS Developers&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:author: Sarah Reichelt&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:copyright: (C) 2024 Sarah Reichelt. All rights reserved.&lt;/span&gt;
&lt;span class=&quot;token attribute-entry tag&quot;&gt;:epub3-stylesdir: theme&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some of these are self-explanatory, but there are some oddities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:doctype: book&lt;/code&gt;: Set the type of this document to a book. AsciiDoctor uses different formatting and allows different header levels for different types of documents.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:notitle: &lt;/code&gt;: Turn off default title page. I wanted to customise my title page.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:toc: macro&lt;/code&gt;: Setting this to macro allows me to specify the location for the table of contents, instead of using the default placement.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:toclevels: 2&lt;/code&gt;: Set the number of header levels to include in the table of contents.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:figure-caption!:&lt;/code&gt;: Tell AsciiDoctor not to add &#39;Figure #&#39; to all image captions.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:epub3-stylesdir: theme&lt;/code&gt;: The folder containing the styles for the ePub version.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next file in components is &lt;strong&gt;content.adoc&lt;/strong&gt;. This is where I include the title page, the table of contents, the introductory pages, the sections and the conclusion. Each section file has a header, an introduction and links to the chapter files.&lt;/p&gt;
&lt;p&gt;With the folder structure in place, I copied the matching Markdown files from the original book. Then I used various tools to convert these files into AsciiDoc. The &lt;a href=&quot;https://github.com/asciidoctor/kramdown-asciidoc&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kramdown AsciiDoc&lt;/a&gt; converter did most of the work to translate Markdown to AsciiDoc. The image links needed special handling, especially when it came to adding captions and sizes. I used Python scripts for this. Python is always my go-to tool for text and file manipulation. I also used Python to strip out the Markdown metadata at the start of every chapter and add the AsciiDoc metadata.&lt;/p&gt;
&lt;p&gt;I am not artistic, so I used Keynote to create the cover image. This allowed me to do object-oriented design with images, shapes and text. I then exported it as a PNG file for use in the book.&lt;/p&gt;
&lt;p&gt;Finally, I had all the files in the right places and in the correct formats. Now to generate and style the various versions.&lt;/p&gt;
&lt;p&gt;This involved using Ruby to run the various AsciiDoctor commands. My Mac had Ruby installed but only an old version, so I used Homebrew to update to Ruby 3.3. I&#39;m not familiar with Ruby, so I had to learn how to install and update the required gems. With them in place, I used the commands in the Rakefile to create the various formats.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;formatting&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Formatting&lt;/h2&gt;
&lt;p&gt;The Pro Git Rakefile started by defining some parameters that could be passed to the AsciiDoctor commands for all the formats.
In their case, params included two attributes: &lt;code&gt;revnumber&lt;/code&gt; and &lt;code&gt;revdate&lt;/code&gt;. I also wanted to specify the syntax highlighter and its style. Working out what to use for the syntax highlighter was a bit tricky as I wanted the code blocks to look like they would in Xcode, but I eventually found the right combination using &lt;a href=&quot;https://pygments.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;pygments&lt;/a&gt; and its &lt;strong&gt;xcode&lt;/strong&gt; style:&lt;/p&gt;
&lt;p&gt;My parameters used by all the different formats ended up like&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;version_string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;2.0&#39;&lt;/span&gt;&lt;/span&gt;
date_string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;now&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strftime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;%Y-%m-%d&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--attribute revnumber=&#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;version_string&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39; --attribute revdate=&#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;date_string&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&quot;&lt;/span&gt;&lt;/span&gt;
syntax_params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--attribute source-highlighter=pygments --attribute pygments-style=xcode&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I had to work out how to style each format.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;html&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;HTML&lt;/h3&gt;
&lt;p&gt;Pro Git uses this command for generating the HTML, which gave me my starting point:&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;sh &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bundle exec asciidoctor &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; -a data-uri progit.asc&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had already added a second set of params for the syntax highlighter, but the next thing was to add my own style sheet. The Pro Git repo had a &lt;strong&gt;theme&lt;/strong&gt; folder with sub-folders for each format. I tried editing &lt;strong&gt;theme/html/html.css&lt;/strong&gt; but that didn&#39;t work. I eventually found that I needed to create a new file called &lt;strong&gt;docinfo.html&lt;/strong&gt; in the &lt;strong&gt;theme&lt;/strong&gt; folder and then add more attributes to access it. This changed my command to:&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;book_title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;macOS_by_Tutorials_v2.0.html&#39;&lt;/span&gt;&lt;/span&gt;
style_sheet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;-a docinfodir=theme -a docinfo=shared&#39;&lt;/span&gt;&lt;/span&gt;

sh &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bundle exec asciidoctor &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;syntax_params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;style_sheet&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; -a data-uri -o &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;book_title&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; book-html.adoc&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This meant that I could add my own CSS or JavaScript to the &lt;strong&gt;theme/docinfo.html&lt;/strong&gt; file and it would be included in the HTML output. The &lt;code&gt;data-uri&lt;/code&gt; attribute tells AsciiDoctor to embed the images in the HTML output. I didn&#39;t want to have to give people a folder of images to go with the HTML file, I wanted a single file that could be opened in a browser and would include everything.&lt;/p&gt;
&lt;p&gt;I generated an HTML file using the default styling, then copied the default CSS into a &lt;code&gt;style&lt;/code&gt; block in this file. I then edited it to my satisfaction.&lt;/p&gt;
&lt;p&gt;One refinement for the HTML version was to add a copy button on the code blocks. I used &lt;a href=&quot;https://clipboardjs.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;clipboard.js&lt;/a&gt; for this and tweaked the style and position of the copy button to match my style. I also added JavaScript to make the copy operation ignore the numbered comment lines that are used to mark explanation points throughout the code.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;pdf&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;PDF&lt;/h3&gt;
&lt;p&gt;The PDF was the most difficult to format as it doesn&#39;t use CSS but includes some other formatting options in a YML file, perhaps based on Postscript? I used AsciiDoctor to export the default PDF theme so I could see the format. Then I created my own PDF theme file, extending the default theme. This involved quite a bit of trial and error as I had to work out what the various styles were called and how to override them. I also wanted to create a dark mode version of the PDF, so I made a second theme file for that.&lt;/p&gt;
&lt;p&gt;The two theme files are called &lt;strong&gt;pdf-theme.yml&lt;/strong&gt; and &lt;strong&gt;pdfdark-theme.yml&lt;/strong&gt;. They are both inside the &lt;strong&gt;theme&lt;/strong&gt; folder. I used these in the Rakefile like this:&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;pdf_params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;-a pdf-theme=pdf -a pdf-themesdir=theme -a pdf-fontsdir=.&#39;&lt;/span&gt;&lt;/span&gt;
output_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; book_title &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;_v&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; version_string &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.pdf&#39;&lt;/span&gt;&lt;/span&gt;

sh &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bundle exec asciidoctor-pdf &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;pdf_params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;syntax_params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; -o &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;output_name&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; book.adoc&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And:&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;pdf_params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;-a pdf-theme=pdfdark -a pdf-themesdir=theme -a pdf-fontsdir=.&#39;&lt;/span&gt;&lt;/span&gt;
syntax_params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--attribute source-highlighter=pygments --attribute pygments-style=github-dark&quot;&lt;/span&gt;&lt;/span&gt;
output_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; book_title &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;_v&#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; version_string &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;_dark.pdf&#39;&lt;/span&gt;&lt;/span&gt;

sh &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bundle exec asciidoctor-pdf &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;pdf_params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;syntax_params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; -o &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;output_name&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; book.adoc&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the &lt;strong&gt;-theme.yml&lt;/strong&gt; part of the file name is not included in the command, just the base name. The &lt;code&gt;pdf-themesdir&lt;/code&gt; attribute is used to point to the theme folder and the &lt;code&gt;pdf-fontsdir&lt;/code&gt; attribute is used to point to the root folder. This worked because I had the font files in a &lt;strong&gt;fonts&lt;/strong&gt; folder and included the folder path in the font specifications of the YML files.&lt;/p&gt;
&lt;p&gt;For the PDF versions, I also added a header to every page with the book title and the chapter title using the YML files.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;epub&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;ePub&lt;/h3&gt;
&lt;p&gt;Finally, the ePub version. To my surprise, an ePub is actually a zip file. The contents are XHTML files, images and styles. The secret here was again to have files with the correct name in the correct location. In this case, I needed &lt;strong&gt;epub3.scss&lt;/strong&gt; and &lt;strong&gt;epub3-css3-only.css&lt;/strong&gt; in the &lt;strong&gt;theme&lt;/strong&gt; folder. I left &lt;strong&gt;epub3.scss&lt;/strong&gt; empty, but it still had to be there. The &lt;strong&gt;epub3-css3-only.css&lt;/strong&gt; file had styles for where I wanted to override the defaults. It wasn&#39;t always easy to know what classes to apply the styles to, but I opened one of the XHTML files in my browser and used the web inspector to find them.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;writing-tools&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Writing Tools&lt;/h2&gt;
&lt;p&gt;The editor I used was &lt;a href=&quot;https://code.visualstudio.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Visual Studio Code&lt;/a&gt;. While it&#39;s not a native Mac app, it&#39;s a very powerful editor and has a fantastic array of extensions. I installed the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=asciidoctor.asciidoctor-vscode&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;AsciiDoc extension&lt;/a&gt; which allowed me to preview the AsciiDoc files in the editor.&lt;/p&gt;
&lt;p&gt;There was a problem with previewing because of the image paths. The &lt;strong&gt;book.adoc&lt;/strong&gt; file specifies the path to the images folder, but when previewing an individual chapter file, this was not applied. I had to add a line to the metadata at top of each chapter file to include the path to the images folder. I also added a header to show a table of contents to make it easier to jump around the file while editing. When building the entire book, these headers had to go - the image path over-rode the one in the book.adoc file and meant that book.adoc couldn&#39;t find any images, and a table of contents was included in every chapter. I ended up turning to Python again and wrote a script to add and remove these headers as needed.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://github.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; to store the book files and to collaborate with my editor, or language engineer as he prefers to be called.&lt;/p&gt;
&lt;p&gt;The other important extension is &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Code Spell Checker&lt;/a&gt;. Of course there will still be typos in the final book, but this extension helps to catch some of them. A big advantage of this spell checker is that it defaults to US English spelling. I normally write UK/Australian English, but I prefer to use US English for my books to avoid things like &lt;strong&gt;Create an NSColor is store the selected colour&lt;/strong&gt; :-). Another big plus is that the custom dictionary is a JSON file stored in the project. That way my editor gets the same spell checking as I do, and I can easily edit the file if I want to delete or change a custom spelling.&lt;/p&gt;
&lt;p&gt;All the code is written in Xcode using the default settings and theme as much as possible.&lt;/p&gt;
&lt;p&gt;Screenshots are taken with &lt;a href=&quot;https://cleanshot.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;CleanShot X&lt;/a&gt; and edited in Preview, which usually just involves cropping.&lt;/p&gt;
&lt;p&gt;For hardware, my main computer is a 24&amp;quot; M3 iMac, my keyboard is an &lt;a href=&quot;https://www.zsa.io/moonlander/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ergodox Moonlander&lt;/a&gt; and my mouse is a &lt;a href=&quot;https://www.logitech.com/en-au/products/mice/mx-vertical-ergonomic-mouse.910-005449.html&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Logitech MX Vertical&lt;/a&gt;. I also have a 13&amp;quot; M1 MacBook Pro for when I need to work in a different location.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;AsciiDoc is just as easy to write as Markdown, but makes creating books vastly easier.&lt;/p&gt;
&lt;p&gt;While creating all the files to format the first book took a lot of time and effort, I now have a template and subsequent books will start from that, making the process much less complicated. I imagine that I will keep tweaking the configuration and design as I go to make each format look better, but I now have the basic structure in place.&lt;/p&gt;
&lt;p&gt;If anyone is interested, I am happy to share the template and the scripts I have written. Contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>macOS by Tutorials</title>
    <link href="https://troz.net/post/2024/macos_tutorials/" />
    <updated>2024-07-23T09:49:12Z</updated>
    <id>https://troz.net/post/2024/macos_tutorials/</id>
    <content type="html">&lt;p&gt;In April 2022, Kodeco published the first edition of &lt;a href=&quot;https://www.kodeco.com/books/macos-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS by Tutorials&lt;/a&gt;. Nothing stays the same in the world of Apple development, so I started to plan an update after WWDC 2023. At around that time, Kodceo was going through a transformation from being like a library to being more of an educational institution. Their focus is now on multi-modal courses and they are no longer interested in publishing books.&lt;/p&gt;
&lt;p&gt;Kodeco agreed to transfer the rights of the macOS by Tutorials book to me, since it was out-of-catalog and since then, I have been working on an update which is almost ready for release. I had hoped to have it out before WWDC 2024, but real-life has a habit of getting in the way, and that was not possible.&lt;/p&gt;
&lt;p&gt;The book is now available at &lt;a href=&quot;https://sarahreichelt.gumroad.com/l/oximx&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gumroad&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you previously bought the book from either Kodeco or Amazon, please &lt;a href=&quot;mailto:books@troz.net?subject=macOS%20by%20Tutorials%20Discount&quot;&gt;email me&lt;/a&gt; for a 50% discount code.
I am unable to provide a free update for the Kodeco version, but I will provide free updates subsequently for the self-published version.&lt;/p&gt;

&lt;p&gt;Every project in this book has been updated to macOS 14 Sonoma and Xcode 15. They have also been tested using Xcode 16 beta 3.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2024/mos_cover_small.png&quot; alt=&quot;Book cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;The major changes in this edition include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;On This Day&lt;/em&gt; app uses &lt;code&gt;NavigationSplitView&lt;/code&gt; instead of the older &lt;code&gt;NavigationView&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On This Day&lt;/em&gt; implements the new &lt;code&gt;Observation&lt;/code&gt; framework for its data.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;Time-ato&lt;/em&gt; app no longer imports the external &lt;code&gt;LaunchAtLogin&lt;/code&gt; package but now uses Apple&#39;s &lt;code&gt;ServiceManagement&lt;/code&gt; library.&lt;/li&gt;
&lt;li&gt;Creating an app intent in &lt;em&gt;ImageSipper&lt;/em&gt; uses the new code-based system.&lt;/li&gt;
&lt;li&gt;The workflow for distributing your app has been updated to reflect the changes in App Store Connect and Xcode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I am now a self-publisher, I will be able to release more timely and incremental updates and will incorporate the latest changes to macOS, Xcode Swift and SwiftUI as they are released.&lt;/p&gt;
&lt;p&gt;To keep up-to-date with my books and other projects, please read my introduction and subscribe to my Gumroad &lt;a href=&quot;https://sarahreichelt.gumroad.com/p/welcome-to-sarah-s-books&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;newsletter&lt;/a&gt;. It will be very low volume, mainly about my own books.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Man Reader 2</title>
    <link href="https://troz.net/post/2023/manreader2/" />
    <updated>2023-10-13T05:46:38Z</updated>
    <id>https://troz.net/post/2023/manreader2/</id>
    <content type="html">&lt;p&gt;Several years after I started planning it, Man Reader version 2 is now available.
This is a major update with a completely new, modern, user interface and it’s available from the &lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Man Reader is a utility app for reading macOS&#39;s &lt;strong&gt;man&lt;/strong&gt; pages. Man Reader allows
quick, convenient and easy access to the man pages on your system, useful for
programmers, system administrators and tweakers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://troz.net/icons/ManReader128.png&quot; alt=&quot;Buy Man Reader from the Mac App Store&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complete re-write for modern versions of macOS.&lt;/li&gt;
&lt;li&gt;Only runs on macOS Sonoma 14.0 or later.&lt;/li&gt;
&lt;li&gt;Support for tabs and multiple windows.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/ManReader2.png&quot; alt=&quot;Man Reader 2&quot;&gt;
&lt;img src=&quot;https://troz.net/images/2023/ManReader2_dark.png&quot; alt=&quot;Man Reader 2 dark mode&quot;&gt;&lt;/p&gt;
&lt;p&gt;Read all the details at the &lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader support page&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Development Notes&lt;/h2&gt;
&lt;p&gt;Version 1.0 of Man Reader was released in 2012. It was written in Objective-C
and it suffered from the MVC - Massive View Controller - problem, but it kept going.&lt;/p&gt;
&lt;p&gt;Over the years, I added new features and fixed bugs, but as macOS evolved, it started looking its age.&lt;/p&gt;
&lt;p&gt;I&#39;ve been planning a major update for a long time, but I&#39;ve been busy with other things so in 2021
I released an interim update to keep the app working and readable on the latest macOS.&lt;/p&gt;
&lt;p&gt;This year, I finally got a decent break in my usual schedule and I was able to get to work.&lt;/p&gt;
&lt;p&gt;The new version was always going to be in Swift. After writing Swift for years now, I am
very comfortable with it and I write better and safer code in Swift than I can in Objective-C.&lt;/p&gt;
&lt;p&gt;The difficult question was whether to use SwiftUI or AppKit as the basis for the app, understanding that
whichever way I went, I could add in components from the other framework. This required
several iterations to find the best solution.&lt;/p&gt;
&lt;p&gt;I prefer working in SwiftUI so I started there first, but as I have written elsewhere,
SwiftUI Lists, especially on macOS, do not handle large numbers of rows. This has improved a lot
in macOS Sonoma but it&#39;s still not great. On my system, I have
more than 10,000 man pages and that rises to over 25,000 if I include all of Xcode&#39;s commands.
I tried a number of different approaches to make a SwiftUI List work, but then decided I&#39;d have to use
AppKit as the starting point for the app.&lt;/p&gt;
&lt;p&gt;This solved the performance issue but caused others. I&#39;ve got so used to SwiftUI&#39;s reactive
data flow that I found it difficult to work with pure AppKit. There were also things that SwiftUI is
just better at, like support for multiple windows.&lt;/p&gt;
&lt;p&gt;So I went back to a SwiftUI base and embedded an AppKit &lt;code&gt;NSTableView&lt;/code&gt; to handle the long list in the sidebar.
This was interesting because I dislike creating an &lt;code&gt;NSTableView&lt;/code&gt; via code - there are just too many components
and settings that storyboards make simple. So for the first time, I embedded a Storyboard view into a SwiftUI
view. I did this by adding an &lt;code&gt;NSViewController&lt;/code&gt; with a &lt;strong&gt;xib&lt;/strong&gt; file and then using that in an
&lt;code&gt;NSViewControllerRepresentable&lt;/code&gt; struct. This worked well and the published app has a SwiftUI base and an AppKit sidebar.&lt;/p&gt;
&lt;p&gt;One feature that I was very keen to maximise was keyboard support. To a large extent, you can operate
Man Reader without the mouse. The menus show most of the available shortcuts, but you can also
press Tab or Shift-Tab to get to the two search fields. The sidebar is a table so you can use the arrow keys
to navigate but you can also use type-select if there is no active cursor. Start typing to select
the first page with a name starting with those characters.&lt;/p&gt;
&lt;p&gt;One other design decision was to support the latest version of macOS only. The old version of Man Reader is
still available for anyone on older systems, but I wanted to learn and use all the latest features of
Swift and SwiftUI.&lt;/p&gt;
&lt;p&gt;If you have any ideas for future versions or
encounter any bugs, please &lt;a href=&quot;mailto:sarah@troz.net?subject=Man%20Reader%202&quot;&gt;email me&lt;/a&gt; or get in touch through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI Data Flow 2023</title>
    <link href="https://troz.net/post/2023/swiftui-data-flow-2023/" />
    <updated>2023-07-09T23:22:54Z</updated>
    <id>https://troz.net/post/2023/swiftui-data-flow-2023/</id>
    <content type="html">&lt;p&gt;SwiftUI is a reactive framework where the data drives the UI. In 2019, I wrote a &lt;a href=&quot;https://troz.net/post/2019/swiftui-data-flow/&quot;&gt;post&lt;/a&gt; detailing how I manage the various forms of data flow through a SwiftUI app, and with the help of others in the community, I iterated over this until I had a good understanding of the concepts and which methods you should use when. In 2021, I updated the post to cover the minor changes, but there have been no major modifications since then.&lt;/p&gt;
&lt;p&gt;At WWDC 2023, things changed a lot! With the introduction of Swift macros, the SwiftUI team was able to reduce the number of property wrappers need to send data around, and remove a lot of boilerplate code.&lt;/p&gt;
&lt;p&gt;For this article, I have re-written my sample app as a Mac app and updated it to use the new data macros.&lt;/p&gt;
&lt;p&gt;Updated 10th July 2023 to cover changes in version 3 of all the betas.&lt;/p&gt;

&lt;h3&gt;Table of Contents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#observation&quot;&gt;Observation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#sample-app&quot;&gt;Sample app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#property&quot;&gt;Property&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#state&quot;&gt;@State&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#binding&quot;&gt;@Binding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#binding-for-a-structure&quot;&gt;@Binding for a Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#observable-and-bindable&quot;&gt;@Observable and @Bindable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#observable-and-bindable-list&quot;&gt;@Observable and @Bindable List&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#environment&quot;&gt;@Environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#wrapping-up&quot;&gt;Wrapping Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the release of all the version 3 betas, I have added these sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#update-1&quot;&gt;Old code still works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#update-2&quot;&gt;Observed properties no longer require initial values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#update-3&quot;&gt;&lt;code&gt;@Environment&lt;/code&gt; properties cannot be used for bindings.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;observation&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Observation&lt;/h3&gt;
&lt;p&gt;In the WWDC talk on &lt;a href=&quot;https://developer.apple.com/wwdc23/10149&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Discover Observation in SwiftUI&lt;/a&gt;, Apple showed the following flow chart:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/apple_data_flow.jpeg&quot; alt=&quot;Apple&#39;s flow chart&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is a good starting point, but I feel that it leaves out a few cases, so I&#39;ve made my own:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/my_data_flow.png&quot; alt=&quot;My flow chart&quot;&gt;&lt;/p&gt;
&lt;p&gt;Apart from adding some details to the decisions points, there are really only two additions to my chart:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a property doesn&#39;t need to change, it can be a &lt;code&gt;let&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@Bindable&lt;/code&gt; only works for classes. The equivalent for structs or primitive data types is still &lt;code&gt;@Binding&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new macros system removes this protocol and property wrappers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ObservableObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@ObservedObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@EnvironmentObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@Published&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s a lot of typing we don&#39;t have to do any more!&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;update-1&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;UPDATE 1:&lt;/h4&gt;
&lt;p&gt;I didn&#39;t make it clear originally, but all these property wrappers still work, and you can still use &lt;code&gt;@Binding&lt;/code&gt; for classes, so you can adapt to the new Observation code incrementally. The Apple developer site has a very useful article on &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;migrating from ObservableObject&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;sample-app&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sample app&lt;/h3&gt;
&lt;p&gt;My sample app demonstrates the following data flow options:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/data_flow_types.png&quot; alt=&quot;Sample app options&quot;&gt;&lt;/p&gt;
&lt;p&gt;The code is available on &lt;a href=&quot;https://github.com/trozware/swiftui-data-flow-2023&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; and I recommend you download the project and follow along. It requires macOS 14 and Xcode 15. Currently I&#39;m using macOS 14.0 beta 3 (23A5286g) and Xcode 15.0 beta 3 (15A5195k), but I will update this article with any changes as we work through the beta cycle.&lt;/p&gt;
&lt;p&gt;Because Xcode 15 now supports live previews for Mac apps, you can test all the options from inside Xcode. In the Project navigator, expand the numbered groups and preview the first file in each group to view and interact with that data flow type.&lt;/p&gt;
&lt;p&gt;Or go to the Bookmarks navigator where I&#39;ve used the new bookmaking system to provide quick links to each one. You can even check off each one as you finish that section.&lt;/p&gt;
&lt;p&gt;In the code snippets below, I include the essentials, but strip out most display modifiers and some similar views to keep the code short. The project contains the complete code.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;property&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Property&lt;/h3&gt;
&lt;p&gt;The first option is using a static property. We get so caught up in property wrappers that it&#39;s easy to forget that SwiftUI doesn&#39;t always need them. Using static text from a constant may seem like a simplistic and obvious example, but separating it out allows for localization or re-usability as well as making the view body neater and easier to read.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// static property that never changes&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; greeting &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello from SwiftUI!&quot;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Using the property directly in a View&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;greeting&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My flow chart shows two use cases for &lt;code&gt;let&lt;/code&gt;. This covers the first one where the view owns the property. The second is when a parent supplies the property to a view but the child view never changes the property. You&#39;ll see an example of this later on in this article.&lt;/p&gt;
&lt;p&gt;A parent view may have some dynamic data that it uses to set properties in a subview where the subview only needs to display the data statically. This data will still change as the parent view changes but the subview will not be able to change the data itself or in the parent view.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a view owns a property or gets it from a parent but doesn&#39;t need to change it, use &lt;code&gt;let&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;state&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;@State&lt;/h3&gt;
&lt;p&gt;This is where things start to get interesting in the SwiftUI world. SwiftUI views are contained in structs, so are non-mutable. Also, views are re-created every time the data changes, so any properties are re-created then too. By marking a property with the &lt;code&gt;@State&lt;/code&gt; property wrapper, you are telling SwiftUI that you want it to keep this data in a separate portion of memory, allow it to be mutated, and preserve the current value during view refreshes.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UsingState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Using @State as this view owns and can mutate the property&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Displaying and editing @State property&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Counter = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        counter &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;plus.circle&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;counter&lt;/code&gt; property uses &lt;code&gt;@State&lt;/code&gt;. The text view displays it and the &lt;code&gt;Button&lt;/code&gt; view can change it. The button&#39;s action changes the counter which causes the view to redraw in order to display the new value, but the value of the &lt;code&gt;@State&lt;/code&gt; property does not get reset to 0 every time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Whenever a view owns the property and that property is mutable, declare it using the &lt;code&gt;@State&lt;/code&gt; property wrapper.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;binding&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;@Binding&lt;/h3&gt;
&lt;p&gt;The Apple flow chart mentioned &lt;code&gt;@Bindable&lt;/code&gt; but that only works with observable classes. For connecting structures or primitive data types to subviews, use &lt;code&gt;@Binding&lt;/code&gt;. Apple does this internally for controls like &lt;code&gt;Toggle&lt;/code&gt; and &lt;code&gt;TextField&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A problem with building SwiftUI views is that it is very easy to end up with a gigantic &lt;em&gt;Pyramid of Doom&lt;/em&gt; as you embed views within views within views. Extracting subviews is a great solution, but then you need a way to pass the data to a subview in a way that allows the subview to edit that property &lt;strong&gt;and&lt;/strong&gt; have the edits flow back to the parent.&lt;/p&gt;
&lt;p&gt;This is where &lt;code&gt;@Binding&lt;/code&gt; and &lt;code&gt;@Bindable&lt;/code&gt; come into play.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Binding 1&lt;/strong&gt; example uses a primitive data type - in this case an &lt;code&gt;Int&lt;/code&gt;. The parent &lt;code&gt;Numbers&lt;/code&gt; view declares it using &lt;code&gt;@State&lt;/code&gt; because it owns the property, and it displays the value. The &lt;code&gt;Numbers&lt;/code&gt; view passes the property to the &lt;code&gt;NumberChooser&lt;/code&gt; subview as an &lt;code&gt;@Binding&lt;/code&gt; property by using the &lt;code&gt;$&lt;/code&gt; prefix.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Using @State as this view owns and can mutate the property&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; stepperValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Display @State property&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Parent view value = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;stepperValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// Pass to subview as a Binding ($) for two-way editing&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;NumberChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $stepperValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;NumberChooser&lt;/code&gt; subview has this property marked with &lt;code&gt;@Binding&lt;/code&gt;. It has no initial value as this view expects to receive the value from its parent view. &lt;code&gt;NumberChooser&lt;/code&gt; has a &lt;code&gt;Stepper&lt;/code&gt; for changing the value of the property. It displays the value after each change and you can see that the value displayed in the parent view also changes as the change flows back.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Using value from parent with 2-way binding&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Using bound state from parent with 2-way binding&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $stepperValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Value in child = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;stepperValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// Using bound state from parent as property&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// this view cannot change the value&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;NumberBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; stepperValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;NumberChooser&lt;/code&gt; has its own subview - &lt;code&gt;NumberBlock&lt;/code&gt;. This is purely a display view that never edits the value. &lt;code&gt;NumberChooser&lt;/code&gt; passes it the value and it stores it as a &lt;code&gt;let&lt;/code&gt;. Whenever the value changes, SwiftUI redraws the view with the new value, and the display updates. This demonstrates the second use of &lt;code&gt;let&lt;/code&gt; that I talked about in the property section. There is no need to make this a &lt;code&gt;var&lt;/code&gt; because it doesn&#39;t change - it gets re-created with every update.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// As this view never changes the value, there is no need to bind it&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;stepperValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.square&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;binding-for-a-structure&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;@Binding for a Structure&lt;/h3&gt;
&lt;p&gt;The previous example showed how to use &lt;code&gt;@Binding&lt;/code&gt; for a primitive data type like &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt; etc. But bindings also work for any value type, including structures, and that&#39;s what you can see in &lt;strong&gt;Binding 2&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;While this is not very common, I enjoyed writing this example for a couple of reasons. Firstly, it shows how to draw the UI based on the different cases in an enum. Check out &lt;strong&gt;Pizza.swift&lt;/strong&gt; which has three enums. Each of the picker views loops through one of these enums to draw the view.&lt;/p&gt;
&lt;p&gt;Secondly, it demonstrates how you can pass portions of the data model to a subview, instead of sending everything everywhere.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PizzaView&lt;/code&gt; has a &lt;code&gt;Pizza&lt;/code&gt; property declared using &lt;code&gt;@State&lt;/code&gt; because this view owns the data. It draws a &lt;code&gt;Form&lt;/code&gt; containing three different pickers. Each picker gets a binding to one of the properties of the pizza. A &lt;code&gt;Text&lt;/code&gt; view shows the assembled data to confirm that the parent knows about any changes.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PizzaView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Using @State for a struct&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; pizza &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pizza&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Using 2-way binding but each component&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// only needs 1 property from the struct&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PizzaNamePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedPizzaName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PizzaSizePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedPizzaSize&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PizzaCrustPicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedPizzaCrust&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;crust&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// Text representation to prove that the&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// subviews are modifying the parent struct&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pizzaSelection&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I won&#39;t show the code for all of the pickers, but here&#39;s the one to select the size:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PizzaSizePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// A single property of the Pizza struct is passed hare.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// It&#39;s a Binding so data flows 2 ways.&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedPizzaSize&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PizzaSize&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Select your size:&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedPizzaSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PizzaSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allCases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; pizzaSize &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pizzaSize&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawValue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;capitalized&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pickerStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;segmented&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&#39;re passing a primitive data type or a struct to a subview, and the subview can change the value of this property, use &lt;code&gt;@Binding&lt;/code&gt; in the subview and pass the property with a &lt;code&gt;$&lt;/code&gt; prefix.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;observable-and-bindable&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;@Observable and @Bindable&lt;/h3&gt;
&lt;p&gt;So far, the examples have been largely the same as before. Now the new property wrappers appear. The first big change is in the model class: &lt;code&gt;ColorSet&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Observation&lt;/span&gt;

&lt;span class=&quot;token attribute atrule&quot;&gt;@Observable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Observable class&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The color components are not private so any changes&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// get published to the views that are observing&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foregroundRed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foregroundGreen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foregroundBlue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Computed variables to create the RGB colors from the components&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foregroundColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; foregroundRed&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; green&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; foregroundGreen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; foregroundBlue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing to notice is the new import: &lt;code&gt;Observation&lt;/code&gt;. This is the library that supports the new &lt;code&gt;@Observable&lt;/code&gt; macro. Previously, &lt;code&gt;ColorSet&lt;/code&gt; conformed to the &lt;code&gt;ObservableObject&lt;/code&gt; protocol and the properties that needed to be observed were marked with the &lt;code&gt;@Published&lt;/code&gt; property wrapper.&lt;/p&gt;
&lt;p&gt;Now the class uses the &lt;code&gt;@Observable&lt;/code&gt; macro and any property that isn&#39;t private, is automatically published. This is only available for classes, not structures.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;update-2&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;UPDATE 2:&lt;/h4&gt;
&lt;p&gt;&lt;s&gt;The properties must all have an initial value - setting them in an &lt;code&gt;init&lt;/code&gt; is not sufficient and will not build.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;In beta 3, observed properties no longer require an initial value. For this class, I actually want to specify initial values, but in the next section, I do not.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you want a look at what&#39;s happening inside the macro, right-click on &lt;code&gt;@Observable&lt;/code&gt; and select &lt;strong&gt;Expand Macro&lt;/strong&gt;. When you&#39;ve finished, right -click again and choose &lt;strong&gt;Hide Macro Expansion&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/show_macro.png&quot; alt=&quot;Expand the macro&quot;&gt;&lt;/p&gt;
&lt;p&gt;While this is not something I can read comfortably, it shows how macros are regular Swift code that saves a lot of typing, but are not doing magical things invisibly in the background.&lt;/p&gt;
&lt;p&gt;Now that the model uses the new macro, how do we use it?&lt;/p&gt;
&lt;p&gt;Again, the owning view declares it using &lt;code&gt;@State&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Using an Observable for reference-based data (classes)&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; colorSet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ColorSetView&lt;/code&gt; uses the data to add color modifiers to the view background and to a system image of a cat.&lt;/p&gt;
&lt;p&gt;But the next change is when sending the data to the &lt;code&gt;ColorChooser&lt;/code&gt; view. This needs two-way binding so the current values can be passed to the chooser and any changes can be passed back. But with the new Observation methods, this uses &lt;code&gt;@Bindable&lt;/code&gt; and not &lt;code&gt;@Binding&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, the &lt;code&gt;ColorChooser&lt;/code&gt; gets the &lt;code&gt;colorSet&lt;/code&gt; property, but this doesn&#39;t need the &lt;code&gt;$&lt;/code&gt; prefix.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ColorChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; colorSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ColorChooser&lt;/code&gt; view receives it as &lt;code&gt;@Bindable&lt;/code&gt; and can in turn, pass it down to its subviews, again using the &lt;code&gt;@Bindable&lt;/code&gt; technique which requires no &lt;code&gt;$&lt;/code&gt; prefixes.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Bindable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; colorSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSet&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The chooser subview also get passed the Observed&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ForeColorChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; colorSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/color_chooser.png&quot; alt=&quot;Color chooser&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is one oddity to beware of. When declaring the original &lt;code&gt;colorSet&lt;/code&gt; property, I use &lt;code&gt;@State&lt;/code&gt; but if I declare it as a simple &lt;code&gt;var&lt;/code&gt;, the app still works. Don&#39;t do this!&lt;/p&gt;
&lt;p&gt;If you remember the older code style, the owning view initialized an &lt;code&gt;ObservableObject&lt;/code&gt; using &lt;code&gt;@StateObject&lt;/code&gt;. Subsequent views declared the same property using &lt;code&gt;@ObservedObject&lt;/code&gt; but it was very important for the owning view to use &lt;code&gt;@StateObject&lt;/code&gt; to avoid strange and difficult to trace bugs. The app worked if you used &lt;code&gt;@ObservedObject&lt;/code&gt; instead of &lt;code&gt;@StateObject&lt;/code&gt; for the original declaration, but then odd things could happen.&lt;/p&gt;
&lt;p&gt;I think this is the same, and it&#39;s just as important for the owning view to declare the original property using &lt;code&gt;@State&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;observable-and-bindable-list&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;@Observable and @Bindable List&lt;/h3&gt;
&lt;p&gt;In the original post, this was the section that gave me the most trouble - having a list of data objects where each one could be edited and updating the original list to show the edits.&lt;/p&gt;
&lt;p&gt;This is now a lot easier, although it took me some time to realize this.&lt;/p&gt;
&lt;p&gt;I have a sample data file and a &lt;code&gt;Person&lt;/code&gt; model class using &lt;code&gt;@Observable&lt;/code&gt;. The &lt;code&gt;PersonListModel&lt;/code&gt; class holds an array of these &lt;code&gt;Person&lt;/code&gt; objects and it also uses &lt;code&gt;@Observable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I wanted to make the selection of the list point directly to a &lt;code&gt;Person&lt;/code&gt; instead of going through an &lt;code&gt;id&lt;/code&gt;, so I added this extension to &lt;code&gt;Person&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Hashable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Equatable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;into hasher&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;inout&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Hasher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    hasher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;combine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lhs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rhs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    lhs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; rhs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I was able to set up a &lt;code&gt;List&lt;/code&gt; to display the names of the people, with the list&#39;s selection bound to an optional &lt;code&gt;Person&lt;/code&gt;. I had to add a &lt;code&gt;tag&lt;/code&gt; to the person, I presume the tag is set to the &lt;code&gt;id&lt;/code&gt; by default.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedPerson&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; $person &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two important things to see in this code snippet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The contents of the &lt;code&gt;ForEach&lt;/code&gt; is a binding as you can see from the &lt;code&gt;$&lt;/code&gt; prefix&lt;/li&gt;
&lt;li&gt;The argument passed in to each entry in the list is also a binding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two bindings make the list respond to edits.&lt;/p&gt;
&lt;p&gt;With this in place, it was time to think about an editor and I decided to try out the new inspector. Unfortunately, unlike with sheets, you can only trigger an inspector with a Boolean so I added an event listener to set the inspector trigger:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; selectedPerson&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; oldValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  shouldShowInspector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newValue &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, I added this inspector modifier:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;inspector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $shouldShowInspector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; selectedPerson &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;PersonEditView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; selectedPerson&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though &lt;code&gt;shouldShowInspector&lt;/code&gt; is only true when there is a valid &lt;code&gt;selectedPerson&lt;/code&gt;, force unwrapping causes a crash as it appears that the inspector tries to draw itself off-screen on launch.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;PersonEditView&lt;/code&gt; in the inspector receives the person as &lt;code&gt;@Bindable&lt;/code&gt; and populates the fields on a form. And editing the data flows right back to the list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/person_edit.mp4&quot; alt=&quot;Editing a person&quot;&gt;&lt;/p&gt;
&lt;p&gt;This took a lot less code than the previous version. Part of the improvement was the ability to use bindings in a list, which we got last year, but another big part was using the &lt;code&gt;Person&lt;/code&gt; directly as the selection. This needs the &lt;code&gt;Hashable&lt;/code&gt; and &lt;code&gt;Equatable&lt;/code&gt; conformance, which I think should be a standard part of &lt;code&gt;Identifiable&lt;/code&gt;, but once that&#39;s in place, all the data flows smoothly.&lt;/p&gt;
&lt;p&gt;If I was using a sheet for the editor, it would be even neater as I could tie the sheet&#39;s appearance to &lt;code&gt;selectedPerson&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you have a model class and you want changes to be published, declare the class with the &lt;code&gt;@Observable&lt;/code&gt; macro. The owning view declares the model using &lt;code&gt;@State&lt;/code&gt; and can pass it to editing subviews using &lt;code&gt;@Bindable&lt;/code&gt; or to display subviews as &lt;code&gt;let&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;environment&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;@Environment&lt;/h3&gt;
&lt;p&gt;The last section is &lt;code&gt;Environment&lt;/code&gt;, which again, has changed a lot. The benefit of using Environment is that the data flow doesn&#39;t have to be unbroken. In the sample app, the parent view (NestedViews) uses the Environment property and so does the GrandChildView, but the ChildView in the middle doesn&#39;t.&lt;/p&gt;
&lt;p&gt;Setting up a property for Environment is the same as for Observable.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Observation&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// An Environment class is just like any Observable class&lt;/span&gt;
&lt;span class=&quot;token attribute atrule&quot;&gt;@Observable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isLoggedIn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The difference is in the way that you use it. When creating the first view that needs this environment value, use an &lt;code&gt;environment&lt;/code&gt; modifier to inject the initial value. In the sample app, this happens in &lt;strong&gt;DataLink.swift&lt;/strong&gt; which displays the view for each example.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;NestedViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To access this property, declare an &lt;code&gt;Environment&lt;/code&gt; property with a key path, which is its type, in this case &lt;code&gt;UserSettings.self&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NestedViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The injected environment property is accessed by type and assigned a name&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; userSettings

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is similar the way we use built-in environment values like &lt;code&gt;colorScheme&lt;/code&gt;, &lt;code&gt;openWindow&lt;/code&gt; and so on. Use this same declaration in any view that needs access.&lt;/p&gt;
&lt;p&gt;Previews need to get access too, like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token other-directive property&quot;&gt;#Preview&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The environment object must be injected into the preview too.&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;NestedViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And even though ChildView doesn&#39;t use the environment object, because its preview contains GrandChildView that does, it also needs the &lt;code&gt;environment&lt;/code&gt; modifier in its preview, but not in the main view code.&lt;/p&gt;
&lt;p&gt;In the sample app, the various nested views are brightly colored to show which is which. Using the &lt;strong&gt;Log In&lt;/strong&gt; or &lt;strong&gt;Log Out&lt;/strong&gt; buttons works on either the outer view or the inner view with the data being synced between them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/environment.png&quot; alt=&quot;Environment&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have a class that is global to your app, like UserSettings in this example, declare the class with the &lt;code&gt;@Observable&lt;/code&gt; macro. Inject it into your view hierarchy using &lt;code&gt;.environment&lt;/code&gt; and then use &lt;code&gt;@Enviroment&lt;/code&gt; to access it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a id=&quot;update-3&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;UPDATE 3:&lt;/h4&gt;
&lt;p&gt;There is one aspect of using &lt;code&gt;@Enviroment&lt;/code&gt; where things get tricky, and that&#39;s if you need to use any properties of the environment object as bindings for other controls.&lt;/p&gt;
&lt;p&gt;I added a &lt;code&gt;Toggle&lt;/code&gt; to &lt;code&gt;GrandChildView&lt;/code&gt; and wanted to connect it to the same &lt;code&gt;userSettings.isLoggedIn&lt;/code&gt; Boolean. A toggle takes a binding, so normally, I&#39;d do this by adding the &lt;code&gt;$&lt;/code&gt; prefix to the property to bind.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;@Environment&lt;/code&gt;, this gives an error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/env_binding.png&quot; alt=&quot;Binding error&quot;&gt;&lt;/p&gt;
&lt;p&gt;The solution is to create an intermediary &lt;code&gt;@Bindable&lt;/code&gt; property from the &lt;code&gt;@Environment&lt;/code&gt; property and bind it to the toggle (or any control that needs a binding).&lt;/p&gt;
&lt;p&gt;Stripping out a lot of what was there, the code becomes:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GrandChildView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; userSettings

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Bindable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; userSettingsBindable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userSettings

    &lt;span class=&quot;token class-name&quot;&gt;Toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isOn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $userSettingsBindable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isLoggedIn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Log In Status&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You declare the &lt;code&gt;@Environment&lt;/code&gt; property as usual, then &lt;strong&gt;inside&lt;/strong&gt; the &lt;code&gt;body&lt;/code&gt;, declare an &lt;code&gt;@Bindable&lt;/code&gt; property with the value from the &lt;code&gt;@Environment&lt;/code&gt; property. You have to do this inside the body because doing it outside gives an error.&lt;/p&gt;
&lt;p&gt;Surprisingly, I didn&#39;t have to add a &lt;code&gt;return&lt;/code&gt; before the Toggle, even though it was no longer the only statement in the body.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://iosdev.space/@StewartLynch&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Stewart Lynch&lt;/a&gt; for suggesting I add this. Hopefully it will be fixed soon but until then, this is the workaround.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;wrapping-up&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;There are two issues I want to discuss before ending.&lt;/p&gt;
&lt;p&gt;First is the use of &lt;code&gt;private&lt;/code&gt; for view properties. Apple recommends marking every &lt;code&gt;@State&lt;/code&gt; property as &lt;code&gt;private&lt;/code&gt;. It seems a bit strange as you then pass them around and sometimes the subviews change the data, but it means that no object can edit the value unless the view has expressly given permission. I presume there are security and performance benefits to this.&lt;/p&gt;
&lt;p&gt;Similarly, &lt;code&gt;@Enviroment&lt;/code&gt; properties can (and I presume, should) be private. The two binding property wrappers do not allow their properties to be private.&lt;/p&gt;
&lt;p&gt;And lastly, in &lt;code&gt;@Observable&lt;/code&gt; classes, everything that is &lt;strong&gt;NOT&lt;/strong&gt; private is published. This is the opposite to what we had before where you had to explicItly state which properties were published. So mark properties as &lt;code&gt;private&lt;/code&gt; if they should not be published.&lt;/p&gt;
&lt;p&gt;The other issue is to do with using &lt;code&gt;var&lt;/code&gt; on its own i.e. without any property wrapper. The Apple flow chart suggests doing this if you get to the end of the flow without making any other decision, but I think you should use &lt;code&gt;let&lt;/code&gt; in this case. The only time I use a plain &lt;code&gt;var&lt;/code&gt; in a view is for computed properties, otherwise I use &lt;code&gt;let&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In my flow chart, I left the appropriate end point showing &lt;strong&gt;var or let&lt;/strong&gt;, to better match Apple&#39;s, but I recommend using &lt;code&gt;let&lt;/code&gt; and only changing to &lt;code&gt;var&lt;/code&gt; if Xcode complains. This is a good rule for any Swift code, not just SwiftUI.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This article may get several updates before we exit this years beta cycle, but I thought other people might find it useful. Writing it certainly helped clarify the new options in my head.&lt;/p&gt;
&lt;p&gt;If you have any suggestions, ideas or corrections, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, I&#39;d love you to &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2023/swiftui-data-flow-2023/#top&quot;&gt;Back to Top&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>macOS Apprentice</title>
    <link href="https://troz.net/post/2023/macos_book_2/" />
    <updated>2023-06-28T23:48:37Z</updated>
    <id>https://troz.net/post/2023/macos_book_2/</id>
    <content type="html">&lt;p&gt;I&#39;ve written another book! This one is for people who want to get started in macOS development and here it is: &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/v1.0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS Apprentice&lt;/a&gt;, published by &lt;a href=&quot;https://www.kodeco.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;kodeco.com&lt;/a&gt; (formerly raywenderlich.com).&lt;/p&gt;
&lt;p&gt;If you’re a beginner or near-beginner who wants to start learning Swift, SwiftUI and AppKit for building Mac apps, then this is the book for you.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/book_cover_2.png&quot; alt=&quot;Book Cover&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Contents&lt;/h3&gt;
&lt;p&gt;The book consists of four sections:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 1&lt;/strong&gt;: Install Xcode and learn the basics of programming in Swift. Experiment with several different ways to execute Swift code on your Mac.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 2&lt;/strong&gt;: Use SwiftUI to develop a word-guessing game called Snowman. Learn about data flow in SwiftUI, managing multiple windows, using charts and adding macOS-specific features such as toolbars and menus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 3&lt;/strong&gt;: There are still a number of tasks where AppKit works better than SwiftUI. In this section, build an AppKit app to browse movie data from IMDb, the online movie database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 4&lt;/strong&gt;: Add AppKit to your SwiftUI app and add SwiftUI to your AppKit app in order to add some finishing touches to both of the apps from the previous sections.&lt;/p&gt;
&lt;p&gt;You can read more details of the book contents in the &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/v1.0/chapters/v-introduction&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Introduction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are a more experienced iOS developer who wants to branch out into macOS development, then my previous book - &lt;a href=&quot;https://www.kodeco.com/books/macos-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS by Tutorials&lt;/a&gt; - might be a better fit, although I think you could still learn a lot of Mac app tips and tricks from this one.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Where can you read the book?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You can read it online at &lt;a href=&quot;https://www.kodeco.com/books/macos-apprentice/v1.0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;kodeco.com&lt;/a&gt; as part of a Kodeco subscription.&lt;/li&gt;
&lt;li&gt;You can download an ebook (ePub or pdf) at the same page if you have a subscription or buy one there if you don&#39;t.&lt;/li&gt;
&lt;li&gt;The paperback is available at &lt;a href=&quot;https://www.amazon.com/dp/1950325865?&amp;amp;linkCode=sl1&amp;amp;tag=kodeco-20&amp;amp;linkId=66080845dc88e8300c02194f4516961a&amp;amp;language=en_US&amp;amp;ref_=as_li_ss_tl&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Amazon&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All the code and extra materials for the book can be downloaded or cloned from &lt;a href=&quot;https://github.com/kodecocodes/maca-materials/tree/editions/1.0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For support, to ask questions or to report any errors, please go to the &lt;a href=&quot;https://forums.kodeco.com/c/books/macos-apprentice/107&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;forum&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Thanks to...&lt;/h3&gt;
&lt;p&gt;This book was made possible by an awesome team. There were a lot of people involved, but I want to give special thanks to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kodeco.com/u/rcritz&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Richard Critz&lt;/a&gt;, the wonderful editor who had the unenviable task of fixing my grammar.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kodeco.com/u/audrey&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Audrey Tam&lt;/a&gt; and &lt;a href=&quot;https://www.kodeco.com/u/ehabamer&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ehab Amer&lt;/a&gt; were the amazing tech editors who had to make sure that it all worked. Audrey also contributed the first chapter on installing and setting up Xcode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole team at &lt;a href=&quot;https://www.kodeco.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco&lt;/a&gt; was great, so a huge thanks to them all. I hope they enjoyed working on the book as much as I did! It&#39;s wonderful to have such a supportive group working to make my content as good as it can be.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;I would love to hear from anyone who read the book, loved it, hated it, found an error or just wanted to say hello.&lt;/p&gt;
&lt;p&gt;You can contact me directly using any of the contact links at the bottom of this page or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Sparkle</title>
    <link href="https://troz.net/post/2023/sparkle/" />
    <updated>2023-04-11T01:48:38Z</updated>
    <id>https://troz.net/post/2023/sparkle/</id>
    <content type="html">&lt;p&gt;Last month, I posted about writing my new todo app called &lt;a href=&quot;https://troz.net/post/2023/to-day/&quot;&gt;To-Day&lt;/a&gt;: why I wrote it and how I wrote it. At the time, I mentioned that I had set it up to install updates using &lt;a href=&quot;https://sparkle-project.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sparkle&lt;/a&gt; but there was too much detail to include in the initial post. So here, as promised, is the article about implementing Sparkle.&lt;/p&gt;

&lt;p&gt;One of the great conveniences of App Store distribution is the update handling. When (or if) an update passes the app review process, Apple does the rest, propagating updates through the App Store app. If you&#39;re distributing your apps externally, you need an alternative method.&lt;/p&gt;
&lt;p&gt;Sparkle is an open-source update framework for macOS. It&#39;s very popular and I&#39;m sure you will have seen it used, even if you didn&#39;t recognise it. But setting it up isn&#39;t totally straight-forward, at least I didn&#39;t find it so, which I why I&#39;m using this post to document the process.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://sparkle-project.org/documentation/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sparkle documentation&lt;/a&gt; is excellent and contains everything you need to know, but they allow for a wide range of use cases and configurations, which makes it difficult to follow at times. For this app, I have the following requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SwiftUI app&lt;/li&gt;
&lt;li&gt;Sandboxed&lt;/li&gt;
&lt;li&gt;Install framework using Swift Package Manager&lt;/li&gt;
&lt;li&gt;Distribution as a disk image&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide deals with those requirements only.&lt;/p&gt;
&lt;h3&gt;Table of Contents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#installation&quot;&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#adding-a-public-key-to-your-app&quot;&gt;Adding a Public Key to your App&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#checking-for-a-existing-key&quot;&gt;Checking for a Existing Key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#creating-a-new-key&quot;&gt;Creating a New Key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#installing-the-public-key&quot;&gt;Installing the Public Key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#sandboxing&quot;&gt;Sandboxing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#locating-the-updates&quot;&gt;Locating the Updates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#coding-the-update-check&quot;&gt;Coding the Update Check&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#exporting-the-app&quot;&gt;Exporting the App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#making-a-disk-image&quot;&gt;Making a Disk Image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#generating-the-appcast-xml&quot;&gt;Generating the appcast.xml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#testing&quot;&gt;Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2023/sparkle/#summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a id=&quot;installation&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Open your app in Xcode and select the project at the top of the project navigator. Click on the project itself in the next sidebar and then choose &lt;strong&gt;Package Dependencies&lt;/strong&gt; from the tabs across the top.&lt;/p&gt;
&lt;p&gt;Click the &lt;strong&gt;+&lt;/strong&gt; at the bottom of the list to add a new package. Enter this URL into the search field and when the package appears, click &lt;strong&gt;Add Package&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://github.com/sparkle-project/Sparkle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/sparkle_install.png&quot; alt=&quot;Install Sparkle&quot;&gt;&lt;/p&gt;
&lt;p&gt;Xcode will download the package and then show another dialog with the Sparkle library checked. Click &lt;strong&gt;Add Package&lt;/strong&gt; again to attach it to your project.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;adding-a-public-key-to-your-app&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Adding a Public Key to your App&lt;/h3&gt;
&lt;p&gt;The next step depends on whether you&#39;ve used Sparkle before. You need to create a private and public key pair for security reasons, but you can then use the same keys for all of your apps (I think).&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;checking-for-a-existing-key&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Checking for a Existing Key&lt;/h4&gt;
&lt;p&gt;To check if you already have a key, open the &lt;strong&gt;Keychain Access&lt;/strong&gt; app from Applications/Utilities. Search for &lt;strong&gt;sparkle&lt;/strong&gt; and if you have a key, you&#39;ll see it listed. If you don&#39;t find anything, skip ahead to the &lt;a href=&quot;https://troz.net/post/2023/sparkle/#creating-a-new-key&quot;&gt;next section&lt;/a&gt; to create a new one.&lt;/p&gt;
&lt;p&gt;Double-click the key to show its details which conveniently includes the public key in the comments.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/sparkle_key.png&quot; alt=&quot;Existing Sparkle keys&quot;&gt;&lt;/p&gt;
&lt;p&gt;Select and copy the public key, then jump to &lt;a href=&quot;https://troz.net/post/2023/sparkle/#installing-the-public-key&quot;&gt;Installing the Public Key&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;creating-a-new-key&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Creating a New Key&lt;/h4&gt;
&lt;p&gt;If you don&#39;t already have a key pair, you&#39;ll use one of Sparkle&#39;s tools to create it.&lt;/p&gt;
&lt;p&gt;Back in Xcode, right-click on Sparkle in the project navigator and select &lt;strong&gt;Show in Finder&lt;/strong&gt; which opens a folder buried deep in your Library. Press &lt;strong&gt;Command-3&lt;/strong&gt; to show the folder in Columns mode and then click the &lt;strong&gt;artifacts&lt;/strong&gt; folder that&#39;s one level above the Sparkle folder.&lt;/p&gt;
&lt;p&gt;From there, navigate through &lt;strong&gt;sparkle&lt;/strong&gt; to &lt;strong&gt;Sparkle&lt;/strong&gt; where you&#39;ll see a &lt;strong&gt;bin&lt;/strong&gt; folder:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/sparkle_folder.png&quot; alt=&quot;Opening the Sparkle folder&quot;&gt;&lt;/p&gt;
&lt;p&gt;Open your preferred Terminal app and type &lt;strong&gt;cd&lt;/strong&gt; followed by a &lt;strong&gt;space&lt;/strong&gt;. Drag in the &lt;strong&gt;Sparkle&lt;/strong&gt; folder with the uppercase &lt;strong&gt;S&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Then enter and run this command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./bin/generate_keys&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This generates the keys, saves them to your keychain and displays the public key. Copy the public key for use in the next section.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;installing-the-public-key&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Installing the Public Key&lt;/h4&gt;
&lt;p&gt;To insert the key into your app, go back to Xcode and select the project and target. Choose &lt;strong&gt;Info&lt;/strong&gt; from the tabs at the top. Click the &lt;strong&gt;+&lt;/strong&gt; blob that appears beside the last entry when you mouse over it, and type in the key name:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SUPublicEDKey
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The type is String (which should be the default) and the value is the public key you just copied:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/sparkle_info_key.png&quot; alt=&quot;Adding the Sparkle public key&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;sandboxing&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sandboxing&lt;/h3&gt;
&lt;p&gt;Mac apps are sandboxed by default, which quarantines their data into their own container and protects the rest of your system. Adding Sparkle to a sandboxed app requires some more steps, but if your app is not sandboxed, &lt;a href=&quot;https://troz.net/post/2023/sparkle/#locating-the-updates&quot;&gt;move on to the next section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, you need to give your app access to the internet, so it can retrieve the update information and download any updates.&lt;/p&gt;
&lt;p&gt;In the app&#39;s target settings, select &lt;strong&gt;Signing and Capabilities&lt;/strong&gt; and turn on &lt;strong&gt;Outgoing Connections (Client)&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/sparkle_connections.png&quot; alt=&quot;Sand-box connection setting&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, add another setting to the target&#39;s Info:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Key&lt;/strong&gt;: SUEnableInstallerLauncherService&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type&lt;/strong&gt;: Boolean&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Value&lt;/strong&gt;: YES&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next settings go into the &lt;strong&gt;&amp;lt;Your-App-Name&amp;gt;.entitlements&lt;/strong&gt; file which you&#39;ll find in the project navigator. Right-click the entitlements file and select &lt;strong&gt;Open As &amp;gt; Source Code&lt;/strong&gt; which makes it possible to paste in the next chunk.&lt;/p&gt;
&lt;p&gt;Just before the last &lt;code&gt;&amp;lt;/dict&amp;gt;&lt;/code&gt;, add a new line and insert:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;com.apple.security.temporary-exception.mach-lookup.global-name&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;$(PRODUCT_BUNDLE_IDENTIFIER)-spks&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;$(PRODUCT_BUNDLE_IDENTIFIER)-spki&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the app is configured to allow Sparkle to work with the Mac sandbox.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;locating-the-updates&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Locating the Updates&lt;/h3&gt;
&lt;p&gt;You need to have an online location for the app and its update information. Since my To-Day app is on GitHub, I decided to use GitHub for distribution too, but I could have used this web site, AWS or any other online service. It&#39;s best if it&#39;s coming from an &lt;strong&gt;https&lt;/strong&gt; server.&lt;/p&gt;
&lt;p&gt;Open my &lt;a href=&quot;https://github.com/trozware/To-Day&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub repo&lt;/a&gt; and look at the file structure. The &lt;strong&gt;Releases&lt;/strong&gt; folder has a disk image (dmg) containing the app and an XML file generated by Sparkle with the update information.&lt;/p&gt;
&lt;p&gt;Work out where you&#39;re going to put these two files for your app and find the exact URL for the &lt;strong&gt;appcast.xml&lt;/strong&gt; file.&lt;/p&gt;
&lt;p&gt;This gives you the last piece of data that your app needs. Add another setting to your target&#39;s Info:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Key&lt;/strong&gt;: SUFeedURL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type&lt;/strong&gt;: String&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Value&lt;/strong&gt;: https://url-to-your-appcast.xml&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#39;re using GitHub, upload a fake file so you can get the URL, remembering to get the URL for the raw file, not its GitHub page.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;coding-the-update-check&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Coding the Update Check&lt;/h3&gt;
&lt;p&gt;You&#39;ve done all the setup work now, but there&#39;s nothing to trigger a check in the app. I copied and pasted most of the suggested code from the &lt;a href=&quot;https://sparkle-project.org/documentation/programmatic-setup/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sparkle docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, I created a new file called &lt;strong&gt;Updater.swift&lt;/strong&gt; which contains:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sparkle&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This view model class publishes when new updates can be checked by the user&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CheckForUpdatesViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; canCheckForUpdates &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updater&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SPUUpdater&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    updater&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canCheckForUpdates&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;$canCheckForUpdates&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This is the view for the Check for Updates menu item&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Note this intermediate view is necessary for the disabled state on the menu item to work properly before Monterey.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more info&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CheckForUpdatesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; checkForUpdatesViewModel&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CheckForUpdatesViewModel&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; updater&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SPUUpdater&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updater&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SPUUpdater&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;updater &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; updater

    &lt;span class=&quot;token comment&quot;&gt;// Create our view model for our CheckForUpdatesView&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;checkForUpdatesViewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CheckForUpdatesViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updater&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; updater&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Check for Updates…&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; updater&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;checkForUpdates&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;checkForUpdatesViewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canCheckForUpdates&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The comments are from the Sparkle team.&lt;/p&gt;
&lt;p&gt;In my To-Day app, I have an &lt;code&gt;AppGroup&lt;/code&gt; view to provide the SwiftUI view for the last section of the menu that controls the app itself.&lt;/p&gt;
&lt;p&gt;Inside this view, I added an &lt;code&gt;updaterController&lt;/code&gt; property:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; updaterController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SPUStandardUpdaterController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  startingUpdater&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  updaterDelegate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  userDriverDelegate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This required another &lt;code&gt;import Sparkle&lt;/code&gt; line at the top of this file.&lt;/p&gt;
&lt;p&gt;To trigger the update checker, I added &lt;code&gt;CheckForUpdatesView&lt;/code&gt; as one of the views in the menu, providing it with this controller&#39;s updater:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;CheckForUpdatesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updater&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; updaterController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;updater&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a id=&quot;exporting-the-app&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Exporting the App&lt;/h3&gt;
&lt;p&gt;Now that the app is coded, you need to export it from Xcode, signing it with your Developer ID and getting it notarised by Apple. This assumes that you have an Apple Developer account.&lt;/p&gt;
&lt;p&gt;In Xcode, select &lt;strong&gt;Archive&lt;/strong&gt; from the &lt;strong&gt;Product&lt;/strong&gt; menu. This builds the app and opens the Organiser window. Click &lt;strong&gt;Distribute App&lt;/strong&gt;, check &lt;strong&gt;Developer ID&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Make sure &lt;strong&gt;Upload&lt;/strong&gt; is selected and click &lt;strong&gt;Next&lt;/strong&gt; again. Choose &lt;strong&gt;Automatically manage signing&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt; again. When the next dialog appears, click &lt;strong&gt;Upload&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This sends the app off to Apple&#39;s servers to be checked for malicious code. It&#39;s not doing any app review, just checking for viruses etc.&lt;/p&gt;
&lt;p&gt;It may take a few minutes, but the app should then be notarised. You may have to go to another display in the Organiser and back again to force a display refresh so you can see this. If there is an error, click &lt;strong&gt;Show Status Log&lt;/strong&gt; to see what went wrong.&lt;/p&gt;
&lt;p&gt;Assuming all went well, click &lt;strong&gt;Export Notarised App&lt;/strong&gt; and save it somewhere convenient.&lt;/p&gt;
&lt;p&gt;If you don&#39;t have an Apple Developer account, select &lt;strong&gt;Copy App&lt;/strong&gt; from the distribution dialog and save a folder containing the app. If a user has the default Gatekeeper settings on their Mac, they&#39;ll need to right-click the app and select &lt;strong&gt;Open&lt;/strong&gt; to run it, after getting past a couple of warnings.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;making-a-disk-image&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Making a Disk Image&lt;/h3&gt;
&lt;p&gt;The next step is to create the disk image for distribution. You can use a zip file or a disk image, but I prefer a disk image because it gives you a way to guide users to install the app in their Applications folder.&lt;/p&gt;
&lt;p&gt;The process for this is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a writeable disk image.&lt;/li&gt;
&lt;li&gt;Add the app and a link to Applications.&lt;/li&gt;
&lt;li&gt;Set the view options, including a background image.&lt;/li&gt;
&lt;li&gt;Convert the image into a read-only copy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Open &lt;strong&gt;Disk Utility&lt;/strong&gt; from Applications/Utilities. Select &lt;strong&gt;New Image &amp;gt; Blank Image…&lt;/strong&gt; from the &lt;strong&gt;File&lt;/strong&gt; menu. Set its name in two places and its size. The size should be about 2 x the size of your app to allow for future updates.&lt;/p&gt;
&lt;p&gt;Double-click the image to open it and drag your exported app into the disk image window. &lt;strong&gt;Command-Option-drag&lt;/strong&gt; your Applications folder in. You&#39;ll know you&#39;re holding down the right modifiers if you see a curved black arrow at the bottom left of the icon. This adds an alias to the current Applications folder, rather than a copy of yours.&lt;/p&gt;
&lt;p&gt;Now configure the disk image window using Finder&#39;s View menu and View Options. I turn off all the extra views: toolbar, path bar, status bar etc. then I set the view options like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/sparkle_dmg.png&quot; alt=&quot;Configuring the disk image&quot;&gt;&lt;/p&gt;
&lt;p&gt;Setting a background image takes a few steps. First, find an image you like. Not being graphically minded, I used a rectangle filled with a gradient and added a curved arrow that pointed from the app to the Applications folder. My file is a png, but I think a jpg will work fine. You can click this link if you want to see my sample &lt;a href=&quot;https://troz.net/images/2023/dmg_background.png&quot;&gt;background image&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Drag the image file into your disk image. Don&#39;t worry about positioning it - you&#39;re about to make the file invisible. With Finder active, press &lt;strong&gt;Shift-Command-.&lt;/strong&gt; to show invisible files. Now, rename the background image file to &lt;strong&gt;.background.png&lt;/strong&gt;. The leading period makes this an invisible file, but you can see it at the moment. Open the View Options again and select &lt;strong&gt;Picture&lt;/strong&gt; for the Background. Drag your image file into the well. This may show that you need to re-position the arrow, so repeat the process until you get an image that looks right. Then press &lt;strong&gt;Shift-Command-.&lt;/strong&gt; again to hide invisible files. Re-position the visible icons to suit and adjust the size of the window to what you want to show.&lt;/p&gt;
&lt;p&gt;Now that the disk image is configured, eject it. Back in Disk Utility, select &lt;strong&gt;Images &amp;gt; Convert…&lt;/strong&gt; and choose your dmg. On the next dialog, change Image Format to &lt;strong&gt;read-only&lt;/strong&gt; and click &lt;strong&gt;Convert&lt;/strong&gt;. This is the disk image that goes into your Releases folder, so change its name to match your app and move it. Keep the read-write disk image too - it&#39;s much easier to edit it when updating instead of going through this process every time.&lt;/p&gt;
&lt;p&gt;Open the read-only image and check that it&#39;s set up the way your want. I have found that sometimes the background image doesn&#39;t stick. If this happens to you, eject and trash the read-only image. Mount the read-write image again, confirm its settings and re-convert.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;generating-the-appcast-xml&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Generating the appcast.xml&lt;/h3&gt;
&lt;p&gt;Now to create the file that Sparkle uses to see if there is an update.&lt;/p&gt;
&lt;p&gt;Use the method from when you generated a key to open a Terminal at the Sparkle directory. Type in:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./bin/generate_appcast /path/to/your/Releases/folder&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you&#39;re typed the command and a space, you can drag your Releases folder in to get its path.
Press Return and wait while Sparkle generates the &lt;strong&gt;appcast.xml&lt;/strong&gt; file.&lt;/p&gt;
&lt;p&gt;And finally, you&#39;re ready to release. Upload your Releases folder to wherever you decided to put it, making sure that the URL for the appcast.xml file is the same as in your app&#39;s info.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;testing&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Testing&lt;/h3&gt;
&lt;p&gt;Move the notarised app into your applications folder and run it. If all has worked, you should be able to check for updates and see that you have the latest version.&lt;/p&gt;
&lt;p&gt;If you get an error, check back through all the setup phases. I know I made every mistake possible when setting it up first, so check the sandbox setting, the info and the entitlements. Use a web browser to confirm that the appcast.xml file is where you said it would be.&lt;/p&gt;
&lt;p&gt;Testing an update requires a new version, so make some small, visible change to your app. Increment the version and build numbers, then archive, notarise and export as before. Find the read-write disk image and mount it. Replace the app there with your new version and then eject the disk image. Create a new read-only disk image and replace the copy in your Releases folder with it.&lt;/p&gt;
&lt;p&gt;Run the &lt;code&gt;./bin/generate_appcast&lt;/code&gt; again and it will add a new entry to your appcast.xml file with the new version data.&lt;/p&gt;
&lt;p&gt;If you want to add some release notes, open &lt;strong&gt;appcast.xml&lt;/strong&gt; in a text editor. The top entry is the latest. You can include a description tag and if you use the &lt;code&gt;&amp;lt;![CDATA[ ... ]]&amp;gt;&lt;/code&gt; wrapper, you can add HTML:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/sparkle_release_notes.png&quot; alt=&quot;Adding release notes&quot;&gt;&lt;/p&gt;
&lt;p&gt;Upload the Releases folder as before and then run your first version of the app. Check for updates and see what happens.&lt;/p&gt;
&lt;p&gt;If it doesn&#39;t work, check the Console app and see if you can see any errors there that might point you in the right direction.&lt;/p&gt;
&lt;p&gt;This can be a frustrating process, but once you have it all set up correctly, it works very well. I added a ReadMe to my project listing the steps I need to follow when publishing an update, so that I don&#39;t have to remember them every time. I recommend this or something similar.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;summary&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;Sparkle is a great tool and once you have everything configured, it works really well. Configuring it can be tricky, especially as their docs cover so many different use cases.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/trozware/To-Day&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub repo&lt;/a&gt; for my To-Day app to see how I&#39;ve configured it.&lt;/p&gt;
&lt;p&gt;If you have any suggestions or if you run into any problems following this guide, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, I&#39;d love you to &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>To-Day</title>
    <link href="https://troz.net/post/2023/to-day/" />
    <updated>2023-03-08T04:58:01Z</updated>
    <id>https://troz.net/post/2023/to-day/</id>
    <content type="html">&lt;p&gt;When I sit down at my computer at the start of a work day, I typically like to plan out the broad outline of my work. This is the three or four major tasks for my day. I don&#39;t want a minute-by-minute plan, just a list of the major items, so that I can check them off as I work, and get to the end of the day without feeling like I&#39;ve forgotten anything.&lt;/p&gt;
&lt;p&gt;There are a ton of todo apps and web services available, but they all provide much more than I need. When I factored in how I wanted to learn about writing a Mac menubar app in SwiftUI, the solution was obvious - write my own.&lt;/p&gt;

&lt;p&gt;First things first: the project is open-source and on &lt;a href=&quot;https://github.com/trozware/To-Day&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; so feel free to download it, fork it, edit it, create pull requests, file issues or whatever you like. If you just want to install the app, you can download the &lt;a href=&quot;https://github.com/trozware/To-Day/raw/main/Releases/To-Day.dmg&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;disk image&lt;/a&gt;. It requires macOS 13.0 or later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/today1.png&quot; alt=&quot;Running the app for the first time&quot;&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m not going to go through the entire app in detail. You can download the project and check out the code to see it all. I&#39;m just going to give some details about the app structure and architecture as well as mentioning some code that I found new or interesting.&lt;/p&gt;
&lt;h2&gt;The App&lt;/h2&gt;
&lt;p&gt;I&#39;ve written menu bar apps using AppKit but this is my first serious look at SwiftUI&#39;s &lt;code&gt;MenuBarExtra&lt;/code&gt;. Configuring the app to be a menu bar app has three steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Remove the &lt;code&gt;WindowGroup&lt;/code&gt; from your app &lt;code&gt;body&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;MenuBarExtra&lt;/code&gt; to your app &lt;code&gt;body&lt;/code&gt; with some views to show in the menu.&lt;/li&gt;
&lt;li&gt;Insert an entry into &lt;strong&gt;Target - Info&lt;/strong&gt;, setting &lt;code&gt;Application is agent (UIElement)&lt;/code&gt; to &lt;code&gt;YES&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/today_info.png&quot; alt=&quot;Editing the app info&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can then delete &lt;strong&gt;ContentView.swift&lt;/strong&gt; from the project since nothing uses it.&lt;/p&gt;
&lt;p&gt;Looking at the first screenshot, you can see that I&#39;ve split the menu into four sections. The first one displays the actual todos and handles checking them off. This view is provided by the main data object.&lt;/p&gt;
&lt;p&gt;The other sections are static and deal with editing the todos, configuring the app settings and managing the app itself. Each of these is split off into a separate view file to keep the main app body as uncluttered as possible. The files for these three sections are in the &lt;strong&gt;Menu Views&lt;/strong&gt; group in the Xcode project.&lt;/p&gt;
&lt;p&gt;Editing the todos caused the most issues with the app and took several iterations, but I&#39;m pleased with this version:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/today_edits.png&quot; alt=&quot;Editing the todos&quot;&gt;&lt;/p&gt;
&lt;p&gt;I tried lots of ways of tracking the focus and adding keyboard commands to move items up and down the list but in the end, I went for simplicity. The editing is embedded in a &lt;code&gt;Form&lt;/code&gt;. This means that I get the standard tabbing behaviour by default, but I also get the small toggle switches, which I really like.&lt;/p&gt;
&lt;p&gt;The main view for this is &lt;code&gt;EditView&lt;/code&gt; and it, with its subviews, are in the &lt;strong&gt;Edit Views&lt;/strong&gt; project group.&lt;/p&gt;
&lt;p&gt;I&#39;ll discuss the &lt;strong&gt;Models&lt;/strong&gt; group and the data flow in a minute, but to finish describing the app structure, the &lt;code&gt;AboutView&lt;/code&gt; tells you more about the app. I love being able to use basic Markdown styling in &lt;code&gt;Text&lt;/code&gt; views. And I learnt a good lesson: if you&#39;re having trouble with truncated text, check out the &lt;code&gt;fixedSize&lt;/code&gt; modifier.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;Updater&lt;/code&gt; is a class to handle using the &lt;strong&gt;&lt;a href=&quot;https://sparkle-project.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sparkle&lt;/a&gt;&lt;/strong&gt; updater, which deserves its own post. I must have made every possible mistake when implementing this, so I think a guide would be useful, for future me if for nobody else.&lt;/p&gt;
&lt;h2&gt;The Data&lt;/h2&gt;
&lt;p&gt;I like to use the &lt;a href=&quot;https://swiftuivoodo.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;VOODO&lt;/a&gt; pattern, which stands for &lt;strong&gt;View - ObservableObject - DataObject&lt;/strong&gt;. Working backwards through this, the &lt;strong&gt;DataObject&lt;/strong&gt; is a struct that contains the properties for a single &lt;code&gt;Todo&lt;/code&gt; item. The &lt;strong&gt;ObservableObject&lt;/strong&gt; is a class called &lt;code&gt;AppState&lt;/code&gt; that contains a published array of todo items as well as a &lt;code&gt;DataStore&lt;/code&gt; struct for saving and loading. The &lt;strong&gt;View&lt;/strong&gt; is the menu itself.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AppState&lt;/code&gt; also contains a bunch of computed properties to make life easier and methods for adding, deleting and checking. It also has a computed property to generate the view for the todos at the top of the menu.&lt;/p&gt;
&lt;p&gt;When you have an &lt;code&gt;ObservableObject&lt;/code&gt;, you can pass it around the app in two ways: as an &lt;code&gt;ObservedObject&lt;/code&gt; or as an &lt;code&gt;EnvironmentObject&lt;/code&gt;. Both work fine, but with the ObservedObject, there has to be an unbroken chain of data passing from view to subview to sub-subview and so on. EnvironmentObject makes it easier because once you insert it into a view hierarchy, any view in that hierarchy can access it.&lt;/p&gt;
&lt;p&gt;It might seem like EnvironmentObject is less efficient, but I&#39;ve read recent reports of it using less memory, perhaps because intermediate views that don&#39;t need it, don&#39;t access it.&lt;/p&gt;
&lt;p&gt;I decided to use EnvironmentObject so in the App struct, I initialised it as a &lt;code&gt;StateObject&lt;/code&gt;. MenuBarExtra can&#39;t take an &lt;code&gt;environmentObject&lt;/code&gt;, so I grouped the menu view sections and assigned the environmentObject to the Group. This let the views in the menu access it using &lt;code&gt;@EnvironmentObject var appState: AppState&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Windows&lt;/h2&gt;
&lt;p&gt;Even though this is a menu bar app, I wanted to be able to open two windows: one for editing the todos and one for showing some info about the app.&lt;/p&gt;
&lt;p&gt;For each of these, I added a &lt;code&gt;Window&lt;/code&gt; scene to the app. A &lt;code&gt;Window&lt;/code&gt; scene differs from a &lt;code&gt;WindowGroup&lt;/code&gt; in that it only opens one copy of the window. If you try to to open it a second time, it activates the original rather than opening another.&lt;/p&gt;
&lt;p&gt;Back of these windows has a &lt;code&gt;title&lt;/code&gt; and an &lt;code&gt;id&lt;/code&gt;. The &lt;code&gt;id&lt;/code&gt; is used by the &lt;code&gt;openWindow&lt;/code&gt; &lt;code&gt;Environment&lt;/code&gt; value to show the window and the title is the window title. The contents of the scene is the view that appears in the window.&lt;/p&gt;
&lt;p&gt;The Edit window also gets the &lt;code&gt;EnvironmentObject&lt;/code&gt; so it can access the data. Both have a &lt;code&gt;defaultSize&lt;/code&gt; - this only applies to the first opening - after that, the app uses whatever you set. The About window also gets a &lt;code&gt;defaultPosition&lt;/code&gt; (which I am not sure works) and a &lt;code&gt;windowResizability&lt;/code&gt; modifier to make sure that the window can&#39;t be bigger than the size needed to show the content.&lt;/p&gt;
&lt;h2&gt;Launch on Login&lt;/h2&gt;
&lt;p&gt;Getting an app to launch when the user logins in used to be a lot of work. Thankfully, we now have access to &lt;code&gt;SMAppService&lt;/code&gt; which makes it much easier. And that&#39;s why the app is only for macOS 13 or higher.&lt;/p&gt;
&lt;p&gt;To tell whether an app is set to launch on login, import the &lt;code&gt;ServiceManagement&lt;/code&gt; library and run this code:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;SMAppService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mainApp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;enabled&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And to change the setting, use:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SMAppService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mainApp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SMAppService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mainApp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unregister&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The really neat thing about this is that people can now see what apps will launch on login, unlike with the previous system where this data was hidden deep in the Library folder. Go to &lt;strong&gt;System Settings - General - Login Items&lt;/strong&gt; to check it out.&lt;/p&gt;
&lt;h2&gt;Saving Changes&lt;/h2&gt;
&lt;p&gt;I wanted to save the todos whenever the user made a change, but with a debounce so it didn&#39;t save after every keystroke.&lt;/p&gt;
&lt;p&gt;After a bit of research, this is what I used:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; saveTask&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatchWorkItem&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;debouncedSave&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveTask&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; task &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatchWorkItem&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;qos&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;background&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveTodos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todos&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;todos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveTask &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; task
  &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;asyncAfter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;deadline&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DispatchTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; execute&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; task&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added a &lt;code&gt;didSet&lt;/code&gt; property observer to the &lt;code&gt;todos&lt;/code&gt; array, which calls this function after every change. This cancels any existing task, creates a new one and schedules it for half a second later. If no other changes occur in that half-second, the app saves, using a background queue.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;DataStore&lt;/code&gt; struct saves and loads the data as JSON, storing it in the Documents folder inside the app&#39;s Container.&lt;/p&gt;
&lt;h2&gt;Distribution&lt;/h2&gt;
&lt;p&gt;Typically, I use the App Store to distribute my apps. Apple handles all the business side of things and it makes distributing updates easy.&lt;/p&gt;
&lt;p&gt;But lately, it seems that the App Store review process has become even more capricious, blocking good apps for spurious reasons, while utterly failing in their stated mission of &amp;quot;keeping us safe&amp;quot; from obvious scams, copycat apps and fake reviews.&lt;/p&gt;
&lt;p&gt;So for this app, I have decided not to charge for it and to distribute it myself. I&#39;m using GitHub both as the &lt;a href=&quot;https://github.com/trozware/To-Day&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;open-source repository&lt;/a&gt;, and as the &lt;a href=&quot;https://github.com/trozware/To-Day/raw/main/Releases/To-Day.dmg&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;app distribution source&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you enjoy using the app, I&#39;d love to hear from you. There are contact details at the bottom of the page. And if you really want to contribute, please &lt;a href=&quot;https://ko-fi.com/H2H3BU7SI&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Updating is the missing piece in this puzzle, but that is a sufficiently large topic that I intend to write a follow-up post detailing how I set up &lt;a href=&quot;https://sparkle-project.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sparkle&lt;/a&gt; and eventually got it to work.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So, how does this compare with writing a Mac menu bar app in AppKit? A lot of things are easier in SwiftUI, but one thing I miss is being able to set up a menu delegate to track when the menu is opened or closed. There doesn&#39;t appear to be any way to do this for a &lt;code&gt;MenuBarExtra&lt;/code&gt; - it can&#39;t have an &lt;code&gt;onAppear&lt;/code&gt; or &lt;code&gt;onDisappear&lt;/code&gt; modifier and &lt;code&gt;scenePhase&lt;/code&gt; detects nothing.&lt;/p&gt;
&lt;p&gt;For this app, it&#39;s not really a problem, but I have used other apps where it would be a real showstopper.&lt;/p&gt;
&lt;p&gt;And what&#39;s next for this app? I&#39;ll use it in my daily routine for a while and see what I like, what I hate and what extra features I&#39;d like. One possibility is to add some global shortcuts. This requires hooking into the Accessibility settings, which I am reluctant to do, so I&#39;ll need to think about this.&lt;/p&gt;
&lt;p&gt;If you have any ideas or feature requests, create an issue on GitHub, or fork and edit the project and then create a pull request.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2023/todo_complete.png&quot; alt=&quot;Day complete&quot;&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>New Links</title>
    <link href="https://troz.net/post/2022/new_links/" />
    <updated>2022-12-16T22:47:35Z</updated>
    <id>https://troz.net/post/2022/new_links/</id>
    <content type="html">&lt;p&gt;As we round out the year, there are several new addresses I&#39;d like people to use to contact me or get my book.&lt;/p&gt;

&lt;p&gt;First, raywenderlich.com has re-branded to &lt;a href=&quot;https://www.kodeco.com/books/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco&lt;/a&gt;. Still the same great content created by terrific people, but with a fresh new design that makes it easier to follow the learning path that you prefer.&lt;/p&gt;
&lt;p&gt;If you want to read my macOS by Tutorials book, it&#39;s now at &lt;a href=&quot;https://www.kodeco.com/books/macos-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;kodeco.com/books/macos-by-tutorials&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/book_cover.png&quot; alt=&quot;Book Cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;As we all know, Twitter is in meltdown and I no longer use it. I&#39;d love it if you came over to Mastodon. People on Mastodon are friendly and sharing. It feels much more like a community.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.movetodon.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Movetodon&lt;/a&gt; is a great tool for finding your Twitter friends on Mastodon and &lt;a href=&quot;https://fedi.tips&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Fedi.tips&lt;/a&gt; is a useful site for learning how to use Mastodon effectively.&lt;/p&gt;
&lt;p&gt;My suggestions are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be nice!&lt;/li&gt;
&lt;li&gt;Don&#39;t stress about choosing a server, you can follow anyone from any server and you can transfer your account to a different server if you change your mind.&lt;/li&gt;
&lt;li&gt;Follow hashtags as well as people, and add hashtags to your posts.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Favorite&lt;/strong&gt; to tell people you liked their post and &lt;strong&gt;Boost&lt;/strong&gt; to share with your followers.&lt;/li&gt;
&lt;li&gt;Add hashtags using UpperCamelCase to assist screen readers e.g. #AdventOfCode, not #adventofcode.&lt;/li&gt;
&lt;li&gt;Follow me: &lt;a href=&quot;https://mastodon.social/@troz&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@troz@mastodon.social&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I look forward to seeing you there.&lt;/p&gt;
&lt;p&gt;I&#39;ve added a &lt;a href=&quot;https://mastodon.social/@troz&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt; link to my web footer, but I also created a &lt;a href=&quot;https://linktr.ee/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;LinkTree&lt;/a&gt; to gather all my links into one place.&lt;/p&gt;
&lt;p&gt;And finally, if you want to support my work, please &lt;a href=&quot;https://ko-fi.com/H2H3BU7SI&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac 2022</title>
    <link href="https://troz.net/post/2022/swiftui-mac-2022/" />
    <updated>2022-06-17T04:18:32Z</updated>
    <id>https://troz.net/post/2022/swiftui-mac-2022/</id>
    <content type="html">&lt;p&gt;In December 2019, I wrote a &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-1/&quot;&gt;series of articles about using SwiftUI to build a Mac app&lt;/a&gt;. And in July 2020, I &lt;a href=&quot;https://troz.net/post/2020/swiftui_mac_big_sur/&quot;&gt;re-visited the sample app&lt;/a&gt; to apply the new SwiftUI features made available in macOS BigSur. Now that macOS Ventura and Xcode 14 are in beta, it&#39;s time to build the app again while learning how to incorporate the new APIs.&lt;/p&gt;

&lt;p&gt;Right now, it&#39;s June 2022 and I&#39;m using macOS Ventura 13 beta 2 (22A5286j) with Xcode 14 beta 2 (14A5229c). There will undoubtedly be changes before release, but I&#39;ll try to update this article or add notes to point out any major differences.&lt;/p&gt;
&lt;p&gt;I don&#39;t intend to cover features that haven&#39;t changed much, but here are the new APIs that I am interested in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#navigation&quot;&gt;Navigation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#detecting-the-active-window&quot;&gt;Detecting the Active Window&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#opening-new-windows&quot;&gt;Opening New Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#charts&quot;&gt;Charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#image-rendering&quot;&gt;Image Rendering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#forms&quot;&gt;Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/post/2022/swiftui-mac-2022/#menu-bar-apps&quot;&gt;Menu Bar Apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In Xcode 14 beta 2, the SwiftUI Previews are crashing unless I go to Project &amp;gt; Target &amp;gt; Signing &amp;amp; Capabilities and select my developer team and set Signing Certificate to Development. So if you can&#39;t see the Previews, make these changes, or run the app directly.&lt;/p&gt;
&lt;h3&gt;Navigation&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/trozware/swiftui-mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;first version of this app&lt;/a&gt; used a two pane layout with a sectioned list in the sidebar leading to a detail view in the main area. With the &lt;a href=&quot;https://github.com/trozware/swiftui-mac-11&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Big Sur version&lt;/a&gt;, I switched to a three pane layout because the sections were not displaying neatly. This time, I&#39;ve reverted to two panes with sections. As before, I&#39;m using &lt;a href=&quot;https://http.cat&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;HTTP Cats&lt;/a&gt; which is a site that serves up a cat image to match almost every HTTP status code.&lt;/p&gt;
&lt;p&gt;Previous versions used &lt;code&gt;NavigationLinks&lt;/code&gt; inside a &lt;code&gt;NavigationView&lt;/code&gt;. &lt;code&gt;NavigationView&lt;/code&gt; is now deprecated and we should use either &lt;code&gt;NavigationStackView&lt;/code&gt; or &lt;code&gt;NavigationSplitView&lt;/code&gt;. &lt;code&gt;NavigationStackView&lt;/code&gt; is better suited to a small screen where each view is pushed on to a stack and can be popped off to navigate back. I am going to use &lt;code&gt;NavigationSplitView&lt;/code&gt; which works well for bigger displays with all the panes visible at once.&lt;/p&gt;
&lt;p&gt;My data model is exactly the same as I developed originally and consists of an array of &lt;code&gt;HttpSections&lt;/code&gt;, each containing an array of &lt;code&gt;HttpStatuses&lt;/code&gt;.
So I was able to render my navigation like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;NavigationSplitView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;httpSections&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedStatus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; section &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;section&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headerCode&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;section&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headerText&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;section&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statuses&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; status &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;SidebarRowView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;minWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; detail&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;DetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sectionTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sectionHeader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; httpStatus&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; selectedStatus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;NavigationSplitView&lt;/code&gt; takes various parameters, depending on the result you want. In this case, the first parameter lays out the sidebar, which is a &lt;code&gt;List&lt;/code&gt;. The detail parameter contains the detail view. For the &lt;code&gt;List&lt;/code&gt;, I loop through the sections, adding a section header for each one and a custom &lt;code&gt;SidebarRowView&lt;/code&gt; to display the individual status entries. The &lt;code&gt;List&lt;/code&gt; selection is bound to an optional &lt;code&gt;HttpStatus&lt;/code&gt;. The key here is to give each row a &lt;code&gt;tag&lt;/code&gt; that maps to the status displayed in that row. If you omit the tag, the sidebar draws but none of the entries are selectable.&lt;/p&gt;
&lt;p&gt;Interestingly, when you create a navigation setup like this, Xcode automatically adds a toolbar with a button to toggle the sidebar. This has always been essential as there is a long-standing bug where a hidden sidebar cannot be dragged back into view. But adding it manually meant digging into AppKit to find a method to call.&lt;/p&gt;
&lt;p&gt;Not so interestingly, the sidebar is often hidden on app launch. I tried giving &lt;code&gt;NavigationStackView&lt;/code&gt; its optional &lt;code&gt;columnVisibility&lt;/code&gt; parameter, but no setting made it appear consistently.&lt;/p&gt;
&lt;p&gt;With the detail, right now there is a bug where you cannot unwrap a conditional and show a view based on that. One workaround is to wrap the entire detail in a &lt;code&gt;ZStack&lt;/code&gt;, but in the interest of keeping my &lt;code&gt;ContentView&lt;/code&gt; code simple and as short as possible, I added an intermediary view. &lt;code&gt;DetailView&lt;/code&gt; takes optional parameters and it decides whether to show the &lt;code&gt;StatusView&lt;/code&gt; or a placeholder. Note: the bug may have gone in beta 2, but as I want to display other UI elements in the detail view anyway, this is still a neater approach.&lt;/p&gt;
&lt;p&gt;There is nothing much new in either of these views, except where &lt;code&gt;StatusView&lt;/code&gt; downloads the selected cat image. I tried using &lt;code&gt;AsyncImage&lt;/code&gt; which worked really well except for one problem. When I selected a new status, the old image stayed in place until the new one arrived. I was unable to work out how to clear or reset an &lt;code&gt;AsyncImage&lt;/code&gt; so it would show its placeholder again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Thanks to &lt;a href=&quot;https://twitter.com/ramzesenok&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@ramzesenok&lt;/a&gt; who pointed out that this is another case where I could use an &lt;code&gt;id&lt;/code&gt; to force a refresh. So now, the app uses &lt;code&gt;AsyncImage&lt;/code&gt; which makes for a lot less code.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;AsyncImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; httpStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; img &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;CatImageView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;catImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; img&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; statusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; httpStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; placeholder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;ProgressView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;httpStatus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// this resets the AsyncImage whenever httpStatus changes&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If anyone is still interested in the &lt;code&gt;async/await&lt;/code&gt; version, check out this &lt;a href=&quot;https://github.com/trozware/swiftui-mac-2022/tree/55dc61b57f50379e4a4b0c247014af521d866c5d&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;earlier commit on GitHub&lt;/a&gt;. It does appear to have better/faster caching, but a beta is not the right place to make speed assessments.&lt;/p&gt;
&lt;p&gt;&lt;s&gt;However I did switch to using &lt;code&gt;async/await&lt;/code&gt; to download the image.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;Updating this was also slightly complicated. I downloaded the first selected image using a &lt;code&gt;task&lt;/code&gt; modifier and downloaded subsequent selections using an &lt;code&gt;onChange&lt;/code&gt; modifier. Both of these were required to show the image every time.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;Thanks to &lt;a href=&quot;https://twitter.com/davbeck&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@davbeck&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/malhal&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@malhal&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/chriseidhof&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@chriseidhof&lt;/a&gt; for pointing out that if I gave the &lt;code&gt;task&lt;/code&gt; an &lt;code&gt;id&lt;/code&gt;, it would be called whenever the &lt;code&gt;id&lt;/code&gt; property changed.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;s&gt;The previous image is first set to &lt;code&gt;nil&lt;/code&gt;, so that the progress indicator appears.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;With all this in place, I had the basis of the app and the navigation was complete:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/navigation_2022.jpg&quot; alt=&quot;Navigation&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Detecting the Active Window&lt;/h3&gt;
&lt;p&gt;macOS apps can have multiple windows open at once, and in previous iterations of SwiftUI, it has been difficult to detect the active one. Last year, we got &lt;code&gt;@FocusedBinding&lt;/code&gt; but it didn&#39;t really work. This year it works, but we also have a new &lt;code&gt;EnvironmentValue&lt;/code&gt; called &lt;code&gt;controlActiveState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As I did last time, I set up some menus by adding a &lt;code&gt;commands&lt;/code&gt; modifier to my &lt;code&gt;WindowGroup&lt;/code&gt; in the main _App.swift file. First, I added the pre-built &lt;code&gt;SidebarCommands()&lt;/code&gt; which added a menu item and keyboard shortcut for toggling the sidebar. Next, I added a menu item for flipping the image:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;CommandGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;after&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textEditing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Flip Image&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flipImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyboardShortcut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;f&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This new item appears in the Edit menu, after the standard items. It has a shortcut of Command-F. The Command key is the default shortcut modifier, so if you only specify a letter, it automatically uses Command. All this menu item does is to broadcast a notification.&lt;/p&gt;
&lt;p&gt;The image is shown in a subview called &lt;code&gt;CatImageView&lt;/code&gt; which now includes the following properties:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controlActiveState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; controlActiveState

&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; flipImageMenuItemSelected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flipImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunLoop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imageIsFlipped &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The image has a &lt;code&gt;rotation3Deffect&lt;/code&gt; modifier with the angle set to either 0 degrees, or 180 degrees, depending on the value of &lt;code&gt;imageIsFlipped&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The view subscribes to the &lt;code&gt;flipImage&lt;/code&gt; notification and receives it like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;flipImageMenuItemSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; controlActiveState &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; controlActiveState &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    imageIsFlipped&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the notification arrives, the code checks the value of &lt;code&gt;controlActiveState&lt;/code&gt;. In my tests, this was always either &lt;code&gt;key&lt;/code&gt; or &lt;code&gt;inactive&lt;/code&gt;, but I saw that &lt;code&gt;active&lt;/code&gt; was an option and added it too, in case it becomes relevant later. If the &lt;code&gt;CatImageView&lt;/code&gt; is in the active window, it flips the image and if not, this notification is ignored.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/target_active_window.jpg&quot; alt=&quot;Target Active Window&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see from the text in the images, only the front window image is flipped.&lt;/p&gt;
&lt;h3&gt;Opening New Windows&lt;/h3&gt;
&lt;p&gt;In early versions of SwiftUI, opening a secondary window was a very complex process. Read &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-2/&quot;&gt;part 2 of the original series&lt;/a&gt; to see how I struggled with this. Later, we got an easy way to open a Preferences window, using a &lt;code&gt;Settings&lt;/code&gt; scene. Preferences are now called Settings, which makes this nomenclature more logical.&lt;/p&gt;
&lt;p&gt;Last year, we got the ability to use a &lt;code&gt;NavigationLink&lt;/code&gt; as a SwiftUI menu item and this will open the destination view in a new window.&lt;/p&gt;
&lt;p&gt;This year, we have a new method that uses another new &lt;code&gt;EnvironmentValue&lt;/code&gt; called &lt;code&gt;openWindow&lt;/code&gt;. First, in the _App.swift file, I added another new scene to the body - this time a second &lt;code&gt;WindowGroup&lt;/code&gt;. A &lt;code&gt;WindowGroup&lt;/code&gt; can be passed an &lt;code&gt;id&lt;/code&gt;, a data object of a specific type, or both. In this case, I didn&#39;t want to pass any data, so I gave it an &lt;code&gt;id&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui_samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;SamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I want to display a window with a view demonstrating some UI elements. The view is called &lt;code&gt;SamplesView&lt;/code&gt;, so I set this as the content of the &lt;code&gt;WindowGroup&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To use it, I added a property to &lt;code&gt;DetailView&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;openWindow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; openWindow&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And inserted a &lt;code&gt;Button&lt;/code&gt; to call it:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Show UI Samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;openWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui_samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This opens a new window with the appropriate &lt;code&gt;id&lt;/code&gt;. This can open multiple copies of the same window. If I had passed a data object to the window, it would have brought the window containing that data object to the front instead of opening a new window. To test this behavior, I changed the &lt;code&gt;WindowGroup&lt;/code&gt; to this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;SamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And changed the &lt;code&gt;Button&lt;/code&gt; to:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Show UI Samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;openWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the &lt;code&gt;WindowGroup&lt;/code&gt; expects a &lt;code&gt;String&lt;/code&gt;. When you pass a &lt;code&gt;String&lt;/code&gt; that has already been attached to a window, that window is brought to the front. If you pass a different &lt;code&gt;String&lt;/code&gt;, you get a new window.&lt;/p&gt;
&lt;p&gt;After that, I realized that there was another scene type I could use. If you create a &lt;code&gt;Window&lt;/code&gt; scene instead of a &lt;code&gt;WindowGroup&lt;/code&gt;, not only does this become a single presentation window, but you get a menu item for it in the Window menu without any extra work. You can add a keyboard shortcut to the &lt;code&gt;Window&lt;/code&gt; scene too. Supposedly, you can add default sizing and positioning, but they don&#39;t appear to work yet.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui_samples&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;SamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyboardShortcut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;u&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defaultPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topLeading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// doesn&#39;t work yet&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defaultSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// doesn&#39;t work yet&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Charts&lt;/h3&gt;
&lt;p&gt;One of the signature features of SwiftUI at WWDC 2022 was the new Charts API. I have struggled with charts in the past, so this was very interesting. I need to learn more, but I added a simple bar chart to the samples window:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/bar_chart.jpg&quot; alt=&quot;Bar chart&quot;&gt;&lt;/p&gt;
&lt;p&gt;This charts the number of entries in each category of HTTP status. I drew a line across at the 8 value because drawing a marker line is a feature I have often needed in charts.&lt;/p&gt;
&lt;p&gt;Creating the chart was smooth:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Chart&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chartData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;BarMark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Count&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chartData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Color&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;RuleMark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Threshold&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Having assembled a dictionary of category codes and counts, I looped through the keys, setting an &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; value for each in its own &lt;code&gt;BarMark&lt;/code&gt;. The line used a &lt;code&gt;RuleMark&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get the different colors, I added this modifier to the &lt;code&gt;Chart&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chartForegroundStyleScale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1xx&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;green&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2xx&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;purple&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3xx&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4xx&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yellow&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5xx&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which was used by &lt;code&gt;foregroundStyle&lt;/code&gt; to assign a different color to each of the categories.&lt;/p&gt;
&lt;p&gt;Next, I wanted to add the ability to switch between vertical and horizontal bars. This requires swapping the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; data so that &lt;code&gt;y&lt;/code&gt; has the category and &lt;code&gt;x&lt;/code&gt; has the count. After testing this manually, I added a Boolean to specify the orientation, and a method for returning the appropriate &lt;code&gt;BarMark&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;barMark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BarMark&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; drawHorizontal &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BarMark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Count&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chartData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BarMark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Count&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chartData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A similar method provided the &lt;code&gt;RuleMark&lt;/code&gt; as either a vertical or horizontal line.&lt;/p&gt;
&lt;p&gt;A segmented picker switches between the two and the only thing now was to animate the change. There is an &lt;code&gt;animation&lt;/code&gt; modifier added last year that allows you to connect an animation to a property, so that when the property changes, the animation is triggered.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easeInOut&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; drawHorizontal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/chart_animation.mp4&quot; alt=&quot;Animated chart&quot;&gt;&lt;/p&gt;
&lt;p&gt;I added &lt;code&gt;Text&lt;/code&gt; views for the title of the chart and for the axes, inside a  &lt;code&gt;VStack&lt;/code&gt; and an &lt;code&gt;HStack&lt;/code&gt;, rotating the text for vertical axis. Maybe there is a chart setting that I&#39;m missing for adding these labels automatically using the Chart library.&lt;/p&gt;
&lt;h3&gt;Image Rendering&lt;/h3&gt;
&lt;p&gt;While investigating charts, I experimented with another new SwiftUI feature: &lt;code&gt;ImageRenderer&lt;/code&gt;. When I have made charts, I&#39;ve often wanted to export them as images for upload. &lt;code&gt;ImageRenderer&lt;/code&gt; allows us to convert a SwiftUI view into an image: on macOS, either &lt;code&gt;NSImage&lt;/code&gt; or &lt;code&gt;CGImage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first step was to convert the chart view plus its labels, into a variable (modifiers and RuleMark omitted from this code snippet for brevity):&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; chartView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Status codes by category&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;drawHorizontal &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Count&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rotationEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;degrees&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;270&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Chart&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chartData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;barMark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;drawHorizontal &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Count&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Category&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I displayed the view using the variable:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  chartView

  &lt;span class=&quot;token comment&quot;&gt;// Picker &amp;amp; Button&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This left the display unchanged, but allowed me to use this variable to create a view for rendering, omitting the controls that should not be part of the image:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Save Chart as Image&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; view &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chartView
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; renderer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageRenderer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; exportImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; renderer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nsImage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;saveImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exportImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;frame&lt;/code&gt; modifiers to the view variable as it came out very small without this. Then I created an &lt;code&gt;ImageRenderer&lt;/code&gt; using this view and converted it into an &lt;code&gt;NSImage&lt;/code&gt;.
The &lt;code&gt;saveImage&lt;/code&gt; method uses an &lt;code&gt;NSSavePanel&lt;/code&gt; to get a URL and then convert the NSImage to JPG data before writing it out. I used the same technique in &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-3/&quot;&gt;part 3 of the original series&lt;/a&gt; but that only exported the downloaded image. This creates an image from a complete SwiftUI view.&lt;/p&gt;
&lt;p&gt;I would be interested to try the SwiftUI &lt;code&gt;fileExporter&lt;/code&gt;, but that&#39;s for another day.&lt;/p&gt;
&lt;p&gt;I also tried to use the new &lt;code&gt;ShareLink&lt;/code&gt; to share this image, but couldn&#39;t get that to work. The &lt;code&gt;ImageRenderer&lt;/code&gt; seems to work asynchronously so that didn&#39;t work well with &lt;code&gt;ShareLink&lt;/code&gt;. Again, that&#39;s for another day.&lt;/p&gt;
&lt;h3&gt;Forms&lt;/h3&gt;
&lt;p&gt;SwiftUI has always had a &lt;code&gt;Form&lt;/code&gt; object, but the new System Settings app (previously System Preferences) has taken this to a new level. So I added some UI elements to a second tab in the UI Samples window.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/form.jpg&quot; alt=&quot;Form&quot;&gt;&lt;/p&gt;
&lt;p&gt;There are a few interesting things here. Firstly, I achieved something I was unable to do last time and that is to make the text edit field have focus when the view opens. This was a three part operation:&lt;/p&gt;
&lt;p&gt;Firstly, I added this property to the view:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@FocusState&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; emailFieldHasFocus&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, I added this modifier to the edit field:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Enter your email address&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$emailFieldHasFocus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, I set the Boolean property in the &lt;code&gt;onAppear&lt;/code&gt; modifier:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onAppear &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  emailFieldHasFocus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, the email text entry field had the focus whenever this view appeared.&lt;/p&gt;
&lt;p&gt;The date picker is slightly confused. It can&#39;t seem to decide whether it&#39;s a graphical picker or a numeric picker, but it works. I haven&#39;t tried the new date range picker yet.&lt;/p&gt;
&lt;p&gt;With the check marks, I was trying to emulate the toggles in System Settings, where they are very small switches. I set the &lt;code&gt;toggleStyle&lt;/code&gt; to &lt;code&gt;switch&lt;/code&gt; which gave the right shape but it was big, like on an iPhone or iPad. The outermost form is set to use the &lt;code&gt;columns&lt;/code&gt; &lt;code&gt;formStyle&lt;/code&gt;. This lines up the labels and controls very neatly. But when I put the &lt;code&gt;Toggles&lt;/code&gt; and &lt;code&gt;Picker&lt;/code&gt; into an inner form with a style of &lt;code&gt;grouped&lt;/code&gt;, I got the exact System Settings look I was going for.&lt;/p&gt;
&lt;p&gt;As an aside, I love the way you can now apply control-specific modifiers to a container view and every appropriate view inside the container will use that setting. For example, I added &lt;code&gt;.toggleStyle(.switch)&lt;/code&gt; to the &lt;code&gt;Form&lt;/code&gt; view and it was applied to every &lt;code&gt;Toggle&lt;/code&gt; inside it.&lt;/p&gt;
&lt;p&gt;The final item in the form is a color picker. It opens up the standard macOS color picker and uses the selected color to fill the capsule beside it. The interesting thing is that the color has a &lt;code&gt;gradient&lt;/code&gt; modifier. So you can see the faint gradient that is automatically applied to the shape.&lt;/p&gt;
&lt;h3&gt;Menu Bar Apps&lt;/h3&gt;
&lt;p&gt;Writing a menu bar app has previously required using AppKit to create a &lt;code&gt;NSStatusItem&lt;/code&gt;. Now, we can add a &lt;code&gt;MenuBarExtra&lt;/code&gt; scene to the app body to create a menu bar app component.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;MenuBarExtra&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HTTP Status Code&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; systemImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;number.circle&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isInserted&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showMenuBar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;MenuBarView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;httpSections&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $httpSections&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting the title without a &lt;code&gt;systemImage&lt;/code&gt; shows the text as the menu bar title. when you specify a &lt;code&gt;systemImage&lt;/code&gt;, the text is not displayed and the menu bar only shows the image.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MenuBarExtra&lt;/code&gt; takes an &lt;code&gt;isInserted&lt;/code&gt; parameter, so you can turn it off and on. I added this to the app&#39;s settings and stored it using &lt;code&gt;@AppStorage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I set up a new SwiftUI view that looped through the HTTP Status data and created a submenu for each category and set this as the &lt;code&gt;MenuBarExtra&lt;/code&gt; view. This works and my menu bar component was functional. Selecting a status uses &lt;code&gt;NSWorkspace&lt;/code&gt; to open the relevant documentation page at MDN.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/menu_bar_extra.jpg&quot; alt=&quot;MenuBarExtra&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;2022 is an exciting year to be a SwiftUI programmer. Apple has made it very clear that this is the way forward and as early adopters, we have the ability to influence how the framework develops, so keep filing those feedback reports! There have been some great new features this year: navigation is better than ever, the platforms are unifying and macOS is not getting left behind.&lt;/p&gt;
&lt;p&gt;The project from this article is available on &lt;a href=&quot;https://github.com/trozware/swiftui-mac-2022&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. And as usual, I&#39;d be thrilled to hear any suggestions, corrections or improvements. Please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. And if you found this article useful, I&#39;d love you to &lt;a href=&quot;https://ko-fi.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;buy me a coffee&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Writing Setup</title>
    <link href="https://troz.net/post/2022/setup/" />
    <updated>2022-06-17T04:08:46Z</updated>
    <id>https://troz.net/post/2022/setup/</id>
    <content type="html">&lt;p&gt;If you read my blog or tweets, you will have detected that I recently wrote a book called &lt;a href=&quot;https://www.raywenderlich.com/books/macos-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS by Tutorials&lt;/a&gt;, published by &lt;a href=&quot;https://www.raywenderlich.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;raywenderlich.com&lt;/a&gt;. in this post, I&#39;m going to show you what hardware and software I used to write it.&lt;/p&gt;

&lt;h2&gt;Hardware&lt;/h2&gt;
&lt;p&gt;I use an &lt;a href=&quot;https://www.apple.com/imac-24/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;24 inch M1 iMac - yellow&lt;/a&gt;. I&#39;ve always loved the form factor of the iMacs. They offer a superb monitor attached to a very good computer and the price is reasonable. My previous iMac used to make a noise like a kettle boiling when Xcode really got going, but this one is totally silent no matter what I do.&lt;/p&gt;
&lt;p&gt;I&#39;m a recent convert to a dual-monitor rig. I&#39;ve always been content to use &lt;a href=&quot;https://support.apple.com/en-au/guide/mac-help/mh14112/mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Spaces&lt;/a&gt; which provides virtual screens, and I still do that. But having the luxury of a second screen is great when you&#39;re writing and previewing. My second screen is a &lt;a href=&quot;https://www.samsung.com/au/monitors/curved/curved-monitor-24-inch-lc24f390fhexxy/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Samsung C24F390&lt;/a&gt; - a curved 24 inch display. The resolution isn&#39;t anything like as good as the iMac, but I tweaked its settings to give as good a result as possible and I can&#39;t imagine going back to a single monitor now.&lt;/p&gt;
&lt;p&gt;My keyboard is an &lt;a href=&quot;https://www.zsa.io/moonlander/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ergodox Moonlander&lt;/a&gt; which I love. This is my first mechanical keyboard and I wasn&#39;t sure if I&#39;d like it, since I appreciated the low travel on my standard Apple keyboard. But I chose the Kailh Silver key switches which have the lowest travel and are the least clicky. It&#39;s still quite noisy, but the travel was not an issue. The configuration options are amazing. It took several iterations, but I now have a &lt;a href=&quot;https://configure.zsa.io/embed/moonlander/layouts/9OwwA/latest/0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;keyboard layout&lt;/a&gt; that works really well for me. And I keep my Apple keyboard tucked under my monitor stand so the Touch ID button is in easy reach.&lt;/p&gt;
&lt;p&gt;For a pointing device, I have the &lt;a href=&quot;https://www.logitech.com/en-au/products/mice/mx-vertical-ergonomic-mouse.910-005449.html&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Logitech MX Vertical Ergonomic Mouse&lt;/a&gt; which is very comfortable to use and charges via USB-C. Just don&#39;t install the Logitech Options utility software which is buggy and causes system problems. Use &lt;a href=&quot;https://folivora.ai&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;BetterTouchTool&lt;/a&gt; instead  if you want to customize the buttons.&lt;/p&gt;
&lt;h2&gt;Software&lt;/h2&gt;
&lt;h3&gt;App Development&lt;/h3&gt;
&lt;p&gt;For writing sample apps and code, I use &lt;a href=&quot;https://apps.apple.com/app/xcode/id497799835&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Xcode&lt;/a&gt; set up in a completely standard way, using the default light scheme. This is so that my screenshots are consistent with what people will see if they are just getting started.&lt;/p&gt;
&lt;p&gt;I wrote an article about &lt;a href=&quot;https://troz.net/post/2018/swiftlint/&quot;&gt;Consistent Swift Style&lt;/a&gt; some years ago. The concepts are still valid but the way you integrate it with Xcode has changed. Add a build phase to your project, move it to before the Compile Scripts phase and set it to this:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$PATH&lt;/span&gt;:/opt/homebrew/bin&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;which&lt;/span&gt; swiftlint &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  swiftlint
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will use the default SwiftLint config which you can change in Terminal.&lt;/p&gt;
&lt;p&gt;If you&#39;ve saved a custom config file, use something like this:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/opt/homebrew/bin:&lt;span class=&quot;token environment constant&quot;&gt;$PATH&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; path/to/your-config-file.yml &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;which&lt;/span&gt; swiftlint &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    swiftlint --no-cache &lt;span class=&quot;token parameter variable&quot;&gt;--config&lt;/span&gt; path-to/your-config-file.yml
  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make SwiftLint fix what it can automatically, duplicate the &lt;code&gt;swiftlint&lt;/code&gt; line and add &lt;code&gt;--fix&lt;/code&gt; as another parameter to the first copy. This will run SwiftLint twice: once to auto-fix what it can, and a second time to supply the warnings and errors about what it couldn&#39;t fix.&lt;/p&gt;
&lt;h3&gt;Writing&lt;/h3&gt;
&lt;p&gt;The text of the book was written in &lt;a href=&quot;https://www.markdownguide.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Markdown&lt;/a&gt; which is great for producing formatted text in a totally open and transferable format. I&#39;m always on the lookout for a new Markdown editor and there are a lot of them around. In Section 3 of the book, I talk you through creating a Markdown editor for yourself, but it isn&#39;t quite ready for full-time use yet. I wrote this book using &lt;a href=&quot;https://www.foldingtext.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;FoldingText&lt;/a&gt; which I was quite happy with. While the folding part was not something I used, I appreciated the outline view. Editing text inside links and image links was difficult and the spell checker sometimes got confused and marked an entire sentence or paragraph as incorrect. But the formatting was good and easy to use with plenty of keyboard shortcuts. Code formatting was also excellent, which is a must.&lt;/p&gt;
&lt;p&gt;I have a bad habit of using the passive voice, which is not good when trying to teach. I use &lt;a href=&quot;https://hemingwayapp.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hemingway Editor&lt;/a&gt; to help me detect this, so that the passive voice can be eradicated... (Passive voice joke there.) You can use it online, but I prefer to use the app.&lt;/p&gt;
&lt;h3&gt;Screen shots&lt;/h3&gt;
&lt;p&gt;If you&#39;ve read the book, you&#39;ll know that it includes a lot of screen shots. There are a lot of third-party utilities for doing this, but the built-in facility gives me everything I need. It&#39;s important to memorise the keyboard shortcuts to make this easier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shift-Command-3&lt;/strong&gt;: screen shot the entire screen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shift-Command-4&lt;/strong&gt;: bring up crosshairs to allow selection of an area:
&lt;ul&gt;
&lt;li&gt;When in this mode:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Escape&lt;/strong&gt; exits without action.&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Space&lt;/strong&gt; to enter window mode. The window under the pointer is selected and will be screen shotted.&lt;/li&gt;
&lt;li&gt;Hold down &lt;strong&gt;Command&lt;/strong&gt; to select a dialog box instead of the complete window.&lt;/li&gt;
&lt;li&gt;Hold down &lt;strong&gt;Option&lt;/strong&gt; to turn off the default shadow.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shift-Command-5&lt;/strong&gt;: bring up the interface where you set options, take screen recordings and set a delay.
&lt;ul&gt;
&lt;li&gt;I find this useful when I want to show the custom cursor during a drag operation. I turn on &lt;strong&gt;Show Mouse Pointer&lt;/strong&gt;, set a 5 second delay and then start dragging before the timer runs out.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once I have the image, I use Preview to crop and resize as needed.&lt;/p&gt;
&lt;h2&gt;And...&lt;/h2&gt;
&lt;p&gt;Finally, there&#39;s my support crew who watch every word I type and listen carefully as I explain my code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/figures.jpg&quot; alt=&quot;Figurines&quot;&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>WWDC 2022 Wishlist</title>
    <link href="https://troz.net/post/2022/wwdc_wishlist/" />
    <updated>2022-04-25T09:00:36Z</updated>
    <id>https://troz.net/post/2022/wwdc_wishlist/</id>
    <content type="html">&lt;p&gt;Apple has announced that their 2022 World Wide Developers Conference will again be an online event from June 7 to 11. The poster shows a very dark Swift logo in a coloured circle. While I have long since given up trying the read the tea leaves of Apple&#39;s graphics to work out what they will announce, I always enjoy speculating. But this year, I thought that instead of trying to guess what Apple will have for us, I&#39;d think about what I would &lt;strong&gt;like&lt;/strong&gt; them to announce, with my main focus being the Mac.&lt;/p&gt;

&lt;h3&gt;Hardware&lt;/h3&gt;
&lt;p&gt;The Apple hardware teams have been doing amazingly well over the last couple of years. The transition of the Mac lineup to Apple Silicon is almost complete and I would guess that they will complete it at WWDC with a new Mac Pro, but that is outside my budget and requirements.&lt;/p&gt;
&lt;p&gt;While I was glad to see them offer a more reasonably priced monitor with the Studio Display, it&#39;s still a bit pricey for me. The desk lamp iMac was one of my all time favourite Mac designs so it was great to see the option for the tilt &amp;amp; height adjustable stand, although the extra price for that option was rather eye-popping.&lt;/p&gt;
&lt;p&gt;I have long thought that the desktop iMac was terrific value because it attaches a stunningly good monitor to a reasonable computer. I wish Apple would supply an iMac monitor using the exact same design, but without the computer. Imagine how great that would look as a second monitor - two coloured machines side by side.&lt;/p&gt;
&lt;p&gt;But really, I&#39;m not looking for any new hardware right now.&lt;/p&gt;
&lt;h3&gt;Services&lt;/h3&gt;
&lt;p&gt;iCloud needs a lot of love, especially on the Mac side. I was trying to collaborate with a colleague so they shared a folder on iCloud. I was able to access their small set of files (24K total) after about 30 minutes, but my edits never uploaded. In the Finder sidebar, I have a perpetual progress indicator beside my iCloud folder and uploads never complete. The only solution is to reboot my computer, which does a complete sync once. We switched to sharing on our iPads and it all worked immediately, so the problem is not iCloud itself, but the macOS integration.&lt;/p&gt;
&lt;p&gt;CloudKit has always looked like a great solution for use in apps. It allows public and private data, it syncs between devices, and it doesn&#39;t have the potential for unexpected and huge data fees like some other options. But like many developers, I&#39;ve tried and failed to get it to work reliably. It appears that the simpler mechanisms work well, but not the more complex options.&lt;/p&gt;
&lt;p&gt;And while I&#39;m talking about iCloud sync issues, some apps (Notes, Reminders etc) sync - albeit very slowly, but Messages operates as if it is only partially connected. If I delete a conversation on my iPhone or iPad, I get a warning telling me that this will remove the conversation from my other devices, which is what I would expect. This deletion flows through to my Mac and the conversation is gone everywhere. On my Mac, deleting a conversation only deletes it locally and I have to delete it again on my iPhone or iPad to get it to disappear completely. What is the logic of that?&lt;/p&gt;
&lt;h3&gt;Software&lt;/h3&gt;
&lt;p&gt;There are two parts to Apple&#39;s software: the operating systems and their own apps. iOS, iPadOS and watchOS seem quite solid in their latest versions. I don&#39;t have any particular requests or complaints.&lt;/p&gt;
&lt;p&gt;With macOS, apart from the iCloud problems that I mentioned above, my main complaint is with notifications. The user experience is terrible. Notifications popup at the top corner, and then disappear quickly. If they have action buttons, you have to mouse over the notification to see them, and this obscures the text. I have tried using System Preferences to change the way notifications are displayed, but this seems to have no effect. And trying to explain this system to a newcomer is impossible.&lt;/p&gt;
&lt;p&gt;Apart from that, I like macOS 12 (Monterey). I would prefer Apple to go back to using numbers instead of names. Trying to work out what system people are using by the name is very confusing.&lt;/p&gt;
&lt;p&gt;The other aspect of software is Apple&#39;s in-house apps. Sadly, Apple has decided to use Catalyst to port a lot of apps from the iPad to macOS. For a small software house with very limited resources, I can see the attraction of Catalyst although, in my opinion, it results in a greatly inferior product that gives Mac users an inferior experience. However this is hardly a valid excuse for a company the size of Apple.&lt;/p&gt;
&lt;p&gt;As a developer, one of my main tools is Xcode. This is an amazing app and over the past few years it has got a lot of new features that allow it to integrate better with source control and with the app distribution system. But I would love to see Apple open up the extension ecosystem again. For web development, I use Microsoft&#39;s Visual Studio Code. Even though this is a cross-platform Electron-based web app, it is a great tool. It doesn&#39;t look like a native Mac app but it performs well. But it&#39;s major advantage is the huge number and variety of extensions so you can make VSC look and work the way you want. Apple has deliberately closed Xcode off from this sort of community involvement which is great shame.&lt;/p&gt;
&lt;p&gt;Apple will tell you that they do allow Xcode extensions. But these are limited in scope, difficult to install and opaque to use. Xcode extensions used to be great - please bring them back again.&lt;/p&gt;
&lt;h3&gt;Developer Relations&lt;/h3&gt;
&lt;p&gt;This is where things get nasty. We all know that Tim Cook will bounce on to the WWDC stage saying how much they love their developers and how many billions they&#39;ve paid out, but this is really just a smoke screen. Most of the money goes to a few large software houses who basically blackmail users into paying over and over again to make their apps (mostly games) usable. The race to the bottom has resulted in app stores where charging up front for an app is virtually impossible, although the Mac App Store is slightly better in this regard.&lt;/p&gt;
&lt;p&gt;The big issue is Apple&#39;s extremely erratic policing of the app stores. They allow behaviour in some apps and ban it is others. Big players get different treatment to small. Despite claiming that the app stores are the safe place to buy apps, Apple makes almost no effort to weed out scam apps, copyright infringements or fake reviews even when they are completely obvious to the most casual search.&lt;/p&gt;
&lt;p&gt;I don&#39;t mind paying Apple a cut of my app revenue. They do a lot of work that I would find tedious or difficult to set up for myself. I don&#39;t even mind their restrictions. What I mind very deeply is the inconsistent way this is handled. I would love it if the App Stores lived up to Apple&#39;s claim as being the safe place to buy apps, but in reality, app buyers are being fooled by purchased reviews, tricked into in-app purchases, and cannot assume that the rules designed to keep them safe have been applied to all apps.&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;So what would I really like to see at WWDC 2022?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iCloud syncing that works on all Apple platforms.&lt;/li&gt;
&lt;li&gt;Real Xcode extensions.&lt;/li&gt;
&lt;li&gt;New App Store rules to weed out copycat apps and fake reviews and that apply equally to all developers.&lt;/li&gt;
&lt;li&gt;Better notifications for macOS - preferably the type we used to have.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>macOS Book</title>
    <link href="https://troz.net/post/2022/macos_book/" />
    <updated>2022-04-09T00:53:43Z</updated>
    <id>https://troz.net/post/2022/macos_book/</id>
    <content type="html">&lt;p&gt;I can finally reveal the reason I have not been blogging. I have been busy writing a book. It&#39;s finally out, so here it is: &lt;a href=&quot;https://www.kodeco.com/books/macos-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS by Tutorials&lt;/a&gt;, published by &lt;a href=&quot;https://www.kodeco.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;kodeco.com&lt;/a&gt; (formerly raywenderlich.com).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2022/book_cover.png&quot; alt=&quot;Book Cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;Anyone who reads my posts or tweets will know that I am a passionate advocate for building real native macOS apps. This book is aimed at iOS developers who know some Swift and SwiftUI but don&#39;t yet know all the tips and tricks needed to convert that knowledge into a great Mac experience.&lt;/p&gt;
&lt;p&gt;You don&#39;t need to be a highly experienced developer, but some basic Swift will be useful.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Contents&lt;/h3&gt;
&lt;p&gt;The book consists of five sections. In each of the first four sections, I&#39;ll walk you through developing a complete app. Each one is a different type of app using different techniques and different frameworks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 1&lt;/strong&gt;: Use SwiftUI to build a multi-paned window app. Learn how to manage windows, menus and toolbars.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 2&lt;/strong&gt;: Build a menu-bar app using AppKit with a bit of SwiftUI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 3&lt;/strong&gt;: Back to SwiftUI to create a document-based app, with a bit of AppKit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Section 4&lt;/strong&gt;: Use your new skills to make an app that communicates with the macOS Terminal.&lt;/p&gt;
&lt;p&gt;The final section deals with app distribution, covering both inside and outside the Mac App Store.&lt;/p&gt;
&lt;p&gt;You can read more details of the book contents and why I wanted to write it in the &lt;a href=&quot;https://www.kodeco.com/books/macos-by-tutorials/v1.0/chapters/v-introduction&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Introduction&lt;/a&gt; which is a free section.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Thanks to...&lt;/h3&gt;
&lt;p&gt;I call this &lt;em&gt;my&lt;/em&gt; book, but of course, it was only made possible by an awesome team. There were a lot of people involved, but I want to give special thanks to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/RayFromVA&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ray Wenderlich&lt;/a&gt; who always has time for my Mac obsession and who let me write the book I wanted to write.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/mandarazeware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Manda Frederick&lt;/a&gt; was the book team lead when we started and was a huge source of encouragement.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/rcritz&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Richard Critz&lt;/a&gt;, the wonderful editor who had the unenviable task of fixing my grammar. He was very supportive and helped me to convince everyone of the merits of this approach.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/mataharimau&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Audrey Tam&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/Waheiba&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ehab Amer&lt;/a&gt; were the amazing tech editors who had to make sure the instructions all worked and made sense. They took what I wrote and made it a lot better.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole team at &lt;a href=&quot;https://twitter.com/rwenderlich&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Kodeco / Ray Wenderlich&lt;/a&gt; was great, so a huge thanks to them all. And I hope they enjoyed working on the book half as much as I did, because I had the best time!&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Where can you read the book?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You can read it online at &lt;a href=&quot;https://www.kodeco.com/books/macos-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;kodeco.com&lt;/a&gt; as part of a Kodeco subscription.&lt;/li&gt;
&lt;li&gt;You can download an ebook (ePub or pdf) at the same page if you have a subscription or buy one there if you don&#39;t.&lt;/li&gt;
&lt;li&gt;The paperback is available at &lt;a href=&quot;https://www.amazon.com/dp/1950325660/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Amazon&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All the code and extra materials for the book can be downloaded or cloned from &lt;a href=&quot;https://github.com/kodecocodes/mos-materials/tree/editions/1.0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;I would love to hear from anyone who read the book, loved it, hated it, found an error or just wanted to say hello.&lt;/p&gt;
&lt;p&gt;You can contact me directly using any of the contact links at the bottom of this page or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.
There is also an &lt;a href=&quot;https://forums.kodeco.com/c/books/macos-by-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;official forum&lt;/a&gt; for the book where you can ask questions and tell us about any errors you find.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Site Search</title>
    <link href="https://troz.net/post/2021/site_search/" />
    <updated>2021-10-24T23:57:12Z</updated>
    <id>https://troz.net/post/2021/site_search/</id>
    <content type="html">&lt;p&gt;Not much action on the site lately as I work on other projects. But I have finally got around to adding a search option to the site.&lt;/p&gt;

&lt;p&gt;I use &lt;a href=&quot;https://gohugo.io/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hugo&lt;/a&gt; as a static site generator, which is great but Hugo doesn&#39;t come with any search facility.&lt;/p&gt;
&lt;p&gt;Today, I read an article by Jesse Squires on &lt;a href=&quot;https://www.jessesquires.com/blog/2018/02/25/replacing-google-with-duckduckgo/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Replacing Google Search with DuckDuckGo&lt;/a&gt; and while I had already switched to using &lt;a href=&quot;https://duckduckgo.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;DuckDuckGo&lt;/a&gt; for my own web searching, I had not considered it for my site search.&lt;/p&gt;
&lt;p&gt;It isn&#39;t as good as an in-built search page, which I have considered setting up using &lt;a href=&quot;https://www.algolia.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Algolia&lt;/a&gt;, but that requires a lot of setup and I never got round to it. This search takes you out of my site and into a DuckDuckGo page, but it limits the search options to the troz.net domain.&lt;/p&gt;
&lt;p&gt;To access the search, go to either the &lt;a href=&quot;https://troz.net/post/&quot;&gt;Archives&lt;/a&gt; or the &lt;a href=&quot;https://troz.net/tags/&quot;&gt;Tags&lt;/a&gt; page, and use the search field at the top of the page.&lt;/p&gt;
&lt;p&gt;As a side note, if you are using DuckDuckGo as your regular search engine, read up about their &lt;a href=&quot;https://duckduckgo.com/bang&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;!bangs&lt;/a&gt;. These are quick and easy ways to target your search. you can prefix your search with &lt;code&gt;!so&lt;/code&gt; to search Stack Overflow, or &lt;code&gt;!i&lt;/code&gt; to search for images. Search the &lt;a href=&quot;https://duckduckgo.com/bang&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;!bangs&lt;/a&gt; page to find the ones that are useful to you.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The Apple DTK Gamble</title>
    <link href="https://troz.net/post/2021/apple_dtk_gamble/" />
    <updated>2021-02-04T08:57:21Z</updated>
    <id>https://troz.net/post/2021/apple_dtk_gamble/</id>
    <content type="html">&lt;p&gt;So I gambled…&lt;/p&gt;
&lt;p&gt;I hoped Apple would do the right thing by developers who paid for access to the Developer Transition Kit (DTK) to get the first Apple Silicon Macs, but I was wrong and I lost.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE - 6 Feb 2020:&lt;/strong&gt; Apple has responded to feedback from me and other disgruntled developers and so I didn&#39;t lose as much as I thought. Apple is now giving US $500 credit, which is what developers in the US paid for the DTK, and they are extending the time limit to the end of the year. I am quite certain they will release a desktop M1 Mac before then, so I will get credit for most of the cost of the DTK (after exchange rate losses) and I will be able to use the credit towards a Mac that I want.&lt;/p&gt;
&lt;p&gt;Thanks Apple for listening and reacting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;
&lt;p&gt;I paid Apple AU $779 for a year’s lease on a DTK which was effectively a Mac mini with an iPad CPU.
I also had to buy a monitor to use with it.&lt;/p&gt;
&lt;p&gt;That was in June 2020.&lt;/p&gt;
&lt;p&gt;In November 2020, the M1 was released which made the A14Z based DTK totally irrelevant for testing or development, so the lease effectively only lasted for 5 months. At that I was luckier than many devs as a lot of these DTK machines started failing at around the 4 month mark.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I kept Apple’s secrets.&lt;/li&gt;
&lt;li&gt;I tested their hardware and software.&lt;/li&gt;
&lt;li&gt;I filed bug reports, a couple of which even got acknowledged.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And in return, I get asked to send back the DTK some months early for which I will get the princely reward of US $200 credit.&lt;/p&gt;
&lt;p&gt;When Apple switched over to Intel CPUs, they gave the DTK buyers a free computer when they returned the DTK, but of course Apple wasn&#39;t a trillion dollar company back then so they could afford to be generous. (How does one indicate heavy sarcasm in text?)&lt;/p&gt;
&lt;p&gt;I don’t want a laptop, I want a desktop Mac, so there is nothing to spend my $200 credit on yet, but as a final sting in the tail, Apple has decreed that the credit expires at the end of May 2021. There is no guarantee that there will be desktop Mac to buy before then. Only Apple knows that and they have made me cynical enough to consider that they might decide not to release any new Macs until after these credits expire.&lt;/p&gt;
&lt;p&gt;I realize that Apple made no commitment to do anything after the DTK program was over, but history indicated they would.&lt;/p&gt;
&lt;p&gt;The smallness of the credit and the short time limit on it seem unnecessarily petty and punitive, especially when the people they are punishing are the ones that add so much value to the Apple platforms.&lt;/p&gt;
&lt;p&gt;Assuming I have to get a laptop, I will want a high-powered one as it will have to be able to run Xcode. Apple&#39;s flagship developer tool is famously demanding.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In the US, the one I want costs US $1,899&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Here in Australia, it is AU $2,899 - 1.53 times the US price.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The US$200 credit will use the exchange rate at 23 December 2020, which for USD - AUD was 1.3218 which is significantly lower than the ratio in Apple’s pricing between the two countries.&lt;/p&gt;
&lt;p&gt;This means that I will get AU $264.36 for my credit, which is 9% of the cost of the computer to replace it, even though that is not the computer I actually want.&lt;/p&gt;
&lt;p&gt;Financially, I would have been much better off waiting until the M1s were released. But Apple needed the early machines tested, so they suckered us in and then slapped us in the face.&lt;/p&gt;
&lt;p&gt;I wonder how Tim Cook and the others can stand up again and again and say that they value their developers. Does that not make them feel ashamed? Or maybe they struggle not to laugh while saying it?&lt;/p&gt;
&lt;p&gt;Thanks Apple.&lt;/p&gt;
&lt;p&gt;And good luck finding suckers to test your next round of new hardware.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwifUI Mac Menus</title>
    <link href="https://troz.net/post/2021/swiftui_mac_menus/" />
    <updated>2021-01-24T05:24:17Z</updated>
    <id>https://troz.net/post/2021/swiftui_mac_menus/</id>
    <content type="html">&lt;p&gt;With the option to create apps using the &lt;strong&gt;SwiftUI App&lt;/strong&gt; life cycle, we get a new way to set up menus in Mac apps. This post will explore some of the ways to do this as well as look at the default menu groups that Apple gives us.&lt;/p&gt;

&lt;hr&gt;
&lt;p&gt;When you create a new Mac app, the standard menu items are included for you. Using the &lt;code&gt;commands&lt;/code&gt; modifier on the &lt;code&gt;WindowGroup&lt;/code&gt; allows you to add new menus, add new menu items and to replace or remove existing menu items.&lt;/p&gt;
&lt;h2&gt;Adding a New Menu&lt;/h2&gt;
&lt;p&gt;To add a completely new menu, you can use &lt;code&gt;CommandMenu&lt;/code&gt;, supplying it with a title and the contents to display.&lt;/p&gt;
&lt;p&gt;After starting this process, the App.swift file looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MenuSamplesApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;commands &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token class-name&quot;&gt;CommandMenu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Custom Menu&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// no contents yet&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So what can be put inside the menu? We are used to only being allowed to use &lt;code&gt;NSMenuItems&lt;/code&gt; but with SwiftUI there are no such things. It turns out that you can put quite a few SwiftUI views into a menu.&lt;/p&gt;
&lt;p&gt;The most obvious is a &lt;code&gt;Button&lt;/code&gt; with an action which appears like any standard menu item.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;CommandMenu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Custom Menu&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Menu Button selected&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Menu Button&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have found that the easiest way to pass this selection on to other views is via a notification. Buttons can be disabled and enabled just as usual.&lt;/p&gt;
&lt;p&gt;The contents of the button will usually be a &lt;code&gt;Text&lt;/code&gt; view, but it can include an &lt;code&gt;Image&lt;/code&gt; view if you want to use an SFSymbol or any other image in the menu.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;clock&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Date &amp;amp; Time&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apart from Buttons, there are several other SwiftUI Views that work in a menu. A very common use case in a menu is to have a switchable boolean setting that is displayed with a leading check mark if it is on.&lt;/p&gt;
&lt;p&gt;This can be done using a &lt;code&gt;Toggle&lt;/code&gt; view.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isOn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $toggleSetting&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Toggle&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I created a variable to hold the setting using &lt;code&gt;@State&lt;/code&gt; but &lt;code&gt;@AppStorage&lt;/code&gt; would be better if this setting needed to persist between launches. To respond to changes, you can use an &lt;code&gt;onChange&lt;/code&gt; modifier.&lt;/p&gt;
&lt;p&gt;This demonstrates one of the huge strengths of SwiftUI&#39;s declarative approach. You tell the compile you want a &lt;code&gt;Toggle&lt;/code&gt; and it is automatically structured to fit the place and platform where it is being used.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Picker&lt;/code&gt; is a good choice for allowing a single selection from multiple options. This makes a sub menu with check marks.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Picker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Appearance&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selection&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $pickerChoice&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dark&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Light&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;System&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;system&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make a sub-menu with standard menu items like buttons, use &lt;code&gt;Menu&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Menu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sub Menu&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sub Item 1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sub Item 2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sub Item 3&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Menu items are often best split up with a separator and this is done in SwiftUI by adding a &lt;code&gt;Divider()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you try a UI element in a menu and it doesn&#39;t work, it tends to show as disabled text and doesn&#39;t break the menu, so experiment. I was surprised to find that &lt;code&gt;Link&lt;/code&gt; does not work in a menu.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Editing Existing Menus&lt;/h2&gt;
&lt;p&gt;I have covered creating a new menu, but what about adding new items to an existing menu?&lt;/p&gt;
&lt;p&gt;In this case, use &lt;code&gt;CommandGroup&lt;/code&gt; instead of &lt;code&gt;CommandMenu&lt;/code&gt;. The difference is that instead of setting a title for the menu, you give it a placement option with a reference to an existing standard menu item.&lt;/p&gt;
&lt;p&gt;Still inside the &lt;code&gt;commands&lt;/code&gt; contents, start typing &lt;code&gt;CommandGroup&lt;/code&gt; to see three options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CommandGroup(after:, addition:)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CommandGroup(before:, addition:)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CommandGroup(replacing:, addition:)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each case, the first parameter is a &lt;code&gt;CommandGroupPlacement&lt;/code&gt; that specifies one of a limited range of standard menu items.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2021/CommandGroupPlacement.png&quot; alt=&quot;CommandGroupPlacement&quot;&gt;&lt;/p&gt;
&lt;p&gt;The options for the menu items inside a &lt;code&gt;CommandGroup&lt;/code&gt; are just the same as when creating your own menu.&lt;/p&gt;
&lt;h3&gt;Removing a Standard Menu Item&lt;/h3&gt;
&lt;p&gt;If you really want to get rid of one of the default menu items (or groups of items), you can use &lt;code&gt;CommandGroupPlacement&lt;/code&gt; and replace the existing one with an &lt;code&gt;EmptyView()&lt;/code&gt;. This is probably not a great idea as regards the Human Interface Guidelines but it is an option if you need it.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;CommandGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;replacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CommandGroupPlacement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appVisibility&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;EmptyView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also replace an existing menu item with your own version if you want to be able to respond to it in a more custom way. I can imagine doing this for the Help menu item as I do not like using Apple&#39;s Help system and prefer to direct users elsewhere.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Preset Menu Groups&lt;/h2&gt;
&lt;p&gt;If you watched my &lt;a href=&quot;https://backtomac.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Back to the Mac&lt;/a&gt; talk called &lt;a href=&quot;https://www.youtube.com/watch?v=hus2LksB3O4&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Build a Mac app inside 30 minutes using nothing but SwiftUI&lt;/a&gt;, you will have seen who I used a rather clunky function to dive into AppKit in order to stop the sidebar disappearing completely and permanently.&lt;/p&gt;
&lt;p&gt;If you haven&#39;t watched it yet, go and take a look - I&#39;ll wait… If you made it this far, I assume you are a Mac app fan like me and I think you will enjoy the talk as it demonstrated a lot of SwiftUI Mac app techniques.&lt;/p&gt;
&lt;p&gt;Since I recorded that video, I found out that Apple provides preset menu groups for dealing with certain things and one of them is for sidebars. My clunky solution is still useful if you want to have a toolbar button, but I think I would include the menu group as well.&lt;/p&gt;
&lt;p&gt;There are five present Commands groups provided by Apple, four of which are useful and the fifth has a purpose that escapes me.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SidebarCommands()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ToolbarCommands()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inserting both these into your &lt;code&gt;commands&lt;/code&gt; will expand the default View menu, so that it goes from this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2021/BasicViewMenu.png&quot; alt=&quot;BasicViewMenu&quot;&gt;&lt;/p&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2021/SidebarToolbarMenu.png&quot; alt=&quot;SidebarToolbarMenu&quot;&gt;&lt;/p&gt;
&lt;p&gt;You do not need to include both, but it was easier to show in one screen shot instead of two.&lt;/p&gt;
&lt;p&gt;If your app allows any serious text editing, you are going to want to use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TextEditingCommands()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which changes the default Edit menu from this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2021/BasicEditMenu.png&quot; alt=&quot;BasicEditMenu&quot;&gt;&lt;/p&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2021/TextEditingMenu.png&quot; alt=&quot;TextEditingMenu&quot;&gt;&lt;/p&gt;
&lt;p&gt;And if you need your users to be able to format text, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TextFormattingCommands()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;adds in a Format menu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2021/TextFormattingMenu.png&quot; alt=&quot;TextFormattingMenu&quot;&gt;&lt;/p&gt;
&lt;p&gt;And finally, there is &lt;code&gt;EmptyCommands()&lt;/code&gt; which does nothing and has no obvious use.&lt;/p&gt;
&lt;p&gt;These are used like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;commands &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;SidebarCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;ToolbarCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;TextEditingCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;TextFormattingCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Using a Separate File&lt;/h2&gt;
&lt;p&gt;If you are adding a lot of menus, you are probably starting to feel that your App.swift file is getting a bit crowded. You can separate out these menu commands into one or more separate files, so long as they are contained in a struct that conforms to the &lt;code&gt;Commands&lt;/code&gt; protocol and which has a &lt;code&gt;body&lt;/code&gt; that also conforms to &lt;code&gt;Commands&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MenuCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Commands&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; toggleSetting&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Commands&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;CommandMenu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Custom Menu&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Menu Button selected&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Menu Button&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

			&lt;span class=&quot;token class-name&quot;&gt;Divider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

			&lt;span class=&quot;token class-name&quot;&gt;Toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isOn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $toggleSetting&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Toggle&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This struct cannot have its own data or you will get lots of warnings, but it can have bindings to data from its parent. Insert a call to this struct, and any others you have created, in the &lt;code&gt;commands&lt;/code&gt; modifier, just like for the Apple presets.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; toggleSetting &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;commands &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;MenuCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;toggleSetting&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $toggleSetting&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token class-name&quot;&gt;SidebarCommands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Bonus: Credits&lt;/h2&gt;
&lt;p&gt;Every Mac app has an &amp;quot;About …&amp;quot; menu item which brings up a simple dialog box with basic app information: icon, app name and version number. These will always appear but there is an easy way to add more information underneath this info and it can include formatted text and links.&lt;/p&gt;
&lt;p&gt;Create a new file called &lt;code&gt;Credits.rtf&lt;/code&gt; - the New File dialog has a Rich Text File option. Anything in a file with this exact name will be shown below the standard information in the About box.&lt;/p&gt;
&lt;p&gt;Xcode&#39;s RTF editor is not great, but it is good enough for this. There are some basic formatting tools above the text editor, but you can also right-click on text to get more options, including Font and Color panels.&lt;/p&gt;
&lt;p&gt;To insert a web link, go to the Editor menu and select Add Link…&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Well that&#39;s it. A quick round-up of the ways to use menus in your SwiftIU Mac apps. I hope you found it useful. If you have any suggestions, comments, corrections or improvements, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI Data Flow</title>
    <link href="https://troz.net/post/2019/swiftui-data-flow/" />
    <updated>2021-01-24T05:17:14Z</updated>
    <id>https://troz.net/post/2019/swiftui-data-flow/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/xcode/swiftui/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftUI&lt;/a&gt; gives us a completely new way to lay out out user interfaces, in a declarative and responsive way. Your data dictates what is displayed. But this leads to a new problem - how should the data models be constructed and how can they be passed around between the various views that make up your app?&lt;/p&gt;
&lt;p&gt;In this post, I intend to discuss the possibilities with examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update - January 2021:&lt;/strong&gt; I think the information in this post is still all valid except for one change. When you are initializing an ObservableObject, you should use &lt;code&gt;@StateObject&lt;/code&gt; instead of &lt;code&gt;@ObservedObject&lt;/code&gt;. Your views can receive objects that are owned by other views as &lt;code&gt;@ObservedObject&lt;/code&gt; or &lt;code&gt;@EnvironmentObject&lt;/code&gt; but the owner of the data should always create the data object with &lt;code&gt;@StateObject&lt;/code&gt;.&lt;/p&gt;

&lt;hr&gt;
&lt;p&gt;For a great diagram showing what data object type to use where, check out this image from &lt;a href=&quot;https://twitter.com/chriseidhof/status/1280433133813456896&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Chris Edihof&lt;/a&gt; - thanks Chris!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/dataflow.png&quot; alt=&quot;Data flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you read this article before November 18th 2019, please check out &lt;a href=&quot;https://troz.net/post/2019/swiftui-data-flow/#observable2&quot;&gt;ObservableObject &amp;amp; @ObservedObject - Part 2&lt;/a&gt; again as it has gone through several iterations, which are all shown for comparison.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;At WWDC 2019, some Apple engineers gave a great presentation on &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/226/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Data Flow Through SwiftUI&lt;/a&gt; and I strongly recommend watching the video. But you need to watch it twice. First watch it so that you can get started, and then, when you feel that this is all way too complicated, watch it again and it will start to click into place. The only real difference between then and now is that BindableObject has become ObservableObject.&lt;/p&gt;
&lt;p&gt;I guess I could stop there, but I will be illustrating my ideas with code examples, which I hope will make things clearer. Some of the examples are rather contrived in order to make a point, but the sample code is also sprinkled with other SwiftUI examples which I hope will prove useful.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trozware/swiftui-data-flow/tree/master&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Download the sample project from GitHub&lt;/a&gt; and open it in Xcode. Go to ContentView.swift and make sure the Canvas is open. Click Resume to make the view appear. Then click the Live Preview button and wait for the view to become active. I recommend clicking the Pin button at the bottom left of the Canvas so that you can investigate the code samples, while still working in the main navigation.&lt;/p&gt;
&lt;h2&gt;Data Flow Options&lt;/h2&gt;
&lt;p&gt;There are 5 ways to specify data in SwiftUI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Property&lt;/li&gt;
&lt;li&gt;@State&lt;/li&gt;
&lt;li&gt;@Binding&lt;/li&gt;
&lt;li&gt;ObservableObject &amp;amp; @ObservedObject&lt;/li&gt;
&lt;li&gt;@EnvironmentObject&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/ContentView.png&quot; alt=&quot;Content View&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Property&lt;/h2&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Property&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; greeting &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello from SwiftUI!&quot;&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Using property directly&lt;/span&gt;
         &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;greeting&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;greeting&lt;/code&gt; property is used in the view. &lt;code&gt;greeting&lt;/code&gt; is static, so the view does not need to monitor it for changes. This may seem like a simplistic and obvious example, but separating it out allows for localization or re-usability.&lt;/p&gt;
&lt;p&gt;The property could also have been supplied by a parent view and this is a really useful feature of properties. A parent view can have some dynamic data that it can use to set properties in a subview where the subview only needs to display the data statically. This data will change as the parent view changes but the subview will not be able to change the data in the parent view.&lt;/p&gt;
&lt;p&gt;And it is important to remember that not everything needs to be set up with one of these new property wrappers.&lt;/p&gt;
&lt;h2&gt;@State&lt;/h2&gt;
&lt;p&gt;This is where things start to get very interesting in the SwiftUI world. SwiftUI views are contained in structs, so are non-mutable. Also, the views are re-created every time the data changes, so any properties are re-created then too. By marking a property with the &lt;code&gt;@State&lt;/code&gt; property wrapper, you are telling SwiftUI that you want it to keep this data in a separate portion of memory, allow it to be mutated, and preserve the current value during the view refresh.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UsingState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; toggleValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Using state with 2-way binding&lt;/span&gt;
         &lt;span class=&quot;token class-name&quot;&gt;Toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isOn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $toggleValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Toggle is &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toggleValue &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ON&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OFF&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;toggleValue&lt;/code&gt; is declared as a &lt;code&gt;Bool&lt;/code&gt; with a property wrapper of &lt;code&gt;@State&lt;/code&gt;. Inside the &lt;code&gt;Toggle&lt;/code&gt;, the &lt;code&gt;isOn&lt;/code&gt; value is bound to &lt;code&gt;toggleValue&lt;/code&gt; by the leading &lt;code&gt;$&lt;/code&gt;. This allows the variable to set the toggle and the toggle to set the variable - 2-way binding.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/Toggle.png&quot; alt=&quot;Toggle&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@State&lt;/code&gt; variables are always value types and are usually local to their view, so Apple recommends marking them as &lt;code&gt;private&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And unlike properties, &lt;code&gt;@State&lt;/code&gt; variables allow you to have data that is dynamic but it can still be passed to subviews as properties for display.&lt;/p&gt;
&lt;h2&gt;@Binding&lt;/h2&gt;
&lt;p&gt;One problem with building SwiftUI views is that it is very easy to end up with a gigantic Pyramid of Doom as you embed views within views within views. The solution is to extract subviews, but then you need a way to pass the data to the subview.&lt;/p&gt;
&lt;p&gt;This is where &lt;code&gt;@Binding&lt;/code&gt; comes in as it allows you to tell a view that the data for a property is actually coming from a parent but that the subview is allowed to change that property and that change will flow back to the parent&#39;s data.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; stepperValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NumberChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $stepperValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Using state from parent with 2-way binding&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ZStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Stepper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $stepperValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Value = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;stepperValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token class-name&quot;&gt;NumberBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; stepperValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NumberBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// As this view never changes the value, there is no need to bind it&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; stepperValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;systemName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;stepperValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.square&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, I have declared a &lt;code&gt;stepperValue&lt;/code&gt; property and marked it with &lt;code&gt;@State&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The interface has been extracted into a subview called &lt;code&gt;NumberChooser&lt;/code&gt; and a &lt;code&gt;Binding&lt;/code&gt; to the &lt;code&gt;stepperValue&lt;/code&gt; property has been passed to &lt;code&gt;NumberChooser&lt;/code&gt; using the &lt;code&gt;$&lt;/code&gt; prefix, which will ensure that changes to the value can come back. Inside &lt;code&gt;NumberChooser&lt;/code&gt; this property is wrapped in the &lt;code&gt;@Binding&lt;/code&gt; property wrapper to indicate that it is coming from another source and that changes will be returned.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NumberChooser&lt;/code&gt; itself has a subview called &lt;code&gt;NumberBlock&lt;/code&gt; but it is a display view only and never mutates the value itself, so &lt;code&gt;stepperValue&lt;/code&gt; is passed to this subview as a property only, without the &lt;code&gt;$&lt;/code&gt; prefix. It will still be updated every time the data changes as it is contained by the view with the &lt;code&gt;@State&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/NumberChooser.png&quot; alt=&quot;Number Chooser&quot;&gt;&lt;/p&gt;
&lt;h2&gt;@State &amp;amp; @Binding - Part 2&lt;/h2&gt;
&lt;p&gt;So far, the examples have used primitive data types for the @State properties, but given that &lt;code&gt;@State&lt;/code&gt; properties are value types, any struct can be used. In the next example, I use a struct to hold the properties of a pizza order and use a SwiftUI Form to allow selections.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PizzaView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Using @State for a struct&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; pizza &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pizza&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Form&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// Using 2-way binding but each component&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// only needs 1 property from the struct&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;PizzaNamePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedPizzaName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;PizzaSizePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedPizzaSize&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;PizzaCrustPicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedPizzaCrust&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;crust&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token comment&quot;&gt;// Text representation to prove that the&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// subviews are modifying the parent struct&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pizza&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pizzaSelection&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigationBarTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Choose Your Pizza&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each subview gets the property it needs using &lt;code&gt;@Binding&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PizzaNamePicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedPizzaName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PizzaName&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// see the GitHub project for more details&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The form consists of 3 subviews - one each for selecting the pizza, size and crust. The Pizza struct holds all three properties, but each subview only needs a &lt;code&gt;Binding&lt;/code&gt; to the single property that it controls. The Text view after the Form is to prove that all the selections come back to the parent.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/PizzaView.png&quot; alt=&quot;Pizza View&quot;&gt;&lt;/p&gt;
&lt;h2&gt;ObservableObject &amp;amp; @ObservedObject - Part 1&lt;/h2&gt;
&lt;p&gt;These are used if your data model is a class and you want to use reference-based data instead of the struct&#39;s value-based system.&lt;/p&gt;
&lt;p&gt;To set up a data model to be Observable, it must conform to the &lt;code&gt;ObservableObject&lt;/code&gt; protocol and any property that needs to be observed should have the &lt;code&gt;@Published&lt;/code&gt; property wrapper. This makes sure that any time this property changes, all the Views that are observing the instance of this data model will be notified to perform the UI updates.&lt;/p&gt;
&lt;p&gt;For this example, I have a ColorSet class with six color components that are used to assemble two RGB colors.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ObservableObject&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The 6 color components are marked as @Published so any changes&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// get published to the views that are observing&lt;/span&gt;

    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foregroundRed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foregroundGreen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foregroundBlue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;

    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; backgroundRed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; backgroundGreen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; backgroundBlue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the primary view, I set up an instance of this class as an &lt;code&gt;@ObservedObject&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; colorSet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The background of the view and the foreground of a system image are set using these colors. A button presents a sheet with sliders to allow editing these colors. The &lt;code&gt;colorSet&lt;/code&gt; is passed to the sheet like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $showChooser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// notice that this does not use $ as the ColorChooser&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// will get a reference to the ColorSet object&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ColorChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;colorSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorChooser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; colorSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ColorSet&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A sheet is not the ideal way of presenting a view that uses sliders for editing, but I wanted to demonstrate that changing the sliders instantly changes the value of the &lt;code&gt;@ObservedObject&lt;/code&gt; for the parent view as well as for the subview. Editing the background color components shows the new background color in the back at the top of the sheet.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ColorChooser&lt;/code&gt; itself uses 2 subviews and they get a reference to the &lt;code&gt;ColorSet&lt;/code&gt; in the same manner.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/ColorChooser.png&quot; alt=&quot;Color Chooser&quot;&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a name=&quot;observable2&quot;&gt;ObservableObject &amp;amp; @ObservedObject - Part 2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section was that one that caused me the most trouble when writing the example app. I wanted to display a list of data and have each entry link to an editable detail view with the edits flowing back to the parent list.&lt;/p&gt;
&lt;p&gt;The initial display of data in a List was straight-forward and I was then able to have the list rows navigate to a detailed view for each entry. The problem was getting the edited data back to the parent List.&lt;/p&gt;
&lt;p&gt;The basic data model was an &lt;code&gt;ObservableObject&lt;/code&gt; that publishes an array of &lt;code&gt;PersonViewModel&lt;/code&gt; objects.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonListModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; persons&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PersonViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// get data from web ...&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newData
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this data is going to trigger a UI update after a background network call, it is important that changes to the &lt;code&gt;@Published&lt;/code&gt; property get switched to the main thread.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;PersonViewModel&lt;/code&gt; also needs to be &lt;code&gt;Observable&lt;/code&gt; with the editable properties marked as &lt;code&gt;@Published&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Identifiable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Even though this is not observed directly,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// it must be an ObservableObject for the data flow to work&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; first&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; last&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; phone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The odd thing was the way that I had to pass the data to the detail view. This is what I tried initially:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; person &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token class-name&quot;&gt;PersonDetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in &lt;code&gt;PersonDetailView&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonViewModel&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This almost worked. The correct data was passed to the detail view, and the data edits changed the data, but the parent list was never re-drawn. If I changed a record&#39;s first name then went back to the list, the change was not displayed. But if I then returned to the detail view for the same record, my edits were there, so I could tell that the data was changing correctly. The problem was how to change it in such a way that the parent view was notified of the change.&lt;/p&gt;
&lt;p&gt;Trying to bind &lt;code&gt;person&lt;/code&gt; with &lt;code&gt;PersonDetailView(person: $person)&lt;/code&gt; gave the error &lt;code&gt;Use of unresolved identifier &#39;$person&#39;&lt;/code&gt;, so the &lt;code&gt;ForEach&lt;/code&gt; enumeration did not provide a direct connection to the &lt;code&gt;personList&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;The solution I came up with was to switch to enumerating by index in the &lt;code&gt;ForEach&lt;/code&gt; and passing a direct member of the parent list&#39;s data to the detail view. And switching the &lt;code&gt;PersonDetailView&lt;/code&gt; to use &lt;code&gt;@Binding var person: PersonViewModel&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; index &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PersonDetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works but as well as being hard to read, it has one major flaw. The rows in the table are identified by their row number, rather than by anything in the data like the &lt;code&gt;person.id&lt;/code&gt;. This can really mess with how SwiftUI handles the List and how it knows which rows have changed and need to be re-rendered. It is important to identify rows by something unique to the data in each row so that SwiftUI knows that you have deleted the row with the ID &amp;quot;abcd-1234&amp;quot; and not row #7. Because if you delete row ID &amp;quot;abcd-1234&amp;quot; there is no longer a row ID &amp;quot;abcd-1234&amp;quot; but if you delete row #7, there is now a different row #7 and anything could happen.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;strong&gt;Update 1:&lt;/strong&gt; &lt;a href=&quot;https://twitter.com/StewartLynch&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@StewartLynch&lt;/a&gt; contacted me to suggest a much neater way to pass the person data to the PersonDetailView by using a function to get a &lt;code&gt;Binding&amp;lt;PersonViewModel&amp;gt;&lt;/code&gt; for each &lt;code&gt;person&lt;/code&gt; being displayed. This worked perfectly and made for a much cleaner looking bit of code. Thanks Stewart.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; person &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PersonDetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectedPerson&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And outside the &lt;code&gt;body&lt;/code&gt; declaration:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;selectedPerson&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Binding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PersonViewModel&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;firstIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This person does not exist.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to have a look at Stewart&#39;s solution, check out &lt;a href=&quot;https://github.com/trozware/swiftui-data-flow/tree/57f48ea28d1e987566398800e74f12e339eac231&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;this commit on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 2:&lt;/strong&gt; &lt;a href=&quot;https://twitter.com/vadimshpakovski&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@vadimshpakovski&lt;/a&gt; says that creating a binding for every &lt;code&gt;person&lt;/code&gt; object is inefficient and that the function to create this binding will slow things down. He suggests using &lt;code&gt;onReceive&lt;/code&gt; to react to changes to &lt;code&gt;person&lt;/code&gt; and trigger an update of &lt;code&gt;personList&lt;/code&gt;. In this case, &lt;code&gt;PersonDetailView&lt;/code&gt; uses &lt;code&gt;@ObservedObject var person: PersonViewModel&lt;/code&gt;. This also works perfectly.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; person &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;PersonDetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objectWillChange&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                  &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objectWillChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to have a look at Vadim&#39;s&#39;s solution, check out &lt;a href=&quot;https://github.com/trozware/swiftui-data-flow/tree/093810bab93a984292c4a7b8bf29316a830e9f50&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;this commit on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 3:&lt;/strong&gt; More suggestions have come in from the community (thanks to everyone who contributed) and it has been pointed out to me that while Vadim&#39;s solution does solve a lot of the issues, it means that the entire &lt;code&gt;ForEach&lt;/code&gt; has to be recalculated to check for changes every time a single &lt;code&gt;Person&lt;/code&gt; is edited. And it also inserts model management code into the view code, which is not great.&lt;/p&gt;
&lt;p&gt;So my next attempt goes back to using &lt;code&gt;@Binding var person: PersonViewModel&lt;/code&gt; in &lt;code&gt;PersonDetailView&lt;/code&gt; but instead of &lt;code&gt;PersonListModel&lt;/code&gt; having an array of &lt;code&gt;PersonViewModels&lt;/code&gt;, it has an array of &lt;code&gt;UUIDs&lt;/code&gt; and a dictionary of &lt;code&gt;UUID: PersonListModel&lt;/code&gt;. The benefit of this is that the UUIDs can be used in the &lt;code&gt;ForEach&lt;/code&gt; as they are unique to each row, and the dictionary can be used to provide a Binding to the &lt;code&gt;person&lt;/code&gt; for each &lt;code&gt;UUID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This removes the problem of my original solution by identifying each row uniquely, it goes back to Stewart&#39;s solution but eliminates the potential slow function to create a binding for the matching person, and eliminates the issue of complete redraws and model management inside views from Vadim&#39;s suggestion.&lt;/p&gt;
&lt;p&gt;But it was not entirely straight-forward as getting a value from a dictionary by key returns an optional. At first I thought I could use the new &lt;code&gt;default&lt;/code&gt; syntax for dictionaries to get a non-optional value for binding but for some reason that couldn&#39;t be used to create a &lt;code&gt;Binding&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The answer was to write an extension on Dictionary with a &lt;code&gt;subscript&lt;/code&gt; function that returns a non-optional value or gives a fatal error. Since I am in control of the data and set up every UUID with a matching PersonViewModel, this is not dangerous.&lt;/p&gt;
&lt;p&gt;So here is what we have now:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonListModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Main list view model&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ObservableObject so that updates are detected&lt;/span&gt;

    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ids&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; persons&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// get data from web ...&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; personViewModels &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dataArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ids &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; personViewModels&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
              uniqueKeysWithValues&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; personViewModels&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The incoming data is mapped to a sorted array of &lt;code&gt;PersonViewModels&lt;/code&gt; before extracting the UUIDs and creating the dictionary. This means that the UUIDs array is in the correct sort order for use in the &lt;code&gt;ForEach&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is the Dictionary extension:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dictionary&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonViewModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;subscript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;unchecked key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This person does not exist.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newValue
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And these go together to allow this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ids&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonDetailView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;unchecked&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;unchecked&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;personList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;unchecked&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/PersonListView.png&quot; alt=&quot;Person List View&quot;&gt;&lt;/p&gt;
&lt;p&gt;This ended up a bit more complicated than my original idea, but I think it is now &lt;em&gt;good&lt;/em&gt; SwiftUI, avoiding several problems from the earlier solutions.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://next.json-generator.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;JSON Generator&lt;/a&gt; for the sample data. And if anyone has any other solutions to this problem, I would love to hear it. You can contact me using any of the buttons at the end of this article.&lt;/p&gt;
&lt;h2&gt;@EnvironmentObject&lt;/h2&gt;
&lt;p&gt;Think of the EnvironmentObject a piece of state that can be used by any view or any descendent of the view once it has been introduced. People who have used React or any of the similar web development technologies will be familiar with the concept of global state and this is similar to that, although not truely global.&lt;/p&gt;
&lt;p&gt;You set up a class as an EnvironmentObject model exactly as you would set up an ObservableObject with the same protocol conformance and using the &lt;code&gt;@Published&lt;/code&gt; property wrapper to mark properties whose changes will trigger UI updates. Here is a very simple example with just one property.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isLoggedIn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/NestedViews.png&quot; alt=&quot;Nested Views&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this example, the yellow view is the parent view - the different views have different brightly colored backgrounds to make them easy to distinguish. The yellow view has access to the &lt;code&gt;UserSettings&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NestingViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@EnvironmentObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; userSettings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ZStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yellow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;edgesIgnoringSafeArea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// UI omitted for space reasons&lt;/span&gt;

                &lt;span class=&quot;token comment&quot;&gt;// display first nested view&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ChildView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ChildView&lt;/code&gt; contained in this parent - the green view - has no need to access this data and so gets no &lt;code&gt;@EnvironmentObject&lt;/code&gt; property. But &lt;code&gt;ChildView&lt;/code&gt; contains another subview - the blue one. And this &lt;code&gt;GrandChildView&lt;/code&gt; does need access to the &lt;code&gt;UserSettings&lt;/code&gt; so it has the exact same &lt;code&gt;@EnvironmentObject var userSettings: UserSettings&lt;/code&gt; property as the parent view.&lt;/p&gt;
&lt;p&gt;If this was using &lt;code&gt;@ObservedObject&lt;/code&gt; the data would have to be passed through every view in an un-broken chain, even though &lt;code&gt;ChildView&lt;/code&gt; did not need this data. By using &lt;code&gt;@EnvironmentObject&lt;/code&gt; the chain can be broken, but any view that needs to, can access and mutate this data. In the example, both the yellow and the blue views display and edit the same data with updates happening in both when either button is pressed.&lt;/p&gt;
&lt;p&gt;One key thing to remember about previewing &lt;code&gt;@EnvironmentObject&lt;/code&gt; in the Canvas is that every view that uses it, or that contains a view that uses it, needs to be supplied with the &lt;code&gt;ObservableObject&lt;/code&gt; in the preview using &lt;code&gt;.environmentObject()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NestingViews_Previews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreviewProvider&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; previews&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NestingViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environmentObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the app itself, only the first view to access the &lt;code&gt;@EnvironmentObject&lt;/code&gt; needs it set. In this example, it is done in the &lt;code&gt;NavigationLink&lt;/code&gt; that goes to the &lt;code&gt;NestingViews&lt;/code&gt; example. The &lt;code&gt;.environmentObject&lt;/code&gt; can be provided to the root view in the &lt;code&gt;SceneDelegate&lt;/code&gt; if the root view needs it.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NestingViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environmentObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ListContents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;@EnvironmentObject&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imageNumber&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Or What Should I Use When?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For value-based data models or data primitives, use &lt;code&gt;@State&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For reference-based data use &lt;code&gt;ObservableObject&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For data needed by a lot of views in your app, use &lt;code&gt;@EnvironmentObject&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;@Binding&lt;/code&gt; or &lt;code&gt;@ObservedObject&lt;/code&gt; to pass data to a view that can mutate it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And one final tip: while creating a view from scratch, use &lt;code&gt;@State&lt;/code&gt; with sample, hard-coded data. Once you have the interface you want, then change it to use real data.&lt;/p&gt;
&lt;p&gt;I am sure people will develop their own theories and their own ways of using SwiftUI, but those are the guidelines that I intend to follow for now. If you have different views and would like to discuss them, please contact me.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Crossfix</title>
    <link href="https://troz.net/post/2020/crossfix/" />
    <updated>2020-11-22T22:59:45Z</updated>
    <id>https://troz.net/post/2020/crossfix/</id>
    <content type="html">&lt;p&gt;I have just released &lt;a href=&quot;https://itunes.apple.com/app/crossfix/id1526235954&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Crossfix&lt;/a&gt; and &lt;a href=&quot;https://itunes.apple.com/app/crossfix-lite/id1526236100&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Crossfix Lite&lt;/a&gt; for iPhone. They are both the same anagram assistant solver for crosswords, particularly cryptic crosswords. The Lite version is free but limits you to three solves per day. The full version is unlimited with no ads, no recurring subscriptions and works with family sharing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Crossfix-anagram.webp&quot; alt=&quot;Anagram solving&quot;&gt;&lt;/p&gt;
&lt;p&gt;For more details, check out the app page at &lt;a href=&quot;https://troz.net/crossfix/&quot;&gt;https://troz/net/crossfix/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And if you are interested in the back story and the development process, have a look at &lt;a href=&quot;https://troz.net/post/2020/name_this_app/&quot;&gt;Name This App&lt;/a&gt;. Thanks to everyone who suggested a name, and special thanks to Jerry who came up with the winning entry.&lt;/p&gt;
&lt;p&gt;This is the first app that I have published that is 100% SwiftUI, although as I started it several months ago, it still uses the old App Delegate life cycle.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac on Big Sur</title>
    <link href="https://troz.net/post/2020/swiftui_mac_big_sur/" />
    <updated>2020-07-08T09:40:16Z</updated>
    <id>https://troz.net/post/2020/swiftui_mac_big_sur/</id>
    <content type="html">&lt;p&gt;In December 2019, I wrote a &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-1/&quot;&gt;series of articles about using SwiftUI to build a Mac app&lt;/a&gt;. At WWDC 2020, Apple announced macOS 11 Big Sur along with Xcode 12 and a heap of new features for SwiftUI, so I decided to try creating my test app again and seeing how much had changed.&lt;/p&gt;

&lt;p&gt;You can read the earlier articles and if you are interested, &lt;a href=&quot;https://github.com/trozware/swiftui-mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;download the previous project from GitHub&lt;/a&gt;. Usually, when revisiting an old app, I would update the existing project, but this time I want to use the new SwiftUI App so I am going to start from scratch with a new app project, copying in code as required. And I plan to follow the same basic thread as I did in the previous articles. Who knows haw many parts this one will have!&lt;/p&gt;
&lt;p&gt;For reference, I am running the beta of macOS 11 Big Sur (20A4299v) and the beta of Xcode 12 (12A6159) on a rather aged MacBook with a dodgy battery. I do not have Xcode 12 installed on my Catalina Mac, so I have no way of telling whether some of the oddities are due to Xcode or Big Sur but I will try to keep this article updated as the new betas improve things.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I have downloaded macOS Big Sur beta 2 (20A4300b) and Xcode 12 beta 2, oddly labelled in the About box as 11.5 (11E608c). I have only found one thing that needs to be changed. I had set the deployment target to 11.0 but the new Xcode does not allow this as an option and the app fails to build. Change the deployment target to 10.16 to make it work.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Xcode_b2_error.png&quot; alt=&quot;Xcode 12 beta 6 error&quot;&gt;&lt;/p&gt;
&lt;p&gt;It looks like Apple is having some problems with consistency in the new version numbers in both macOS and Xcode.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The New Project&lt;/h2&gt;
&lt;p&gt;As before, the first step was to create a new project in Xcode. I chose the macOS App template although the Multiplatform option was intriguing. When setting the options for the new app, I chose SwiftUI for the Interface, SwiftUI App for the Life Cycle and Swift (of course) for the language.&lt;/p&gt;
&lt;p&gt;Once I had selected a save location and the project was open, I checked out the file structure. The main difference here was that there is no AppDelegate.swift file. Instead there is a file called &amp;quot;SwiftUI_Mac_11App.swift&amp;quot; where &amp;quot;SwiftUI_Mac_11&amp;quot; is the name of my app. This file is vastly simpler than the old AppDelegate but it sets up the app, the scene and the window group, with the expected ContentView as the main content.&lt;/p&gt;
&lt;p&gt;ContentView.swift had a change too. Instead of setting the &lt;code&gt;maxWidth&lt;/code&gt; and &lt;code&gt;maxHeight&lt;/code&gt; of the view to &lt;code&gt;.infinity&lt;/code&gt;, this Content View only adds some padding to the standard &lt;code&gt;Text&lt;/code&gt; view. Time to see what happens when I run...&lt;/p&gt;
&lt;p&gt;The first run took longer than I expected (I told you it was an old MacBook) but then a very small window appeared, just big enough to show the &amp;quot;Hello world!&amp;quot; text with its padding. The app has all the expected menus although I have no idea where they are set up.&lt;/p&gt;
&lt;p&gt;Closing the app and using the Canvas preview worked much as before, although the canvas now has additional controls including a button to add another preview and a menu to configure each preview. Oddly, running the preview and clicking &amp;quot;Bring Forward&amp;quot; displayed two app windows. I presume this is an Xcode 12 beta &amp;quot;feature&amp;quot;.&lt;/p&gt;
&lt;p&gt;I used the attributes Inspector to edit the &lt;code&gt;Text&lt;/code&gt; view, setting text, font style and weight and color. I had read that Xcode 12 had much better auto-formatting, but when adding modifiers this way, they all ended up on a single line which was not pretty. Manually adding line feeds before each dot did give a very neat structure, so typing in modifiers will be my preferred method.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Editing the Layout&lt;/h2&gt;
&lt;p&gt;Now on to my own layout. As I did last time, I used a static list to make a &lt;code&gt;NavigationView&lt;/code&gt; for preliminary testing.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NavigationView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                 &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; index &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                     &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                         &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Link &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
             &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I ran the app or looked at it in the Canvas it looked great, but when I clicked a link in the sidebar, the window shrunk to an unusably small size. So I added a &lt;code&gt;frame&lt;/code&gt; modifier to the destination view. And I also set the &lt;code&gt;ListStyle&lt;/code&gt; to &lt;code&gt;SidebarListStyle()&lt;/code&gt; which gave the new macOS 11 full height sidebar look.&lt;/p&gt;
&lt;p&gt;But here is where I encountered the first major difference. When I selected an item in the sidebar, the app correctly showed the detail view. But if I put the app into the background, this detail view disappeared. It literally disappeared because I was able to track that it was calling its &lt;code&gt;onDisappear&lt;/code&gt; modifier.&lt;/p&gt;
&lt;p&gt;After a lot of testing, several days of frustration and help from &lt;a href=&quot;https://twitter.com/jsngr&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jordan Singer&lt;/a&gt; with his &lt;a href=&quot;https://github.com/jordansinger/mail-swiftui-sample&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;mail-swiftui-sample&lt;/a&gt; project, I was finally able to work this out. The difference is that the initial view has to specify what comes next as well as what it is displaying. So in the initial &lt;code&gt;NavigationView&lt;/code&gt;, I had a &lt;code&gt;List&lt;/code&gt;, but I also needed some view to show that I was going to show another view in another column. This second column view has to reserve the space for the detail view to come.&lt;/p&gt;
&lt;p&gt;I have no idea how Jordan worked this out, but I am very grateful. What I ended up with was this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NavigationView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; index &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Link &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SidebarListStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Select a link...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &amp;quot;Select a link...&amp;quot; text is shown when there is no destination view to display. And when I click one of the entries in the sidebar, its content replaces that &lt;code&gt;Text&lt;/code&gt; view but keeps its frame.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Displaying Data&lt;/h2&gt;
&lt;p&gt;As with the previous app, I am displaying data from &lt;a href=&quot;https://http.cat/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;HTTP Cats&lt;/a&gt;. This part remains the same, so check back to the original article if you want to see how I did this. I did get warnings about the &lt;code&gt;Decodable&lt;/code&gt; data structs I was using. My normal pattern when I want data to be &lt;code&gt;Identifiable&lt;/code&gt; is to use something like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpSection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Identifiable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Decodable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; headerCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; headerText&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; statuses&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But with Xcode 12, this gives an error. The error messages seem much more useful than before and they offered two suggestions. I tried what appeared to be the easier one of making the &lt;code&gt;id&lt;/code&gt; mutable, but that stopped the decoding from working, so I added a &lt;code&gt;CodingKeys&lt;/code&gt; enum to each struct to restrict the keys that would be included.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodingKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodingKey&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; headerCode
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; headerText
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; statuses
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the app ran, I had a sidebar with collapsible sections. I wasn&#39;t that happy with the default look of the section headers and the disclosure marker for the first header was positioned beside the text instead of at the right, but I do like the selected item lozenge look. I set a frame height for the section header view and that made the spacing better but the disclosure mark for the first header was still incorrectly positioned.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-mac-11-BadCollapse.png&quot; alt=&quot;Initial collapsible sections&quot;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Detail&lt;/code&gt; view to be displayed when a status is selected had no major changes, so I dropped in the file from the previous app, without the extra bits to do with notifications and preferences. However since SwiftUI now includes a &lt;code&gt;ProgressView&lt;/code&gt;, I swapped out my &amp;quot;Loading...&amp;quot; text for a indeterminate progress twirly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Three Column Layout&lt;/h3&gt;
&lt;p&gt;Since the collapsible sidebar wasn&#39;t looking good, I decide to try for a three column layout with the categories in the first column, the codes for the selected category in the second column and the detail view in the third column.&lt;/p&gt;
&lt;p&gt;The way I implemented this was to have the first list only show the section headers with &lt;code&gt;NavigationLinks&lt;/code&gt; to a sub menu. To reserve the column layout, I had an empty submenu after the main list.&lt;/p&gt;
&lt;p&gt;In the second column, I showed the sub-headers with &lt;code&gt;NavigationLinks&lt;/code&gt; to the &lt;code&gt;DetailView&lt;/code&gt;. This view reserved the space for the third column with a &lt;code&gt;Text&lt;/code&gt; view that changed its text depending on whether that submenu was displaying anything or not.&lt;/p&gt;
&lt;p&gt;I expanded the menu items to be big and chunky, which I feel fits the new style. The only remaining problem was that with the primary list set to use &lt;code&gt;SidebarListStyle()&lt;/code&gt;, it only ever showed the first item. I had to change this to &lt;code&gt;InsetListStyle()&lt;/code&gt; to see all the items! This lost the full height sidebar effect, but I assume this is a temporary bug.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-mac-11-ThreeColumns.png&quot; alt=&quot;Three column layout&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Menus&lt;/h3&gt;
&lt;p&gt;Now I had the layout equivalent to what I ended up with after part 1 of my original series, so it was time to explore menus, which are very different. In the old style SwiftUI apps which were wrapped in AppKit, the menus were in a storyboard that you could edit. Now you add &lt;code&gt;commands&lt;/code&gt; to the app&#39;s &lt;code&gt;WindowGroup&lt;/code&gt; to alter the menus.&lt;/p&gt;
&lt;p&gt;The easiest way is to add a completely new menu, but it is also possible to insert new menu items after or before certain standard menu items.&lt;/p&gt;
&lt;p&gt;Here is how to add a new Utilities menu with three items:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI_Mac_11App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;commands &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;CommandMenu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Utilities&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dark mode&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Light mode&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;System mode&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;CommandMenu&lt;/code&gt; adds a new menu and it is always positioned between the standard View and Window menus. To add items to an existing menu, you need to use a &lt;code&gt;CommandGroup&lt;/code&gt; and tell it where to appear. It can be &lt;code&gt;after&lt;/code&gt; or &lt;code&gt;before&lt;/code&gt; various standard menu items. Look up the docs for &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/commandgroupplacement&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;CommandGroupPlacement&lt;/a&gt; to see which items you can use as positional markers. I used &lt;code&gt;after .windowArrangement&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Weirdly, the font, color and spacing of these menu items was different to the standard menu items. I can only assume this is a bug which will be fixed. I faked the proper look with modifiers, but it didn&#39;t feel right. This screen shot shows the default look with the three mode items being my additions to the standard Window menu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-mac-11-menus-bad-format.png&quot; alt=&quot;Menus with incorrect formatting&quot;&gt;&lt;/p&gt;
&lt;p&gt;I tried using &lt;code&gt;Labels&lt;/code&gt; to add an icon to the menu item. As I was switching between view modes, I wanted to have a checkmark before the selected menu item. This worked when first drawn, but the menus did not refresh when the data changed, so that was no use.&lt;/p&gt;
&lt;p&gt;I also tested using &lt;code&gt;Divider()&lt;/code&gt; to get a menu separator. It worked, but the look didn&#39;t match the standard dividers. So menus need a lot of work still...&lt;/p&gt;
&lt;p&gt;I kept the technique of having menu items publish notifications that other views could pick up. This seems to work well. I would have liked to have certain menus or menu items only appear conditionally or perhaps enable/disable conditionally, but I could not see how to do that. Since menus do not appear to update when data changes, this may not be possible right now.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Preferences&lt;/h3&gt;
&lt;p&gt;There was one place where the new app architecture was amazingly good and that was to do with preferences. In my previous attempt at this app, I had a Preferences window and it was a major exercise to stop it opening more than once. This time, it was super easy. In the App.swift file, beside the &lt;code&gt;WindowGroup&lt;/code&gt;, I added &lt;code&gt;Settings&lt;/code&gt; which contained a view which had the UI for my app&#39;s preferences.&lt;/p&gt;
&lt;p&gt;This caused a few errors at first, but I added a &lt;code&gt;@SceneBuilder&lt;/code&gt; property wrapper to the App&#39;s body and that fixed the errors.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@main&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI_Mac_11App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@SceneBuilder&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Scene&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;WindowGroup&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Settings&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;SettingsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These three lines of code added a Preferences menu item and hooked it up to my &lt;code&gt;SettingsView&lt;/code&gt;. This view opens in a new window when the Preferences menu item or keyboard shortcut is used and only one copy of this window ever appears.&lt;/p&gt;
&lt;p&gt;Then on to the second brilliant thing about preferences: &lt;code&gt;@AppStorage&lt;/code&gt;. In the previous version, I used a custom property wrapper to wrap settings that I wanted saved into UserDefaults. This is now built into SwiftUI and this was all I had to do:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@AppStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;showCopyright&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showCopyright&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now any time that variable was accessed, it was read from UserDefaults and every time it was changed, it was written back into UserDefaults. Instant preferences in a single line!&lt;/p&gt;
&lt;p&gt;To pass this setting to a different view, all I had to do was give that view this same declaration. And it just worked!&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Opening Another Window&lt;/h3&gt;
&lt;p&gt;Last time, I tried lots of different ways using hosting controllers, storyboards etc. to open secondary windows. This time I decided to stick to using &lt;code&gt;sheet&lt;/code&gt; since it seems to be what works best for SwiftUI apps. So I added my User Interface Elements sample window as a sheet.&lt;/p&gt;
&lt;p&gt;The view itself was reasonably similar except that last time, I used an &lt;code&gt;NSViewRepresentable&lt;/code&gt; to embed an AppKit color well into the view. This time I was able to use the new SwiftUI ColorPicker which made it much easier to get the color back into the main view. I also added the new date and time pickers.&lt;/p&gt;
&lt;p&gt;The color picker worked really well except that there is an option to specify whether the picker should support opacity or not. I found that no matter what I chose here, I never got the option to chose a color with opacity.&lt;/p&gt;
&lt;p&gt;The date picker was good. It showed the date numerically using my local format - DD/MM/YYY. Clicking on any of the elements popped up a calendar view. The time picker was not so easy to use. Again, it showed the time in my format e.g. 7:53 pm. But there was no graphical entry, you click on an element (hour, minute or am/pm) and type. This would be OK except that every time you type, the focus jumps back to the hour. I found this confusing.&lt;/p&gt;
&lt;p&gt;One big improvement is the ability to apply a keyboard shortcut to a button. When I set the keyboard shortcut for the Close button to &lt;code&gt;.defaultAction&lt;/code&gt;, the button got the default appearance and was triggered by pressing the Return key.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Close&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyboardShortcut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultAction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was still unable to work out how to set the focus to a TextField. There are two new property wrappers to do with focus, but I understand they are not yet operational.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Dialogs&lt;/h3&gt;
&lt;p&gt;I have already used a sheet to display the UI elements and ActionSheets are not available on macOS, so that just leaves Alerts and file dialogs. File dialogs work identically in the app although they look quite different on Big Sur. Alerts look quite different but I think they are nice. Since the app icon is a prominent part of the dialog, I added one to make it look good. I understand that different numbers of buttons will get arranged in different ways.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-mac-11-alert.png&quot; alt=&quot;Alert dialog&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Other Improvements&lt;/h3&gt;
&lt;h4&gt;Self&lt;/h4&gt;
&lt;p&gt;Do a search for &amp;quot;self.&amp;quot; and get rid of them all. Your code will be much cleaner and easier to read and SwiftUI now understands what you mean. If you want to read more details about this change, check out &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0269-implicit-self-explicit-capture.md&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;swift-evolution proposal SE-0269&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Navigation Titles&lt;/h4&gt;
&lt;p&gt;iOS had the concept of setting a navigation title but it didn&#39;t really work in macOS. Now you can set the &lt;code&gt;navigationTitle&lt;/code&gt; of a view and this will be the window title.&lt;/p&gt;
&lt;h4&gt;Toolbars&lt;/h4&gt;
&lt;p&gt;This was an aspect of macOS apps that I referred to but didn&#39;t explore last time mainly because I assumed it would be done through the storyboard, just as with an AppKit app. Now we can add a &lt;code&gt;.toolbar&lt;/code&gt; modifier to a view and have it include &lt;code&gt;ToolbarItems&lt;/code&gt;. For the first time, we can now use &lt;a href=&quot;https://developer.apple.com/sf-symbols/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SF Symbols&lt;/a&gt; in a macOS app, so that made it easy to add good looking toolbar items.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-mac-11-toolbar.png&quot; alt=&quot;Toolbar and navigation title&quot;&gt;&lt;/p&gt;
&lt;p&gt;I would like to vary the toolbar according to what is being displayed, and the toolbar is also used to show the navigation title, but perhaps that is a topic for another day. There is certainly a lot to explore there.&lt;/p&gt;
&lt;h4&gt;Pyramid of Doom&lt;/h4&gt;
&lt;p&gt;With menus and toolbars, there is a heap of extra code added to certain views. I would love to be able to extract that into its own function or view modifier. I was able to extract the toolbar, but I am not sure how to do this with menus yet. I imagine it will need some specific sort of ViewBuilder but I think this is very necessary. Perhaps the topic for yet another article...&lt;/p&gt;
&lt;p&gt;The project is available on &lt;a href=&quot;https://github.com/trozware/swiftui-mac-11&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. As always, if you have any comments, suggestions or ideas, I would love to hear from you. Please contact me using one of the links below or through my &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page. Thanks for reading.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://twitter.com/kb091412&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sungbin Jo (조성빈)&lt;/a&gt; for proof-reading and suggestions.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI Snapshot Testing</title>
    <link href="https://troz.net/post/2020/swiftui_snapshots/" />
    <updated>2020-05-17T08:50:09Z</updated>
    <id>https://troz.net/post/2020/swiftui_snapshots/</id>
    <content type="html">&lt;p&gt;Snapshot testing is a technique that has been very popular in the web development world and it seems like a great way to test SwiftUI user interfaces. I read about snapshot tests in a recent blog post and was intrigued, but I had some difficulty getting it to work, so when I finally succeeded, I decided to share my experiences in the hope that others will find them useful.&lt;/p&gt;

&lt;hr&gt;
&lt;h2&gt;What is Snapshot Testing&lt;/h2&gt;
&lt;p&gt;Unit testing checks that when you call various functions or methods with certain inputs, you get the output you expect. I use unit tests for testing my models and the methods that change them. But this only tests the logic behind the app, it does nothing to test whether the app is displaying what it should, or whether it is responding correctly to the user&#39;s actions.&lt;/p&gt;
&lt;p&gt;UI testing emulates user actions by faking taps, clicks, text entry and so on and checks that labels, buttons etc are showing the correct information after these fake interactions.&lt;/p&gt;
&lt;p&gt;Snapshot testing is in between these two as it effectively takes a picture of the interface. The first time you run the test it will store an image and all subsequent test runs will check that the current interface matches this stored image. If there are any differences, the test will fail so you can decide whether to keep the new version or revert to what you had before.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;How did I get started?&lt;/h2&gt;
&lt;p&gt;I first read about the idea of using snapshot testing for SwiftUI in a blog post by &lt;a href=&quot;https://www.vadimbulavin.com/snapshot-testing-swiftui-views/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Vadim Bulavin&lt;/a&gt;. He made a very good argument for this, but I found his instructions assumed more knowledge of the topic than I had at the time and so I discarded the idea after an initial attempt.&lt;/p&gt;
&lt;p&gt;But he was suggesting using a &lt;a href=&quot;https://github.com/pointfreeco/swift-snapshot-testing&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;snapshotting library&lt;/a&gt; published by &lt;a href=&quot;https://www.pointfree.co&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Point-Free&lt;/a&gt; and I later discovered a link to one of their videos where they discuss this exact thing: &lt;a href=&quot;https://www.pointfree.co/episodes/ep86-swiftui-snapshot-testing&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftUI Snapshot Testing&lt;/a&gt;. This was enough to get me going with attempt #2.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Setting up an app for snapshot testing&lt;/h2&gt;
&lt;p&gt;Since the blog post and video were talking about iOS apps, I decided to start there, but you know me, I will get to macOS apps later...&lt;/p&gt;
&lt;p&gt;First off, I created a single view iOS app using SwiftUI making sure to check &amp;quot;Include Unit Tests&amp;quot;, but not &amp;quot;Include UI Tests&amp;quot;. I created a simple view so I had something to test.&lt;/p&gt;
&lt;p&gt;If you want to use this on an app that does not already have a unit tests target, go to the project settings, click the + button to add a new target and choose a Unit Testing Bundle.&lt;/p&gt;
&lt;p&gt;Next step was to import the snapshot testing library using Swift Package Manager. Go to File &amp;gt; Swift Packages &amp;gt; Add Package Dependency. Paste in the URL below and click Next.&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://github.com/pointfreeco/swift-snapshot-testing&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I accepted the default versioning suggestions on the next pane.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Snapshot-AddPackage.png&quot; alt=&quot;Adding the snapshot package&quot;&gt;&lt;/p&gt;
&lt;p&gt;On the final pane, it is important to select the correct target for this package. Select the app&#39;s test target, not the app itself and not the UI test target if you have one. I made this mistake on my first try as I assumed that snapshot testing would be part of UI testing but it is actually part of unit testing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Snapshot-SetTarget.png&quot; alt=&quot;Setting the package target&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Writing a Snapshot Test&lt;/h2&gt;
&lt;p&gt;Now I added a new Unit Test Case Class file to the tests target in my app. I had to import SwiftUI and SnapshotTesting into this test file as well as declaring the app as a testable import. The easiest way to do this is to copy the &lt;code&gt;@testable import&lt;/code&gt; heading from the sample test file to make sure it is exactly right. The import needs to match the name of your app module.&lt;/p&gt;
&lt;p&gt;Finally it&#39;s time to write the first snapshot test:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCTest&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SnapshotTesting&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token attribute atrule&quot;&gt;@testable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Snapshots&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SnapshotsTests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCTestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testDefaultAppearance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses the snapshot library&#39;s &lt;code&gt;assertSnapshot&lt;/code&gt; method to save the content view as an image. But unfortunately, this doesn&#39;t work. The problem is that the second parameter is a Snapshotting strategy that can convert various UI elements into some form of data or image. But the library doesn&#39;t know what a SwiftUI View is, so it needs a way to convert the view into something that can be recognized by the snapshotter.&lt;/p&gt;
&lt;p&gt;I added this extension to SwiftUI&#39;s View that wraps the SwiftUI View in a UIHostingController. It returns this as a UIViewController which is a valid input for a snapshotter and can be converted to an image.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; vc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIHostingController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rootView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        vc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frame &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bounds
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; vc
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now my first test became:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testDefaultAppearance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it worked. Or rather it failed as expected because there was no image to compare it with.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/images/2020/Snapshot-Test1Result.png&quot;&gt;&lt;img src=&quot;https://troz.net/images/2020/Snapshot-Test1Result.png&quot; alt=&quot;Test result&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Checking the error message, I was able to see where it had created the snapshot image file which I could look at. And the second time I ran the test, it passed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Snapshot-TestImage1.png&quot; alt=&quot;Test image in Finder&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you ever get an error message saying &amp;quot;No such module &#39;SnapshotTesting&#39;&amp;quot; use Shift-Command-U to re-build for testing. This usually only happens after you have cleaned your build folder.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;Testing a Change&lt;/h2&gt;
&lt;p&gt;Now that I had a passing test, the next thing was to check what happens if the UI changes. This may be due to a deliberate change or because the cat just walked across your keyboard (a not infrequent occurrence around here).&lt;/p&gt;
&lt;p&gt;Where I originally had a button with the label &amp;quot;Save&amp;quot;, I decided to change this to &amp;quot;OK&amp;quot; (rejecting the cat&#39;s suggestion of &amp;quot;q2eegrnh&amp;quot;).&lt;/p&gt;
&lt;p&gt;Running the test again produced this result:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/images/2020/Snapshot-FailedTest.png&quot;&gt;&lt;img src=&quot;https://troz.net/images/2020/Snapshot-FailedTest.png&quot; alt=&quot;Failed snapshot test&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And I was then able to compare the 2 images, using the path to the failing image from the error message.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Snapshot-TestResults.png&quot; alt=&quot;Test results: success and failure&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once I had confirmed that the new image was what I wanted and not a result of error, either feline or human, I set the test to &lt;code&gt;record&lt;/code&gt; a new result so that the new version became the official test version.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testDefaultAppearance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        record &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This caused a failing test again as the new version was written to the Snapshots folder, but after removing the &lt;code&gt;record = true&lt;/code&gt; line and re-running the test, it passed again, with my new button label now an accepted part of the test.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Using Snapshots with State&lt;/h2&gt;
&lt;p&gt;In SwiftUI, the UI displayed is a function of state, so changing state properties changes the UI. This is what makes snapshot testing really good for SwiftUI apps as you can change the state programmatically and confirm that this is reflected in the UI.&lt;/p&gt;
&lt;p&gt;So having proved that the snapshot tests worked, I decided to move on and test it with my new &lt;a href=&quot;https://troz.net/post/2020/name_this_app/&quot;&gt;anagram assistant app&lt;/a&gt;. This is quite a simple app that has a single &lt;code&gt;AppState&lt;/code&gt; class that holds all the program data.&lt;/p&gt;
&lt;p&gt;So I was able to write a suite of tests that changed the state in various ways and then snap-shotted the UI with that state. Here are a couple of examples:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testEmptyContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testAfterLocking&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; appState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sampleState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        appState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;availableLetters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        appState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedLetterIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
        appState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;placeSelectedLetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;at&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        appState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggleLockedState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        appState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;availableLetters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; appState
        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked really well with only one slight problem. As the state arranges the &lt;code&gt;availableLetters&lt;/code&gt; array randomly for display, I had to add a &lt;code&gt;sort&lt;/code&gt; to make sure they always displayed in the same order and made the tests repeatable.&lt;/p&gt;
&lt;p&gt;And as a bonus, I was able to test a screen in dark mode with this test which sets the &lt;code&gt;colorScheme&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testDarkMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sampleAppState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;colorScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Accessibility Tests&lt;/h2&gt;
&lt;p&gt;iOS supports dynamic type and if your app uses standard font styles, it will adopt these dynamic sizes automatically. I can&#39;t find the link right now, but I remember reading an article that said nearly half of all iPhone users change the default text size, setting it either smaller or larger.&lt;/p&gt;
&lt;p&gt;With snapshot testing, it is quick and easy to get a view of how you app looks with different font sizes. Here is my test function for taking a snapshot of every possible font size variation.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testDynamicFonts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sampleAppState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; contentSize &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentSizeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allCases &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contentView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sizeCategory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contentSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                           named&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;contentSize&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the settings screen, I decided that smaller fonts were not a problem, but I wanted to check the two largest options, so I used this test function:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testSettingsScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; settingsView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SettingsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; settingsView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; settingsView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sizeCategory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentSizeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accessibilityExtraExtraExtraLarge
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            named&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AccessibilityXXXL&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;assertSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            matching&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; settingsView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sizeCategory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentSizeCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;extraExtraExtraLarge
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            named&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;XXXL&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Snapshot-DynamicFonts.png&quot; alt=&quot;Dynamic font tests&quot;&gt;&lt;/p&gt;
&lt;p&gt;This let me quickly see where the problems were and what I needed to adjust.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Snapshot Test for Mac Apps&lt;/h2&gt;
&lt;p&gt;You knew you weren&#39;t going to get through this without me going on about Mac apps...&lt;/p&gt;
&lt;p&gt;Snapshot tests for a Mac app work well, with one caveat.&lt;/p&gt;
&lt;p&gt;First I had to change the Swift.View extension so that it returned an NSViewController instead of a UIViewController.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;toVC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSViewController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; vc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSHostingController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rootView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        vc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frame &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;768&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; vc
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I chose an arbitrary size for the snapshot, you just need to make sure your UI will fit into whatever size you select.&lt;/p&gt;
&lt;p&gt;The real problem was with sand-boxing. The snapshot library was blocked from writing the image files to the project directory if the app was sand-boxed. This seems really peculiar, since Xcode is running the tests and Xcode writes to the project directory all the time!&lt;/p&gt;
&lt;p&gt;I found two ways around this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Turn off sand-box mode temporarily while testing.&lt;/li&gt;
&lt;li&gt;Make a non-sand-boxed target and use it for testing against.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Neither of these are particularly great. Option 1 is tedious, although I think it can work if the snapshots remain the same, it only fails if there is a change that it needs to write to disk.&lt;/p&gt;
&lt;p&gt;Option 2 is tedious to set up (&lt;a href=&quot;https://troz.net/contact/&quot;&gt;contact me&lt;/a&gt; if you would like more details) but is more seamless after that.&lt;/p&gt;
&lt;p&gt;The best solution would be for Xcode to allow you to turn off sand-boxing for a test target. Maybe Xcode 12...&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Limitations of Snapshot Testing&lt;/h2&gt;
&lt;p&gt;Ignoring the Mac and concentrating only on iOS apps for the moment, there were a few issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You have to run your tests against the same simulator every time, or at least against a simulator with the same screen dimensions. I decided to to use the iPhone SE (2nd generation) as it has a small screen and I find smaller screens to be more of a problem than large ones. You also need to make sure it is always using the same appearance: light or dark, unless you want to specify this for every test.&lt;/p&gt;
&lt;p&gt;I ended up with this setup function that ran before my snapshot test suite:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; device &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIDevice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; device &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;iPhone SE (2nd generation)&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;fatalError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Switch to using iPhone SE (2nd generation) for these tests.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAnimationsEnabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;windows&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;

        record &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses a couple of tricks that are supposed to speed up tests and has a &lt;code&gt;record&lt;/code&gt; setting that I could set for the entire suite if I wished, and it throws a &lt;code&gt;fatalError&lt;/code&gt; if I select the wrong device or simulator.&lt;/p&gt;
&lt;p&gt;It would be neater if Xcode allowed you to select a simulator in the test target build settings, but I think you can only do this if you run tests from the command line.&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;Snapshot tests confirm that the UI matches the state, but they do not check to see if the state changes in response to user input. That is the missing link that UI testing provides, but even without that, I believe that snapshot testing is a very useful tool and much better than having no form of UI testing at all.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You need to look at your snapshots. This may sound obvious but the snapshot library creates a set of images. These images are then set as the goal for future tests. If you don&#39;t check that they are correct, then every test could be confirming that the UI is wrong but unchanged. If the tests report a difference, look at both copies and see which one is right. For the same reason, the snapshot images need to be included in your version control repository.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Will I use snapshot tests for my SwiftUI apps? Yes, definitely. I use unit tests for my model classes but mostly avoid UI tests as they are too clumsy to write and time-consuming to run. Snapshot tests are better for SwiftUI, and very fast.&lt;/p&gt;
&lt;p&gt;Huge thanks to &lt;a href=&quot;https://twitter.com/V8tr&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Vadim Bualvin&lt;/a&gt; for the original inspiration for this article. Go and read his &lt;a href=&quot;https://www.vadimbulavin.com/snapshot-testing-swiftui-views/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;blog post&lt;/a&gt; for a more detailed look. And thanks to &lt;a href=&quot;https://www.twitter.com/mbrandonw&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Brandon Williams&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://www.twitter.com/stephencelis&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Stephen Celis&lt;/a&gt; at &lt;a href=&quot;https://www.pointfree.co&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Point-Free&lt;/a&gt; for getting me going after my initial discarding of the idea. Any mistakes or errors are mine and not theirs.&lt;/p&gt;
&lt;p&gt;If you want to learn about UI testing for SwiftUI apps, I recommend watching &lt;a href=&quot;https://twitter.com/azamsharp&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;azamsharp&#39;s&lt;/a&gt; YouTube video: &lt;a href=&quot;https://www.youtube.com/watch?v=dFQtFJsvt04&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;User Interface Testing for SwiftUI Applications&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As always, if you have any comments, suggestions or ideas, I would love to hear from you. Please contact me using one of the links below or through my &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Name This App</title>
    <link href="https://troz.net/post/2020/name_this_app/" />
    <updated>2020-05-09T01:26:21Z</updated>
    <id>https://troz.net/post/2020/name_this_app/</id>
    <content type="html">&lt;p&gt;My current work in progress is an iPhone app designed to make it easier to solve crossword anagrams by emulating and improving upon an ability that was there when we used to do crosswords on paper, but is missing for digital crosswords.&lt;/p&gt;
&lt;p&gt;But I cannot think of a clever name for the app, so please read the story and contact me with your name suggestions or if you would like to test the pre-release version of this app.&lt;/p&gt;

&lt;hr&gt;
&lt;h3&gt;Cryptic Crosswords&lt;/h3&gt;
&lt;p&gt;Programmers are basically puzzlers, and programming is the world&#39;s best type of puzzle, but I like to start my day with a cryptic crossword. Like programming, cryptic crossword clues have their own syntax and what seems like gibberish on a first glance, can be parsed into logical parts to take you to the answer. But there are aspects of solving crosswords that were easier with pencil and paper, so I decided to write an app to help fill one of those gaps.&lt;/p&gt;
&lt;p&gt;My crossword of choice is the &lt;a href=&quot;https://www.irishtimes.com/games/crosswords/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Crosaire&lt;/a&gt; crossword from &lt;a href=&quot;https://www.irishtimes.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;The Irish Times&lt;/a&gt;. A lot of newspapers have a cryptic crossword and most of them need a subscription, but the Irish Times has a crossword only subscription that I really like. Crosswords also tend to require some local knowledge, but for the Crosaire, if you remember that the Abbey and the Gate are both theatres and that Down is a county, you will have most of what you need. And as an added bonus, the crossword creator later blogs the solutions with explanations so you can learn how the clues were constructed if you are totally mystified.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Anagrams&lt;/h3&gt;
&lt;p&gt;Anagrams are a very popular clue type but they can be tricky to solve because our brains are too good at discerning patterns. We can recognize words using only the first and last letters if they have the correct number of letters inside. This means that asking our brains to scramble these letters into a completely different word is very difficult. In the olden days, when I used to do crosswords in the newspaper, the best method was to find a space in the margin and write the letters out in a circle which forced my brain to reconsider the options.&lt;/p&gt;
&lt;p&gt;Here is an example from a recent Crosaire:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Users rip off revelation (8)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The word &amp;quot;off&amp;quot; is a hint that this is an anagram, &amp;quot;Users rip&amp;quot; has the right number of letters, so the overall clue must be &amp;quot;revelation&amp;quot;. This is not a very long anagram and I think having two words makes it easier, but lets take a look at how I would solve this the old-fashioned way. (And I knew the &amp;quot;I&amp;quot; was the third last letter.)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/Anagram1.gif&quot; alt=&quot;Anagram solving manually&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you watch this gif and then look at it when it stops repeating, hopefully you will be able to see that the answer is &amp;quot;SURPRISE&amp;quot;. Refresh the page if you want to start the gif looping again.&lt;/p&gt;
&lt;p&gt;But I do crosswords on my iPad now and while I could use a notes app and my Apple Pencil to draw the letters out like I did in the GIF above, I don&#39;t always have the Pencil with me and I want to keep looking at the crossword. So I decided that a companion iPhone app was the way that would work best for me.&lt;/p&gt;
&lt;p&gt;There are sites and apps that offer anagram solvers, but they are just looking through a dictionary and showing you all the possibilities. To me, this feels like cheating, but I don&#39;t have a problem with using an app to emulate the techniques I used to use with pencil and paper.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;App Design&lt;/h3&gt;
&lt;p&gt;So I planned out the basic features of the app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enter a word&lt;/li&gt;
&lt;li&gt;display the letters in a circle&lt;/li&gt;
&lt;li&gt;shuffle these letters if needed&lt;/li&gt;
&lt;li&gt;place letters in a possible solution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Arranging the letters in a circle was made relatively easy by using SwiftUI. I placed the letters in a ZStack so they were all piled on the one spot. Then I calculated the X &amp;amp; Y offsets needed to move each letter to the edge of a circle. This involving digging out some long-forgotten trigonometry to convert from polar coordinates to rectangular coordinates. I could calculate the radius of the letter circle based on the screen size and the angle between each letter was 360 degrees divided by the number of letters. So some quick calculations converted these two numbers into X &amp;amp; Y coordinates for each letter, and then I had my circle.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; radiansBetweenLetters&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; degrees &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360.0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anagramLetters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; radians &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; degrees &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pi &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;180&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; radians
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; radiusForCircle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; screenWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bounds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screenWidth &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.35&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;XoffsetForLetterAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// x = R cos t&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; totalRadians &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; radiansBetweenLetters &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; radiusForCircle &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;totalRadians&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;YoffsetForLetterAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// y = R sin t&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; totalRadians &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; radiansBetweenLetters &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; radiusForCircle &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;totalRadians&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The original word was converted into an array for display, so re-arranging the letters was a matter of calling &lt;code&gt;shuffle()&lt;/code&gt; on the array which automatically triggered a SwiftUI redraw.&lt;/p&gt;
&lt;p&gt;And here is version 1 of the interface running in the SwiftUI Canvas:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AnagramAssistv1.gif&quot; alt=&quot;Anagram app version 1&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Refresh the page to see the gif play, if it has stopped.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;A Different Type of Anagram&lt;/h3&gt;
&lt;p&gt;Now we come to a variation of the anagram clue that is very popular with the author of the Crosaire crossword. I don&#39;t know if there is an official name, but I call them &amp;quot;subtraction anagrams&amp;quot;.&lt;/p&gt;
&lt;p&gt;Here is an example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Creationist has no taste for what&#39;s sarcastic (6)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea here is to remove the letters in &amp;quot;taste&amp;quot; from the letters in &amp;quot;Creationist&amp;quot; and then unscramble the remaining letters to find a word that means &amp;quot;sarcastic&amp;quot;. My first attempt at this used a Trash can button that removed the letter and shortened the space for the solution, but then I decided to have a discard tray at the bottom of the screen to show what had been removed and allow replacing any letters discarded by mistake.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AnagramDiscard.png&quot; alt=&quot;Anagram discard tray&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;After this, I added a bit of style to make it look nicer and then moved on to the next feature. If I knew some of the cross letters already, I wanted to be able to place them in the solution squares and then lock them in place. This would allow me to shuffle in letters to test and remove all the unlocked ones with a button click. So here is what the app looked like after those changes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AnagramLocks.mp4&quot; alt=&quot;Anagram with locked letters&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Refresh the page to see the gif play, if it has stopped.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Drag &amp;amp; Drop&lt;/h3&gt;
&lt;p&gt;My next experiment was with drag &amp;amp; drop to place or discard letters. I waited until iOS 13.4 brought drag &amp;amp; drop to SwiftUI but I was not happy with the result. Firstly, there is a big lag on dragging. You have to hold your finger down on a letter for quite a while before it becomes draggable. This was so counter-intuitive that I thought it wasn&#39;t working, but occasionally it did work and so I eventually deduced that it was a timing issue.&lt;/p&gt;
&lt;p&gt;The second problem was solved thanks to a suggestion by &lt;a href=&quot;https://twitter.com/luisramos1337&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Luis Ramos&lt;/a&gt; on &lt;a href=&quot;https://twitter.com/trozware/status/1254568520295124992&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt;. When I dragged a letter, the drag preview image was the shuffle button from the middle of the ZStack instead of the selected letter. This was solved by moving the &lt;code&gt;onDrag&lt;/code&gt; modifier to BEFORE the &lt;code&gt;offset&lt;/code&gt; modifier (not AFTER like I said in my original response to Luis). But I was still not happy with it, so I decided to discard this idea and stick to tapping.&lt;/p&gt;
&lt;p&gt;There was one side-effect: I wasn&#39;t able to get the drag preview to be round - it was a square with opaque corners. As a work-around, I changed the letters to be inside squares and then decided this was a better look anyway, since it was more like what you see in a crossword.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Further UI Enhancements&lt;/h3&gt;
&lt;p&gt;I installed a custom font so that it looked more like hand-writing. I wrote about &lt;a href=&quot;https://troz.net/post/2020/custom-fonts/&quot;&gt;installing custom fonts&lt;/a&gt; in a separate article, so if you are interested check that out for details on the multiple steps that you need to take to get them to work. I went through a few different fonts until I found one that I thought worked well and clearly for all the upper case letters but I settled on &lt;a href=&quot;https://www.fontspace.com/search?q=oregano&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Oregano&lt;/a&gt;. And then I made the app work in dark mode too:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AnagramDarkMode.png&quot; alt=&quot;Custom font and dark mode&quot;&gt;&lt;/p&gt;
&lt;p&gt;The clue for this one is &lt;em&gt;&amp;quot;Consumables minus bacon and shellfish (6)&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Animations&lt;/h3&gt;
&lt;p&gt;SwiftUI has animation built in, so I was able to add an &lt;code&gt;animation&lt;/code&gt; modifier to the various views to make the transitions look smooth. I particularly liked the way the letters re-arranged themselves around the circle when I removed or replaced a letter.&lt;/p&gt;
&lt;p&gt;But to add something extra, I thought it would be fun if the letters all started in the middle and moved outwards to their places on the circle. The way I did this was by using a Boolean to store whether the letters had had their first draw. If not, the radius of the circle was zero, so they all piled in the middle. After a delay, this Boolean was toggled so the radius was changed to the full size and the animation modifier already applied, made the letters smoothly expand outwards.&lt;/p&gt;
&lt;p&gt;For the letter shuffle, I did the reverse, moving the letters in, shuffling, then moving them out again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AnagramAnimation.mp4&quot; alt=&quot;Animations&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Refresh the page to see the gif play, if it has stopped.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I also added sound effects and a slide-up sheet view with settings and help. I enjoyed using &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Apple&#39;s SF Symbols&lt;/a&gt; to show icons for each paragraph of the help, although I may change the colour of these icons as they look at bit too much like buttons right now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AnagramHelp.png&quot; alt=&quot;Help &amp;amp; Settings&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;Well, I am hoping that someone will come up with a good name. I thought of &amp;quot;Anagram Assistant&amp;quot; but that doesn&#39;t fit underneath the home screen icon and it was shortened to &amp;quot;Anagram&amp;quot;. I don&#39;t want people to see it and think it is just one of those dictionary lookup anagram finders, so I would like something more descriptive while still being short, or at least having a short variant.&lt;/p&gt;
&lt;p&gt;In honour of P.G.Wodehouse&#39;s favourite crossword clue, I thought of calling it &amp;quot;E blank U&amp;quot; but that isn&#39;t an anagram, so it doesn&#39;t really work. For people not familiar with that reference, Wodehouse&#39;s less quick-witted characters always get stuck on &amp;quot;Large flightless Australian bird, 3 letters. E blank U&amp;quot;.&lt;/p&gt;
&lt;p&gt;A clever name would be good - perhaps an anagram itself? If you think of anything, please let me know on &lt;a href=&quot;https://twitter.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt;, by using the email link below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact page&lt;/a&gt;. The namer will get full credit in the app. And use one of those methods to contact me if you would like to test the app before it is released.&lt;/p&gt;
&lt;p&gt;Once I have a name, I can get to work on the usual secondary tasks which take so much time: App Store set up, screen shots, a support page and so on.&lt;/p&gt;
&lt;p&gt;I haven&#39;t tried an iPad version yet. I got stung once by testing an iPad version of an app, deciding it didn&#39;t work well and then running into Apple&#39;s ban on dropping a platform from a published app. This has discouraged me from iPad apps ever since, which I doubt was their intention.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;And if you are curious about the solutions to the clues I used in the examples:&lt;/p&gt;
&lt;p&gt;Users rip off revelation (8) = SURPRISE&lt;br&gt;
Creationist has no taste for what&#39;s sarcastic (6) = IRONIC&lt;br&gt;
Consumables minus bacon and shellfish (6) = MUSSEL&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI Color</title>
    <link href="https://troz.net/post/2020/swiftui-color/" />
    <updated>2020-05-09T01:26:08Z</updated>
    <id>https://troz.net/post/2020/swiftui-color/</id>
    <content type="html">&lt;p&gt;As developers, we are used to thinking of color as a numeric way to specify a particular tint. But in SwiftUI, &lt;code&gt;Color&lt;/code&gt; - like almost everything else - is actually a &lt;code&gt;View&lt;/code&gt; in its own right. This leads us to two very interesting questions: how do we use a view to specify a color and how can we use the fact that &lt;code&gt;Color&lt;/code&gt; is &lt;code&gt;View&lt;/code&gt;?&lt;/p&gt;

&lt;hr&gt;
&lt;h2&gt;Specifying a Color&lt;/h2&gt;
&lt;p&gt;To start my investigations of &lt;code&gt;Color&lt;/code&gt;, I created a new iOS single view app in Xcode. Regular followers will know that I am a passionate advocate of macOS programming, but when experimenting with SwiftUI, I prefer to use an iPhone app as the preview canvas fits very neatly into the main Xcode window.&lt;/p&gt;
&lt;p&gt;The default &lt;code&gt;ContentView&lt;/code&gt; contains a single &lt;code&gt;Text&lt;/code&gt; view. To change the color of the text in this view, I added a modifier:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;foregroundColor&lt;/code&gt; modifier expects a &lt;code&gt;Color&lt;/code&gt; so there was no need to tell it that the parameter is a &lt;code&gt;Color&lt;/code&gt; and I was able to use the short way of describing one of the standard colors.&lt;/p&gt;
&lt;p&gt;Adding a background color is a bit different because a background can be any view, not just a color. So the modifier needs to specify that this is a &lt;code&gt;Color&lt;/code&gt; view, as well as setting the actual color.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yellow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;I added the padding to make the overall size bigger so that the background color was more obvious.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Command-click on &lt;code&gt;.red&lt;/code&gt; or &lt;code&gt;.yellow&lt;/code&gt; in the code and select &amp;quot;Jump to Definition&amp;quot;. This lists the available pre-defined colors. As well a set of basic colors and a clear option, there are two extra entries: primary &amp;amp; secondary. And if you scroll back up the page a bit, you will see another one: accentColor.&lt;/p&gt;
&lt;p&gt;Choosing one of the preset options is the easiest way to select a color. But try this:&lt;/p&gt;
&lt;p&gt;In your code, type &lt;code&gt;let uiColor = UIColor.&lt;/code&gt; and have a look at the auto-complete suggestions that appear after you type the period. Scrolling past the various &lt;code&gt;init&lt;/code&gt; methods, you will see that &lt;code&gt;UIColor&lt;/code&gt; has a lot more options than &lt;code&gt;Color&lt;/code&gt;. There are a bunch of colors, a section of system colors and a lot of semantic colors like placeholderText, secondarySystemBackground.&lt;/p&gt;
&lt;p&gt;Why does UIColor get these other useful looking options and Color does not?&lt;/p&gt;
&lt;p&gt;Well, I don&#39;t have an answer to that except that I hope they will appear over time. But in the meantime, it is easy enough to create a SwiftUI &lt;code&gt;Color&lt;/code&gt; from a &lt;code&gt;UIColor&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; backgroundColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondarySystemBackground&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;backgroundColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you know the RGB values of the color you want to use, it is possible to create a &lt;code&gt;Color&lt;/code&gt; directly by various methods like this one for setting the color using the RGB values.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; rgbColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; green&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But when you get to this stage, I strongly suggest that you start using color assets instead.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Using Color Assets&lt;/h2&gt;
&lt;p&gt;Go to Assets.xcassets and click the plus button at the bottom of the list of assets. Select &amp;quot;New Color Set&amp;quot; and you will get a new asset called &amp;quot;Color&amp;quot;. You can double-click the name to edit it to something that makes sense to you.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-colors-add-set.png&quot; alt=&quot;Add Color Set&quot;&gt;&lt;/p&gt;
&lt;p&gt;While naming it after the color may seem logical, I prefer to think about the use cases for this color and set the name to something like &amp;quot;cardBackground&amp;quot; or &amp;quot;alertText&amp;quot;.&lt;/p&gt;
&lt;p&gt;By default, your new color set will contains a single color block. Click in the color block itself and then you will be able to edit the color in the Attributes Inspector. If you click the &amp;quot;Content&amp;quot; popup, you will get access to all the system colors, and you can also select any color space and create a custom color using that color space. Click &amp;quot;Color Panel&amp;quot; to access a standard color picker if you need it. And change the input method to whatever suits the color information you have.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-colors-set.png&quot; alt=&quot;Edit Color Set&quot;&gt;&lt;/p&gt;
&lt;p&gt;Modern apps need to be able to handle light and dark modes, and this is where using a color set really shines. Set the color to what you want to use for light mode and then choose &amp;quot;Any, Dark&amp;quot; from the Appearances popup menu in the Attributes Inspector.&lt;/p&gt;
&lt;p&gt;Now your color set has two blocks and you can change the Dark one to whatever color this should be in dark mode. This feature of Color Sets is a very strong argument for using them instead of defining colors using their RGB values.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-colors-multi-set.png&quot; alt=&quot;Light &amp;amp; Dark Color Set&quot;&gt;&lt;/p&gt;
&lt;p&gt;And now for the really neat part. To use any of these color sets in your code, add a modifier like &lt;code&gt;foregroundColor&lt;/code&gt; just the same as usual, but when the placeholder for the color is selected, press Shift-Command-L to bring up the Library palette and choose the Color icon at the right. You will see all your color sets there and you can insert them easily and accurately.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-colors-use-color-set.webp&quot; alt=&quot;Using a Color Set&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here is the code for my &lt;code&gt;AlertView&lt;/code&gt; using two color sets.&lt;/p&gt;
&lt;p&gt;Some people may object to using &amp;quot;magic strings&amp;quot; to specify the color sets but I feel that so long as the Library is used to insert the color names automatically, there is very little chance of error. But you could always make an enum or struct of color name constants and use these instead.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AlertView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; alertText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong!&quot;&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alertText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondarySystemBackground&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cornerRadius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;alertShadow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; radius&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;alertText&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AlertView_Previews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreviewProvider&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; previews&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;AlertView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preferredColorScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;light&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;AlertView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preferredColorScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;previewLayout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sizeThatFits&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is a composite image showing the two versions of the alert with the colors specified by two color sets:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-colors-alerts.png&quot; alt=&quot;Alerts Using Color Set&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Light &amp;amp; Dark Mode System Colors&lt;/h2&gt;
&lt;p&gt;Color&#39;s preset colors adapt automatically to the environment so that they work with dark mode or light mode just like your own Color Sets can.&lt;/p&gt;
&lt;p&gt;I wrote a test view to loop through all the preset colors (except clear) and display their descriptions in their own color.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; standardColors&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;black&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gray&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;green&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;orange&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yellow&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pink&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;purple&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;primary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secondary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accentColor
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;standardColors&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView_Previews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreviewProvider&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; previews&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preferredColorScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;light&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preferredColorScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The canvas shows two previews: one for light mode and one for dark mode.&lt;/p&gt;
&lt;p&gt;If you have a color picker app (I use &lt;a href=&quot;https://github.com/Toinane/colorpicker/releases/tag/2.0.3&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Colorpicker&lt;/a&gt;), you can detect the RGB values in the two previews. I made the font bold to make this easier. Red is &lt;code&gt;rgb(232, 77, 61)&lt;/code&gt; in light mode but &lt;code&gt;rgb(233, 85, 69)&lt;/code&gt; in dark mode. And the others have similar modifications to make them look their best in each environment.&lt;/p&gt;
&lt;p&gt;Take particular note of the three at the bottom of the list: primary, secondary and accentColor. These are incredibly useful when making a layout that works in both light and dark modes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/swiftui-colors-light-dark.png&quot; alt=&quot;Color previews&quot;&gt;&lt;/p&gt;
&lt;p&gt;When choosing a UIColor or NSColor, it is best to use the system versions, so &lt;code&gt;.systemBlue&lt;/code&gt; instead of &lt;code&gt;.blue&lt;/code&gt; as this gives the dark/light variants that we just saw. And the other semantic colors like &lt;code&gt;UIColor.secondarySystemBackground&lt;/code&gt; also have dark/light variants automatically.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Color as a View&lt;/h3&gt;
&lt;p&gt;Now let&#39;s consider the effect of &lt;code&gt;Color&lt;/code&gt; being a view and not just a description of a tint. Imagine you wanted to draw a colored box on the screen. Using UIKit, you would probably think of using a &lt;code&gt;UIView&lt;/code&gt;. With AppKit, maybe an &lt;code&gt;NSBox&lt;/code&gt;. Or perhaps you would have jumped straight to a Bezier path.&lt;/p&gt;
&lt;p&gt;Create a new SwiftUI view and replace the default &lt;code&gt;Text&lt;/code&gt; with a &lt;code&gt;Color&lt;/code&gt;, like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BoxView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resume the canvas preview and you will see the color fill the preview iPhone screen, except for the safe areas top and bottom.&lt;/p&gt;
&lt;p&gt;To make this into a box, add a &lt;code&gt;frame&lt;/code&gt; modifier with the required height and width. And now you have a box, far more easily than you could in either UIKit or AppKit.&lt;/p&gt;
&lt;p&gt;To be honest, in SwiftUI, you would probably use a &lt;code&gt;Rectangle&lt;/code&gt; instead of a plain &lt;code&gt;Color&lt;/code&gt; if you wanted a box of a set size but it is interesting to consider how &lt;code&gt;Color&lt;/code&gt; could be made to work.&lt;/p&gt;
&lt;p&gt;A more valuable use of &lt;code&gt;Color&lt;/code&gt; is to set the background.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BackgroundView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ZStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;green

            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, green background&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes the entire background green and shows the text centered on it.&lt;/p&gt;
&lt;p&gt;And to make the color extend into the safe areas:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;green&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;edgesIgnoringSafeArea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I prefer to put the &lt;code&gt;edgesIgnoringSafeArea&lt;/code&gt; modifier on the &lt;code&gt;Color&lt;/code&gt; only and not on the entire &lt;code&gt;ZStack&lt;/code&gt;. That way the other contents of the &lt;code&gt;ZStack&lt;/code&gt; will all stay inside the safe area.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So in summary, use preset colors or color assets to set your colors. And use the &lt;code&gt;Color&lt;/code&gt; view to set a background color for a view.&lt;/p&gt;
&lt;p&gt;I hope you found this article useful, and if you have any suggestions, corrections or improvements, please contact me using one of the links below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Custom Fonts in iOS &amp; macOS apps</title>
    <link href="https://troz.net/post/2020/custom-fonts/" />
    <updated>2020-04-01T00:27:59Z</updated>
    <id>https://troz.net/post/2020/custom-fonts/</id>
    <content type="html">&lt;p&gt;As a rule, I prefer to use fonts that come pre-installed with the system. That means that your interface is already familiar to users, you get dynamic font sizing and if Apple updates the fonts, you get the updates without doing anything.&lt;/p&gt;
&lt;p&gt;But sometimes, you really need to use a different font in your apps, and as the process of getting a custom font to display in your app can be confusing and tedious, I thought I would go through the steps for both iOS and macOS apps.&lt;/p&gt;
&lt;p&gt;I probably should have published this on a different day, but it is not a joke.... really.&lt;/p&gt;

&lt;hr&gt;
&lt;h2&gt;Getting a Font File&lt;/h2&gt;
&lt;p&gt;The first thing is to find a suitable font and make sure that the license for that font matches your use. I wanted a font that looked like it was hand-written, so I went to fontspace and searched the &lt;a href=&quot;https://www.fontspace.com/category/handwriting&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;hand-writing category&lt;/a&gt; there.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/SelectFont1.png&quot; alt=&quot;Fonts&quot;&gt;&lt;/p&gt;
&lt;p&gt;Since I wanted to use this in a paid app, I checked the &amp;quot;Commercial-use&amp;quot; button to limit the search. The one I chose was called &amp;quot;&lt;a href=&quot;https://www.fontspace.com/get/family/4yyjn&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Painting With Chocolate&lt;/a&gt;&amp;quot; by &amp;quot;Make mooze&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/SelectFont2.png&quot; alt=&quot;Download font&quot;&gt;&lt;/p&gt;
&lt;p&gt;I downloaded the font and un-zipped the file which gave me a single file called &amp;quot;Paintingwithchocolate-K5mo.ttf&amp;quot;. You can use either &amp;quot;.ttf&amp;quot; or &amp;quot;.otf&amp;quot; font files.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Using the Font in an iOS App&lt;/h2&gt;
&lt;p&gt;Now to use the font, first in an iOS app. Boot up Xcode and create a new SwiftUI project for iOS.&lt;/p&gt;
&lt;p&gt;Once the project is open, drag the font file into the Project Navigator. At this stage there are 2 crucial settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check &amp;quot;Copy files if needed&amp;quot;.&lt;/li&gt;
&lt;li&gt;Check your app in the &amp;quot;Add to targets&amp;quot; section.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AddFontToProject.png&quot; alt=&quot;Add font to project&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In one of my tests, the file stayed in the Downloads folder even after checking &amp;quot;Copy files&amp;quot;, so I deleted the reference, manually moved the font file to my project folder and added it again.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now the font file is in the project but there is still more work to do.&lt;/p&gt;
&lt;p&gt;Select the font file in the Project Navigator. Press Return as if you are going to edit the name. Select the complete file name, &lt;strong&gt;including the file extension&lt;/strong&gt; and copy it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/CopyFontName.png&quot; alt=&quot;Copy font file name&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now go to the Info.plist file. Right-click in the blank space below the existing entries and select &amp;quot;Add Row&amp;quot; from the popup menu. In the box that appears, start typing &amp;quot;Font&amp;quot; using an upper-case &amp;quot;F&amp;quot;. When you see &amp;quot;Fonts provided by application&amp;quot;, select that.&lt;/p&gt;
&lt;p&gt;This entry is an array. Click the disclosure triangle to the left and you should see &amp;quot;Item 0&amp;quot; appear. Paste the name of your font file, including the file extension, into the value for &amp;quot;Item 0&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/FontsProvided.png&quot; alt=&quot;Setting Info.plist entry&quot;&gt;&lt;/p&gt;
&lt;p&gt;Your app now has a font file and knows to make it available. But you aren&#39;t finished yet.&lt;/p&gt;
&lt;p&gt;To use a custom font, you need to know its exact name. In SwiftUI, the code will be something like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Font-Name-Goes-Here&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the exact name is not always obvious and is rarely the file name. So the best thing to do is to ask the app what fonts it now has available. This will also act as confirmation that the font file is being included correctly in your project.&lt;/p&gt;
&lt;p&gt;To get the app to list all its fonts, I used this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onAppear &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listInstalledFonts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;listInstalledFonts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fontFamilies &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIFont&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;familyNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; family &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; fontFamilies &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;family&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; font &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIFont&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fontNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forFamilyName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; family&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&#92;t&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can run the app in the Simulator or use &amp;quot;Debug Preview&amp;quot; to make this show up in the console, but once you have the list, scroll through in until you find the font you just added. The headers are the names of font families and the indented entries are the font names themselves.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/FontsList-iOS.png&quot; alt=&quot;List of Fonts&quot;&gt;&lt;/p&gt;
&lt;p&gt;This time I found that the name of the font was quite obvious: &amp;quot;PaintingWithChocolate&amp;quot; but this is not always the case. And if the font has any variants, you will need to use the complete name e.g. &amp;quot;Optima-ExtraBlack&amp;quot;&lt;/p&gt;
&lt;p&gt;With the name of the font, you are finally able to use it in the app, like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;PaintingWithChocolate&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/FontUsed-iOS.png&quot; alt=&quot;Font in use - iOS&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Using the Font in a Mac App&lt;/h2&gt;
&lt;p&gt;The process for a Mac app is mostly the same.&lt;/p&gt;
&lt;p&gt;Install the font file in your project as before, making sure to copy the file into the project and to add it to the app target.&lt;/p&gt;
&lt;p&gt;For a Mac app, you do not need to specify the font file name in your Info.plist file. Instead, you have to tell the Info.plist where to look for custom fonts in your project directory.&lt;/p&gt;
&lt;p&gt;Open your Info.plist, right-click in the blank space below the existing entries and select &amp;quot;Add Row&amp;quot; from the popup menu. Start typing &amp;quot;Application&amp;quot; using an upper-case &amp;quot;A&amp;quot;. When you see &amp;quot;Application fonts resource path&amp;quot;, select that and enter a period as the value. A period or full-stop tells Xcode that you mean the current directory but even if you have your fonts in a sub-directory, this seems to work.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/AppFontPath.png&quot; alt=&quot;Application Font Path&quot;&gt;&lt;/p&gt;
&lt;p&gt;You do not need to set &amp;quot;Fonts provided by application&amp;quot; for a Mac app, although if I had a problem, I would set it as I feel that this is something that Apple may add at some stage.&lt;/p&gt;
&lt;p&gt;While I was using the same font for both apps and so already had the exact name, you may still need to check the list of installed fonts in a Mac app. The function for doing that is quite different:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;listInstalledFonts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fontFamilies &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSFontManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;availableFontFamilies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; family &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; fontFamilies &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;family&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; familyFonts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSFontManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;availableMembers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ofFontFamily&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; family&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fonts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; familyFonts &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; font &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; fonts &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&#92;t&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The format of the print-out is different too, but you can see where I have selected the exact name to use.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/FontList-Mac.png&quot; alt=&quot;List of Fonts - Mac&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then the SwiftUI code for using the font was just the same:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;PaintingWithChocolate&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2020/FontUsed-Mac.png&quot; alt=&quot;Font in use in Mac app&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Storyboard apps&lt;/h2&gt;
&lt;p&gt;The examples above were both SwiftUI projects, but these techniques work much the same in a storyboard project.&lt;/p&gt;
&lt;p&gt;Follow the same steps to install the font and configure the Info.plist file and use the same functions to list the installed files.&lt;/p&gt;
&lt;p&gt;To use the fonts, if you are using a storyboard in an iOS project, select &amp;quot;Custom&amp;quot; in the font picker and then you will be able to see your custom font listed if it is installed correctly. I could not work out how to get the custom font appearing in this menu for a macOS app.&lt;/p&gt;
&lt;p&gt;To apply the font programmatically, you can use something like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// iOS&lt;/span&gt;
    fontLabel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;font &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIFont&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;PaintingWithChocolate&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// macOS&lt;/span&gt;
    fontLabel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;font &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSFont&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;PaintingWithChocolate&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Trouble-shooting&lt;/h2&gt;
&lt;p&gt;If you have gone through these steps and the font still isn&#39;t working, here are a few things to check:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Is the font file&#39;s target membership set to the app?
&lt;img src=&quot;https://troz.net/images/2020/FontFileTargetSet.png&quot; alt=&quot;File target Set&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Is the font file being included in the &amp;quot;Copy Bundle Resources&amp;quot; build phase? If not, drag it in manually from the Project Navigator.
&lt;img src=&quot;https://troz.net/images/2020/FontInCopyBundleResources.png&quot; alt=&quot;Copy Bundle Resources&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS Info.plist - is there a typo in the &amp;quot;Fonts provided by application&amp;quot; setting? Copy the file name and paste it in directly rather than trying to re-type it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;macOS Info.plist- have you put a single period &amp;quot;.&amp;quot; in the &amp;quot;Application fonts resource path&amp;quot; setting?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the font name listing utility - does the font show up there?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Have you used the font name EXACTLY as shown in the font listing? Copy &amp;amp; paste it from the font list.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;Having struggled with various of these steps myself recently, I thought it was worth documenting it. I am sure that future me will be grateful, but hopefully others will find it useful also.&lt;/p&gt;
&lt;p&gt;If you have any suggestions, corrections or improvements, please contact me using one of the links below or though the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact&lt;/a&gt; page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac Extras</title>
    <link href="https://troz.net/post/2020/swiftui_for-mac-extras/" />
    <updated>2020-01-27T01:32:57Z</updated>
    <id>https://troz.net/post/2020/swiftui_for-mac-extras/</id>
    <content type="html">&lt;p&gt;Last year, I wrote a 3 part series of articles on &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-1/&quot;&gt;using SwiftUI to build a Mac&lt;/a&gt; app. I would like to thank everyone who contacted me about this series. It was received very well and revealed that there is still a large amount of interest in programming for the Mac.&lt;/p&gt;
&lt;p&gt;Some of the responses I got were pointing out different or better ways to do things, so I am going to list them here, adding to this post as I get new information. The relevant sections in the original posts will have links to the fixes suggested here, but I decided it was easier to list the changes in a separate post, rather than asking people to re-read the whole series looking for modifications.&lt;/p&gt;

&lt;hr&gt;
&lt;h2&gt;Dismissing sheets&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I just read your series on writing Mac apps with SwiftUI. Great stuff!
Just wanted to add that in part 2 when dismissing sheets there are two ways to do that, one of them is the one that you figured out and the other is to have the view dismiss itself by grabbing its PresentationMode from the environment. This way you don’t need to pass presentation bindings to your sheet views.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Paul Hudson of Hacking with Swift explains how to use both methods very clearly in his article on &lt;a href=&quot;https://www.hackingwithswift.com/quick-start/swiftui/how-to-make-a-view-dismiss-itself&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;How to make a view dismiss itself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-3#sheets&quot;&gt;original technique&lt;/a&gt; passed the Boolean that triggered the sheet to appear, as a Binding to the sheet view. The sheet view could then toggle this to make the parent view dismiss it.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isVisible&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isVisible&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is a sheet.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isVisible &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The presentation mode method leaves it up to the sheet to dismiss itself. The parent view shows the sheet but does not pass any binding.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sheet view gets an environment property that it can use to change its presentation mode, dismissing itself that way.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;presentationMode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; presentationMode

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is a sheet.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;presentationMode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wrappedValue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dismiss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I guess the presentation mode method is slightly easier to understand and it leaves the caller of the sheet view less cluttered which is a good thing. But the two methods are functionally identical, so use whichever you prefer. The sample project now contains both methods, with one commented out, so you can swap the comments to test.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Subscribing on the Main Thread&lt;/h2&gt;
&lt;p&gt;In &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-2#passing-menu-data-to-a-view&quot;&gt;part 2 of this series&lt;/a&gt;, I created a menu item caused the downloaded image to be flipped. Selecting the menu item posted a notification:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;flipImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flipImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which the view showing the image was listening for.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; flipImageMenuItemSelected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flipImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The outermost component of the view&#39;s body used an &lt;code&gt;onReceive&lt;/code&gt; modifier to get this notification and react accordingly.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// layout omitted for brevity&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;flipImageMenuItemSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
          &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageIsFlipped&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the notification was triggering a change to the UI, I made sure that this happened on the main thread.&lt;/p&gt;
&lt;p&gt;But it was pointed out to me that it would be easier to make sure that the publisher was set up to use the main thread all the time, instead of having to specify the main queue when processing the notification.&lt;/p&gt;
&lt;p&gt;So now the publisher is defined like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; flipImageMenuItemSelected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flipImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunLoop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the &lt;code&gt;onReceive&lt;/code&gt; modifier can toggle the &lt;code&gt;imageIsFlipped&lt;/code&gt; flag directly, without having to worry about the thread.&lt;/p&gt;
&lt;p&gt;This seems to me a much better solution as it sets the correct thread once when the publisher is created and makes using it much cleaner and easier.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Passing data back from AppKit&lt;/h2&gt;
&lt;p&gt;The previous two changes have been more a matter of style, but this one is a real error that would stop an app working as it should.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-2#user-interface-elements&quot;&gt;the User Interface Elements section of the series&lt;/a&gt;, I used &lt;code&gt;NSViewRepresentable&lt;/code&gt; to embed a standard &lt;code&gt;NSColorWell&lt;/code&gt; in a SwiftUI view. I thought this was working properly, but then I got this email:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I noticed that the selectedColor in the EmbeddedColorWell is not being mutated and is not being used in a two-way manner. The selectedColor is initialized to NSColor.blue and used to set the color in the NSColorWell view. When the NSColorWell color is changed, it does change the color of the EmbeddedColorWell view. However, the actual @State var selectedColor value is never mutated in this process beyond the initialized value of NSColor.blue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is in the Form tab of the UI Samples window. As suggested to me, I added a &lt;code&gt;background&lt;/code&gt; modifier to set the background of a portion of the view to the selected color. And although the NSColorWell showed newly selected colors, the background remained stubbornly blue.&lt;/p&gt;
&lt;p&gt;The solution was to add a &lt;code&gt;Coordinator&lt;/code&gt; to the &lt;code&gt;NSViewRepresentable&lt;/code&gt; and have it subscribe to any changes in the color and update the embedded view&#39;s selected color as needed. I would not have come up with this by myself, so many thanks to the person who sent it to me.&lt;/p&gt;
&lt;p&gt;Here is the full code for the &lt;code&gt;EmbeddedColorWell&lt;/code&gt; struct:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmbeddedColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSViewRepresentable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColor&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; embedded&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmbeddedColorWell&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; subscription&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyCancellable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; embedded&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmbeddedColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;embedded &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; embedded
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// Observe KVO compliant color property on NSColorWell object.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Update the selectedColor property on EmbeddedColorWell as needed.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;changeColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorWell&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            subscription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; colorWell
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;new&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sink &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;embedded&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; color
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;makeCoordinator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmbeddedColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Coordinator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Coordinator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;makeNSView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColorWell&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; colorWell &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;frame&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zero&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;coordinator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changeColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorWell&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; colorWell&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; colorWell
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;updateNSView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; nsView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        nsView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedColor
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the previous section, I changed the publisher to use the main RunLoop so as to avoid having the use &lt;code&gt;DispatchQueue.main.async&lt;/code&gt;. In this instance, that did not work so well. It only updated the selected color after the mouse had been released. But using &lt;code&gt;DispatchQueue.main.async&lt;/code&gt; made the update live.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As I explained at the start of these articles, I was documenting my explorations in using SwiftUI for a Mac app and while I hoped there would be some useful examples, this was not intended to be a definitive guide. So I am very grateful to everyone who has contributed to these updates.&lt;/p&gt;
&lt;p&gt;If you have anything more that you would like to be included, please contact me using one of the contact buttons below or through the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact page&lt;/a&gt; on this site.&lt;/p&gt;
&lt;p&gt;The edited project is available on &lt;a href=&quot;https://github.com/trozware/swiftui-mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac - Part 3</title>
    <link href="https://troz.net/post/2019/swiftui-for-mac-3/" />
    <updated>2019-12-15T07:28:20Z</updated>
    <id>https://troz.net/post/2019/swiftui-for-mac-3/</id>
    <content type="html">&lt;p&gt;In &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-1/&quot;&gt;part 1 of this series&lt;/a&gt;, I created a Mac app using SwiftUI. The app uses a Master-Detail design to list entries in an outline on the left and show details about the selected entry in the detail view on the right. In &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-2/&quot;&gt;part 2&lt;/a&gt; I explored using menus, adding all the expected user interface elements and opening secondary windows.&lt;/p&gt;
&lt;p&gt;In this third and final part, I want to look at the various ways to present dialogs to the user.&lt;/p&gt;

&lt;p&gt;There are four different types of dialog that I want to explore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alert&lt;/li&gt;
&lt;li&gt;Action&lt;/li&gt;
&lt;li&gt;Sheet&lt;/li&gt;
&lt;li&gt;File dialogs (open &amp;amp; save)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the first thing to do is add a footer to the DetailView to trigger each of these. I am going to separate this out into a new subview for neatness.&lt;/p&gt;
&lt;h2&gt;Alert&lt;/h2&gt;
&lt;p&gt;To make an Alert, I need an @State Bool which sets whether the alert is visible or not. All the button has to do is toggle that Bool. Stripping out the extra code and views, this is what I have.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DialogsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; alertIsShowing &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dialogResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Click the buttons above to test the dialogs.&quot;&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Alert&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;alertIsShowing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To configure the alert itself, I added an alert modifier to the outmost view in this view. The &lt;code&gt;dialogResult&lt;/code&gt; string is a diagnostic that I can use to confirm that the results of the various dialogs get passed back to the parent view.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;Alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Alert&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is an alert!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        dismissButton&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dialogResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK clicked in Alert&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There were a few things that tripped me up in this relatively short chunk of code. Firstly, both title and message must be Text views, not strings. If you get an error message that says &amp;quot;Cannot convert value of type &#39;String&#39; to expected argument type &#39;Text&#39;&amp;quot;, then you have forgotten to use a Text view.&lt;/p&gt;
&lt;p&gt;Then there is the button which auto-suggest tells me is of type Alert.Button. I couldn&#39;t find any documentation for this, but delving into the definition for Alert, I see that there are three pre-defined button types: default, cancel or destructive. Cancel actually has two variants and will use a label appropriate to the user&#39;s locale if no label is supplied.&lt;/p&gt;
&lt;p&gt;Again, these buttons need a Text view as the label (if supplied) and can take an action closure, which I used to update my &lt;code&gt;dialogResult&lt;/code&gt; string.&lt;/p&gt;
&lt;p&gt;This version showed a single &lt;code&gt;dismissButton&lt;/code&gt; but I saw that there was a variation of Alert with &lt;code&gt;primary&lt;/code&gt; and &lt;code&gt;secondary&lt;/code&gt; buttons. It was not obvious that these would also dismiss the alert dialog, but I tried anyway.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;Alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Alert&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is an alert!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        primaryButton&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dialogResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK clicked in Alert&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; secondaryButton&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dialogResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cancel clicked in Alert&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked very nicely and the Esc and Return keys triggered the two buttons as you would expect with both of them closing the dialog.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-alert.jpg&quot; alt=&quot;Alert&quot;&gt;&lt;/p&gt;
&lt;p&gt;I tried using the &lt;code&gt;destructive&lt;/code&gt; button type, but there was no difference to either the appearance or behavior of the button.&lt;/p&gt;
&lt;p&gt;So Alert is a great choice for a text-based dialog, either for informational use or to allow two choices of action.&lt;/p&gt;
&lt;h2&gt;Action&lt;/h2&gt;
&lt;p&gt;Very short section here - &#39;ActionSheet&#39; is unavailable in macOS! I probably should have researched that before I started this section. So use Alerts, I guess or a custom sheet.&lt;/p&gt;
&lt;h2&gt;Sheets&lt;/h2&gt;
&lt;p&gt;While Alerts have a very fixed structure, sheets allow us to put any SwiftUI view into a sheet dialog.&lt;/p&gt;
&lt;p&gt;So I added another Bool for the Sheet button to toggle, and added this sheet modifier. SheetView right now is simply a TextView.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This didn&#39;t work so well. It showed the sheet, but the sheet was tiny - only the size of the Text view it contained. And I had no way of dismissing it...&lt;/p&gt;
&lt;p&gt;The size problem was solved by setting a frame on the Text view in SheetView.&lt;/p&gt;
&lt;p&gt;The trick to dismissing the sheet is to pass it a Binding to the Bool that triggered it to open in the first place. If a button in the sheet sets this Bool back to false, the parent view will hide the sheet. That sounds confusing, but it works.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isVisible&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isVisible&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is a sheet.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isVisible &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is a very bad diagram that tries to explain what is happening:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-sheet.png&quot; alt=&quot;Sheet&quot;&gt;&lt;/p&gt;
&lt;p&gt;The parent view has an @State Boolean variable called &lt;code&gt;sheetIsShowing&lt;/code&gt;. This is bound to the alert&#39;s &lt;code&gt;isPresented&lt;/code&gt; so it dictates whenever the sheet is visible. When the Sheet button is clicked, this variable is set to &lt;code&gt;true&lt;/code&gt; and the sheet opens. But at the same time, a Binding to this variable is passed to the sheet. I deliberately gave this a different name, so as to make it clear which View was changing what variable.&lt;/p&gt;
&lt;p&gt;When the sheet wants to close, it does not close itself. Instead it sets this variable to false. Because it is a Binding, this sets the original &lt;code&gt;sheetIsShowing&lt;/code&gt; variable on the parent view to false and the parent view then closes the sheet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Look at &lt;strong&gt;&lt;a href=&quot;https://troz.net/post/2020/swiftui_for-mac-extras#dismissing-sheets&quot;&gt;Dismissing sheets&lt;/a&gt;&lt;/strong&gt; in my post of extras &amp;amp; changes to this series for an alternative way to dismiss a sheet.&lt;/p&gt;
&lt;h3&gt;Sheets &amp;amp; Data&lt;/h3&gt;
&lt;p&gt;With this in place, I had the sheet opening and closing perfectly, but I was not yet passing data back &amp;amp; forth between the sheet and its parent view. I decide to put a TextField in the SheetView and bind its contents to the &lt;code&gt;dialogResult&lt;/code&gt; property in the DetailView so that any edits appeared immediately in the DetailView. And while I am there, I might as well add some more decorations to the SheetView since it is a full View and not a restricted Alert.&lt;/p&gt;
&lt;p&gt;Calling the SheetView changed to this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isPresented&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isVisible&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$sheetIsShowing&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; enteredText&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$dialogResult&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the SheetView itself (not all the interface is listed here for brevity):&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isVisible&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; enteredText&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Enter some text below…&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;multilineTextAlignment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;center&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Enter the result of the dialog here…&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $enteredText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;HStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isVisible &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;enteredText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cancel clicked in Sheet&quot;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Spacer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isVisible &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;enteredText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OK: &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;enteredText&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-sheet-data.png&quot; alt=&quot;Sheet with data&quot;&gt;&lt;/p&gt;
&lt;p&gt;I only had two issues with this now. I was not able to get the focus into the TextField automatically when the sheet opened and I was not able to assign keyboard shortcuts to the Cancel and OK buttons so that they could be operated without a mouse. And as I mentioned in the previous part, I was not able to make the OK button take on the default styling.&lt;/p&gt;
&lt;p&gt;One useful technique that I developed: the SheetView is in the DialogsView.swift file instead of in its own SwiftUI file. It would probably be a good idea to separate it out but I didn&#39;t which meant that it had no Canvas preview to look at while I was laying it out.&lt;/p&gt;
&lt;p&gt;So I edited the PreviewProvider like this, so that I could change the comments to switch it between showing the DialogsView and showing the SheetView.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DialogsView_Previews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PreviewProvider&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; previews&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// DialogsView()&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;SheetView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isVisible&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; enteredText&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Files&lt;/h2&gt;
&lt;p&gt;AppKit provides NSOpenPanel for selecting a file and NSSavePanel for saving. I will try to implement NSSavePanel to allow saving the current cat image.&lt;/p&gt;
&lt;p&gt;Since this is an AppKit control rather than a SwiftUI control, I assumed that I would need to use NSViewRepresentable like I did for the NSColorWell in part 2. But while NSColorWell is a descendent of NSView, NSSavePanel is not. So I need a new idea.&lt;/p&gt;
&lt;p&gt;Rather naively, I thought maybe I could just create an NSSavePanel in a function inside DialogsView and see what happened.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;saveImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; panel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSSavePanel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nameFieldLabel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Save cat image as:&quot;&lt;/span&gt;&lt;/span&gt;
      panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nameFieldStringValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat.jpg&quot;&lt;/span&gt;&lt;/span&gt;
      panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canCreateDirectories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
      panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;begin &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; response &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModalResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fileUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Crash &amp;amp; burn... so what if I made the NSSavePanel an @State property of the View? No, that crashed even faster. Maybe SwiftUI Views don&#39;t like this sort of thing, but how about if I get the Application Delegate to handle it? What if I moved the &lt;code&gt;saveImage&lt;/code&gt; method to the App Delegate and changed the calling function to access it there?&lt;/p&gt;
&lt;p&gt;Still crashed. At this stage I am beginning to wonder if I know how to use an NSSavePanel. Time to create a simple test app without SwiftUI and see what happens. Well it appears that I no longer know how to use an NSSavePanel. Code from an older project that works fine, will not work in my new sample project!&lt;/p&gt;
&lt;p&gt;Guess what - it was another macOS Catalina security issue which I would have realised faster I had opened the Console. Back to the Signing &amp;amp; Capabilities section of the target settings and this time I set File Access for User Selected File to Read/Write.&lt;/p&gt;
&lt;p&gt;Now the NSSavePanel opens when called from DialogsView and prints the selected file URL if one is chosen.&lt;/p&gt;
&lt;p&gt;But this is all happening in DialogsView, which is a subview of DetailView. And DetailView is the view that holds the image, not DialogsView. So how can I save the image? Do I pass the URL to DetailView or pass the image to DialogsView? Or do something clever with Notifications and Subscriptions?&lt;/p&gt;
&lt;p&gt;I really don&#39;t know what is best, but I have decided to post a Notification with the URL as its object. DetailView can receive this Notification and save the image whenever it is received.&lt;/p&gt;
&lt;p&gt;So I replaced the &lt;code&gt;print&lt;/code&gt; line in the &lt;code&gt;saveImage()&lt;/code&gt; method with:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fileUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in DetailView, I set up the publisher:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; saveImageUrlSelected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;VStack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// view code removed for brevity&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;saveImageUrlSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; publisher &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; saveUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; publisher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;catImage&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tiffRepresentation &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageRep &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSBitmapImageRep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; saveData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageRep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;representation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jpeg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                              properties&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; saveData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; saveUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there we have it. Three types of dialogs demonstrated in a SwiftUI for Mac app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alerts: good for simply text-only dialogs&lt;/li&gt;
&lt;li&gt;Sheets: good for more complex dialogs&lt;/li&gt;
&lt;li&gt;Panels: AppKit dialogs that can be called from a SwiftUI View.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think this time I really am finished. This article has already expanded out into a 3-part monster, so I think it is way past time that I stopped typing. I hope you have enjoyed this series. Please use any of the buttons below to contact me or use the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact page&lt;/a&gt; on this site. I would love to hear from anyone who found this series useful or who had any suggestions or corrections to make.&lt;/p&gt;
&lt;p&gt;The final project is available on &lt;a href=&quot;https://github.com/trozware/swiftui-mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; if you would like to download it and take a look.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac - Part 2</title>
    <link href="https://troz.net/post/2019/swiftui-for-mac-2/" />
    <updated>2019-12-15T07:28:10Z</updated>
    <id>https://troz.net/post/2019/swiftui-for-mac-2/</id>
    <content type="html">&lt;p&gt;In &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-1/&quot;&gt;part 1 of this series&lt;/a&gt;, I created a Mac app using SwiftUI. The app uses a Master-Detail design to list entries in an outline on the left and show details about the selected entry in the detail view on the right.&lt;/p&gt;
&lt;p&gt;Now it is time to explore some more of the features of a Mac app with SwiftUI.&lt;/p&gt;

&lt;h2&gt;Menus&lt;/h2&gt;
&lt;p&gt;I had already discovered that the menu bar is configured in the Main.storyboard file. As an experiment, I will add new menu items that allows the user to select between dark mode, light mode or the system mode.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUi-Mac-menus.png&quot; alt=&quot;Menus&quot;&gt;&lt;/p&gt;
&lt;p&gt;Looking at the possibilities along the top that I could send the menu command to, the App Delegate looks like the best option, especially since these menus will change the entire app.&lt;/p&gt;
&lt;p&gt;I opened AppDelegate.swift in the Assistant Editor and Control-dragged from each of the new menu items to create the 3 IBActions and added the code to set the mode.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;darkModeSelected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NSApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appearance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSAppearance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;named&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;darkAqua&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token attribute atrule&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;lightModeSelected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NSApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appearance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSAppearance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;named&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aqua&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token attribute atrule&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;systemModeSelected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NSApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appearance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And since I realized that I would want to check or un-check these based on the current settings, I added IBOutlets for each of these menu items too. As a side note, I love how Xcode is now clever enough to decide whether to add an IBOutlet or an IBAction depending on where in the file you Control-drag to.&lt;/p&gt;
&lt;h2&gt;Using a Property Wrapper&lt;/h2&gt;
&lt;p&gt;Changing mode worked perfectly in the Xcode Preview and in the built app, but the app was not storing this or displaying the current selection with a check mark in the menu.&lt;/p&gt;
&lt;p&gt;For storing the selection, I decided to try another new thing and use a UserDefaults property wrapper, based on &lt;a href=&quot;https://www.avanderlee.com/swift/property-wrappers/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;this post by Antoine van der Lee&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I configured a UserDefault wrapped variable for the mode, set it whenever the menu items changed it and added a function to read it in and apply it on startup.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@UserDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;system_mode&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; defaultValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;system&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; systemMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It would be neater to use an enum here instead of strings, but that would have required fiddling with rawValues to save the data, so for this first test, I stuck to strings.&lt;/p&gt;
&lt;p&gt;The code to toggle the checkmarks in the menu items is standard stuff and nothing particularly SwiftUI related, so I won&#39;t detail it here, but it is in the GitHub project if you are interested.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-dark.png&quot; alt=&quot;Dark mode&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Passing menu data to a View&lt;/h2&gt;
&lt;p&gt;This worked fine and the app worked equally as well in dark or light modes, but I wanted to test how to communicate data back from the menus to the SwiftUI views.&lt;/p&gt;
&lt;p&gt;As a totally pointless but possibly amusing feature, I decided to add a menu item to flip the cat image and then work out how I could send that to the ContentView.&lt;/p&gt;
&lt;p&gt;I added the new menu item to the Format menu and as a first attempt, I tried to Control-drag from there into the ContentView. It didn&#39;t take, so I guess I need to send the message to the AppDelegate as before and then pass it on to the ContentView. I feel like this might be a good place to use a Publisher although so far I have only used the built-in ObservableObject type publishers.&lt;/p&gt;
&lt;p&gt;In this case, it would make more sense to have the DetailView respond to the menu, since it contains the image. But trying to add an @IBAction to a SwiftUI View struct gives this error: &lt;code&gt;Only instance methods can be declared @IBAction&lt;/code&gt; so I am using the App Delegate instead. I feel like this is a problem I need to solve, or the AppDelegate is going to end up filled with @IBActions, but for now, let&#39;s keep going.&lt;/p&gt;
&lt;p&gt;In AppDelegate.swift, I added the IBAction for the Flip Image menu item and had it issue a Notification, after setting up an extension on Notification.Name to de-stringify the name of the notification.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;flipImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flipImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In DetailView.swift, I set up a Boolean @State property to store whether the image was flipped or not and added an onReceive handler to toggle it. Since this is updating the UI, I made sure that it happened on the main thread, but I am not sure whether this is necessary, or whether onReceive uses the main thread automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Check out &lt;strong&gt;&lt;a href=&quot;https://troz.net/post/2020/swiftui_for-mac-extras#subscribing-on-the-main-thread&quot;&gt;Subscribing on the Main Thread&lt;/a&gt;&lt;/strong&gt; in my post of extras &amp;amp; changes to this series for an alternative way to make sure the updates happen on the correct thread.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imageIsFlipped &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;flipImageMenuItemSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;DispatchQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageIsFlipped&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I had that Boolean property, I added two more modifiers to the image. Don&#39;t hate me for the force un-wrapping. I tried using if-let but it wouldn&#39;t compile so I check for &lt;code&gt;catImage != nil&lt;/code&gt; before using this force-unwrap.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nsImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; catImage&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resizable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aspectRatio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rotation3DEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Angle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;degrees&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageIsFlipped &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;180&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        axis&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the image can be flipped from the menu item or even by using the keyboard shortcut. And with a smooth animation (smoother in the app than in this gif).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-Flip.mp4&quot; alt=&quot;Flipping the image&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Opening another window&lt;/h2&gt;
&lt;p&gt;For my next challenge, I want to open a second window that I can use to show samples of various UI elements. As I want to open it from a menu command, my first thought was to add a Window Controller to the Main storyboard and open it that way. I replaced the default View Controller with a Hosting Controller so that I could insert my SwiftUI View into it.&lt;/p&gt;
&lt;p&gt;I had great trouble finding any documentation or examples of how to implement this, but after a lot of trial and error, here is what I got to work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Drag a Hosting View Controller into the storyboard and connect it to the menu item with a segue.&lt;/li&gt;
&lt;li&gt;Set the Presentation property for the window controller to Single so that multiple copies of the window are not created.&lt;/li&gt;
&lt;li&gt;Create a new Swift file and subclass NSHostingController. Don&#39;t bother using a Cocoa class file - the template doesn&#39;t work. Use the code below, replacing SamplesView with the name of the SwiftUI view you want to display.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SamplesHostingController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSHostingController&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PrefsView&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token attribute atrule&quot;&gt;@objc&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;dynamic&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSCoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; coder&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rootView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SamplesView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Back in the storyboard, set the class of the HostingController to this sub-class: SamplesHostingController in my case.&lt;/li&gt;
&lt;li&gt;Set a minimum size for the new Window Controller, otherwise it shrinks to a very small stripe on the screen.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;.frame(maxWidth: .infinity, maxHeight: .infinity)&lt;/code&gt; modifier to the root view in the view you are displaying - my SamplesView.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With all these in place, I was able to open and close a Samples window that was displaying my SwiftUI content.&lt;/p&gt;
&lt;h2&gt;User Interface Elements&lt;/h2&gt;
&lt;p&gt;Not all the expected user interface elements are available in SwiftUI and some of the ones that are implemented are using different names. Again this is a point at which iOS development has got all the attention, so there are conversion tables out there listing UIKit elements and their SwiftUI equivalents, but I was not able to find a similar listing for AppKit. If you find one, please let me know.&lt;/p&gt;
&lt;p&gt;Anyway, I experimented and got most of what I wanted as you can see below. The &lt;a href=&quot;https://github.com/trozware/swiftui-mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;sample app on GitHub&lt;/a&gt; shows the SwiftUI code for these samples, but the main advice I would give is firstly to use the Object Browser to look for what is available and then to check out the styles that can be applied to the element you have chosen.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-UI.png&quot; alt=&quot;UI elements&quot;&gt;&lt;/p&gt;
&lt;p&gt;Mostly, I managed to achieve what I wanted but there were a few exceptions.&lt;/p&gt;
&lt;p&gt;Buttons were a bit odd as there are multiple button styles listed, but I found that they either all looked the same or they disappeared completely. I was not able to style a button with an image and text which is now easy to do in iOS. And I was not able to set a button as the default button and I could not work out how to set a keyboard equivalent for a button. (Setting the keyboard equivalent to Return or Enter might set it to the default style automatically.)&lt;/p&gt;
&lt;p&gt;Of the other UI elements that I wanted to use, a color picker was the main one missing. So I decided to embed an NSColorWell from AppKit into my SwiftUI view.&lt;/p&gt;
&lt;p&gt;The process for embedding a UIKit view is quite well documented elsewhere and the AppKit equivalent was not significantly different. Replace every &amp;quot;UI&amp;quot; with &amp;quot;NS&amp;quot; and you are nearly there.&lt;/p&gt;
&lt;p&gt;First I made a struct that conformed to &lt;code&gt;NSViewRepresentable&lt;/code&gt; and had the required two functions: &lt;code&gt;makeNSView(context:)&lt;/code&gt; and &lt;code&gt;updateNSView(_:context:)&lt;/code&gt;. I supplied an @Binding variable to hold the selected color as an NSColor since that is what the NSColorWell uses.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmbeddedColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSViewRepresentable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Binding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selectedColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColor&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;makeNSView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColorWell&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NSColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;frame&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zero&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;updateNSView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; nsView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        nsView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedColor
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back in my SwiftUI view, I was then able to use &lt;code&gt;EmbeddedColorWell&lt;/code&gt; just like any other SwiftUI view, passing it a binding to an @State variable to set and get the selected color.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;EmbeddedColorWell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $selectedColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Be sure to read &lt;strong&gt;&lt;a href=&quot;https://troz.net/post/2020/swiftui_for-mac-extras#passing-data-back-from-appkit&quot;&gt;Passing data back from AppKit&lt;/a&gt;&lt;/strong&gt; in my post of extras &amp;amp; changes to this series. This is an important fix that is needed to make this embedded control pass data back properly.&lt;/p&gt;
&lt;p&gt;The other stumbling block that I encountered was that I had no way to close the window programmatically, in response to a button click or some other interaction. Maybe this was not the best way to open a fully SwifUI window, but it was still an interesting experiment.&lt;/p&gt;
&lt;p&gt;One thing I learned while working on this project was that SwiftUI can be a bit too clever about working out what needs to be redrawn in the previews. If you edit something in an separate file to the View, you may need to force a re-build to get the updates to show.&lt;/p&gt;
&lt;h2&gt;Communicating Between Windows&lt;/h2&gt;
&lt;p&gt;The last feature that I need to explore is transferring data between two windows. I tested using a &lt;code&gt;.sheet&lt;/code&gt; modifier to open a dialog and that worked exactly as it would in an iOS app - I was able to pass an @Binding variable to the sheet window and any changes made to that in the sheet window flowed back to the calling view.&lt;/p&gt;
&lt;p&gt;But I want to set up a Preferences window which will be opened from the menu bar and not directly from the ContentView or any of its sub-views.&lt;/p&gt;
&lt;p&gt;I started out by using the same technique as before with a Hosting View Controller. And here is where I came mightily unstuck as I could not make a data connection. I had hoped an EnvironmentObject would work, but I could not get an EnvironmentObject through to the HostingController as it was not in the same view hierarchy.&lt;/p&gt;
&lt;p&gt;So instead of trying to use the storyboard, I decide to see if I could use something similar to the initial window creation and call that new window from my ContentView so that it was part of the view and therefore the data hierarchy.&lt;/p&gt;
&lt;p&gt;As usual, there was a lot of trial and error involved here, but I came up with a workable solution.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, Prefs!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSWindow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        window &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createStandardWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;withTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Preferences&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                               width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                               height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSHostingView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rootView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeKeyAndOrderFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For convenience and reusability, I made an extension on NSWindow that created a window with the basic properties configured, much like they are in AppDelegate. I temporarily added a Button to ContentView like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Prefs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This opened the window perfectly, but it had a problem: repeatedly clicking the button kept opening a new window instead of bringing the existing one to the front.&lt;/p&gt;
&lt;p&gt;After a lot more trial and error, this is what I ended up with for my &lt;code&gt;PrefsView&lt;/code&gt; struct:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@State&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; prefsWindowDelegate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsWindowDelegate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, Prefs!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSWindow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        window &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createStandardWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;withTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Preferences&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                               width&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                               height&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSHostingView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rootView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delegate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prefsWindowDelegate
        prefsWindowDelegate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;windowIsOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeKeyAndOrderFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsWindowDelegate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSWindowDelegate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; windowIsOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;windowWillClose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; notification&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            windowIsOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So my PrefsView opens its own window and sets a window delegate that records when the window is closed.&lt;/p&gt;
&lt;p&gt;I removed my temporary button and back in AppDelegate.swift, I added an IBAction called by the Preferences menu item:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; prefsView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsView&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;

    &lt;span class=&quot;token attribute atrule&quot;&gt;@IBAction&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;openPrefsWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prefsView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prefsView&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prefsView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prefsWindowDelegate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;windowIsOpen &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            prefsView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeKeyAndOrderFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            prefsView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this gave me what I was after - a view that opened from a menu item but that knew itself whether it still had a window so could decide whether to open a new one or bring the existing one to the front. This looks a bit convoluted, but it doesn&#39;t appear to cause any memory leaks and the responsibility for the window rests on the view that uses it, which feels right to me.&lt;/p&gt;
&lt;p&gt;After I had this working, getting the data passing around was quite simple. I created a Prefs class to store the preference data. I wanted this data stored in UserDefaults, but since I also wanted to use an ObservableObject with the @Published property wrapper, I was not able to use the @UserDefaults property wrapper that I had used for the app mode.&lt;/p&gt;
&lt;p&gt;So instead I used @Published with a &lt;code&gt;didSet&lt;/code&gt; that saved the changed data to UserDefaults. Thanks to &lt;a href=&quot;https://twitter.com/azamsharp&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@azamsharp&lt;/a&gt; for this technique.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Prefs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; showCopyright&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDefaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forKey&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;showCopyright&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;didSet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;UserDefaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;showCopyright&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; forKey&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;showCopyright&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This preference will dictate whether a copyright notice is shown on every cat image, so it needs to be available to DetailView and to PrefsView. Since I did not want to pass it through every view on the way to DetailView, I decided to use an EnvironmentObject. But I was not able to work out how to set the environmentObject for the PrefsView as I created it, so I ended up sending an @ObservedObject to PrefsView but using an @EnvironmentObject for DetailView. (Actually for CatImageView as I had extracted the image from DetailView as a subview.)&lt;/p&gt;
&lt;p&gt;In AppDelegate.swift:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prefs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Prefs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// and in the openPrefsWindow IBAction:&lt;/span&gt;
    prefsView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrefsView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prefs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In PrefsView.swift:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; prefs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Prefs&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In CatImageView (a subview of DetailView):&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CatImageView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@EnvironmentObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; prefs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Prefs&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; catImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageIsFlipped&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nsImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; catImage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// modifiers as before&lt;/span&gt;

         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;overlay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
             &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;showCopyright &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Copyright © https://http.cat&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;caption&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foregroundColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;white&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;radius&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;alignment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomTrailing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that is that! I have shown how it is possible to create an entire Mac app using SwiftUI.&lt;/p&gt;
&lt;p&gt;I am sure that there are numerous ways that this could be improved, so if you have any ideas or suggestions, please use one of the buttons below to contact me.&lt;/p&gt;
&lt;p&gt;If you want to check out the project at this stage, here is a link to the &lt;a href=&quot;https://github.com/trozware/swiftui-mac/tree/0ea77f80832e0f32e477ce89ac57d8a95f45f035&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;relevant GitHub commit&lt;/a&gt;. Or if you would prefer, here is a link to the &lt;a href=&quot;https://github.com/trozware/swiftui-mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;final version&lt;/a&gt; of the project.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Moving on to an unplanned &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-3/&quot;&gt;part 3 of this series&lt;/a&gt;, I am going to experiment with various forms of dialog.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI for Mac - Part 1</title>
    <link href="https://troz.net/post/2019/swiftui-for-mac-1/" />
    <updated>2019-12-15T07:28:00Z</updated>
    <id>https://troz.net/post/2019/swiftui-for-mac-1/</id>
    <content type="html">&lt;p&gt;So far, nearly all the articles I have seen about SwiftUI show it being used for iOS, more particularly for iPhone.
But SwiftUI works on all Apple&#39;s platforms, and as I am primarily a Mac developer, I decided to try out a Mac app and see what happened.&lt;/p&gt;

&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;I opened up Xcode and created a new project selecting the macOS App template. The project opened at the usual ContentView.swift but there were a few differences in the project structure as well as one in the ContentView struct.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-project.png&quot; alt=&quot;New Mac project&quot;&gt;&lt;/p&gt;
&lt;p&gt;The first thing to notice is that the default &amp;quot;Hello, World!&amp;quot; Text view has a frame set:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I removed this frame modifier, the preview display in the Canvas changed so that the view was only the size of the text instead of being a standard window size. I guess an iOS device always knows what size it is, but a Mac window can be any size, so you have to be more explicit to stop SwiftUI shrinking the container view to the minimum size possible.&lt;/p&gt;
&lt;p&gt;The next thing is to look at the files that are included in the project. There is no SceneDelegate.swift as you would see in an iOS project. And to my surprise, there was still a Main.storyboard file! And going to the General settings for the app target, I could see that this storyboard was selected as the Main Interface.&lt;/p&gt;
&lt;p&gt;Opening it up reveals that this is where the application menu is configured. I had wondered where the menus were configured in Mac SwiftUI apps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-Storyboard.png&quot; alt=&quot;Main storyboard&quot;&gt;&lt;/p&gt;
&lt;p&gt;The AppDelegate was the next thing I looked at and here I found some of the code that I would have expected to find in a SceneDelegate. The &lt;code&gt;applicationDidFinishLaunching(_:)&lt;/code&gt; method creates an instance of ContentView, creates an NSWindow and uses an NSHostingView to display the ContentView inside the window. At this stage, running the app gives me what I would expect: a fully-fledged Mac app with a window and a menu, both with all the functions you would expect in any standard Mac app.&lt;/p&gt;
&lt;h2&gt;The Canvas&lt;/h2&gt;
&lt;p&gt;I was not expecting the Canvas to be much use when it came to previewing a Mac app. It works so well with an iPhone app because the iPhone is tall and thin and fits neatly into one side of the code window. But a Mac view is likely to be much bigger, so it would have to be scaled down a lot to avoid talking up too much precious space in my Xcode window.&lt;/p&gt;
&lt;p&gt;But it works as expected, and even scaled down, you get a good idea of the layout while still getting the live reloading that is part of what makes developing in SwiftUI so much fun.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-Canvas.png&quot; alt=&quot;Canvas&quot;&gt;&lt;/p&gt;
&lt;p&gt;But here is where I got my first real surprise, with a feature that I had not seen yet in any SwiftUI tutorial or article. Click the Live Preview button and see what happens...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-LivePreview.png&quot; alt=&quot;Live Preview&quot;&gt;&lt;/p&gt;
&lt;p&gt;Of course I clicked &amp;quot;Bring Forward&amp;quot; and there was my app running in a window called &amp;quot;Xcode Preview&amp;quot;. There was an app in my Dock and when I chose &amp;quot;Show in Finder&amp;quot;, I found that the app is buried deep in DerivedData. Positioning my windows so I could type in Xcode while watching this preview window, I saw that it instantly updated my view as I typed, just like an iPhone in the Canvas.&lt;/p&gt;
&lt;p&gt;If I changed the structure of the view, the app closed and re-opened immediately with the new content. This is amazing and shows that the Xcode &amp;amp; SwiftUI teams really thought about how to use these new features in Mac apps as well as iOS.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In Xcode 11.3, I found that I was having trouble with the previews. They would not display and above the Canvas, I got the super helpful message &amp;quot;Cannot preview in this file --- SwiftUI-Mac.app may have crashed.&amp;quot;. It turned out that this was a signing issue. If you go to the app target and look in the Signing and Capabilities section, check that Signing Certificate is not set to &amp;quot;Sign to Run Locally&amp;quot;. If it is, switch to &amp;quot;Development&amp;quot; and the previews will start working again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-signing.png&quot; alt=&quot;Code signing&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Laying out the View&lt;/h2&gt;
&lt;p&gt;Now that I have the project and I know how to preview it, it&#39;s time to work out what to display in the app. The next real app I want to work on will use a master-detail layout, so that is what I decided to try here.&lt;/p&gt;
&lt;p&gt;Before worrying about the data, I decided to try populating the master view with a static list and using that to navigate to a detail view that was simply a Text view.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NavigationView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                 &lt;span class=&quot;token class-name&quot;&gt;ForEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; index &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
                     &lt;span class=&quot;token class-name&quot;&gt;NavigationLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                         &lt;span class=&quot;token class-name&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Link &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
             &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked, except that the left column was only about 20 pixels wide. But I was able to use the mouse to drag it wider and there were my List entries. Clicking on one did indeed show the detail I wanted, but it shrunk the window to one line high!&lt;/p&gt;
&lt;p&gt;The first thing I did was to apply a &lt;code&gt;listStyle&lt;/code&gt; modifier to make it show the semi-transparent Mac sidebar. This fixed the width of the sidebar. But the whole window still shrunk when I selected an item.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SidebarListStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I tried applying the frame modifier to the NavigationView and that made the window stay the same size, but the content still shrunk into a tiny section in the middle. It looks like I need to apply that frame modifier to the detail view as well.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxHeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infinity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And as you can see from this gif, I then had a full functional master-detail view with a collapsible and expandable semi-transparent sidebar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-master-detail.gif&quot; alt=&quot;Master detail view&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Adding Data&lt;/h2&gt;
&lt;p&gt;After some scouting around for a free API that I could hook into, I came across &lt;a href=&quot;https://http.cat&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;HTTP Cats&lt;/a&gt; which is a site that serves up a cat image to match almost every HTTP status code.&lt;/p&gt;
&lt;p&gt;This sounded ideal: I can list the codes in the master view on the left and display the image in the detail view on the right.&lt;/p&gt;
&lt;p&gt;First I created a JSON file to list all the HTTP status codes so that I could put them into a List view. This was a very simple array with each entry having a code and a title:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Continue&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;101&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Switching Protocols&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ...
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I created an HttpStatus struct with these 2 properties and I borrowed &lt;a href=&quot;https://www.hackingwithswift.com/example-code/system/how-to-decode-json-from-your-app-bundle-the-easy-way&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Paul Hudson&#39;s excellent Helper Bundle extension&lt;/a&gt; to decode the JSON file. For a first attempt, I used the numeric codes to build the list and showed the title of the selected one in the detail view. But one of the best things about SwiftUI is that it makes it so easy to configure table rows, so it is time to create a new View to do this.&lt;/p&gt;
&lt;p&gt;After some experimentation, I had a TableRowView that I liked the look of, but the default sidebar width was too narrow and truncated the status code titles, so I added a frame modifier to the List to set a minimum and maximum width for the sidebar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-Master.png&quot; alt=&quot;Master view&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Outline List&lt;/h2&gt;
&lt;p&gt;At this point I decided that it would be more useful to have a outline list with the status codes grouped by their category.&lt;/p&gt;
&lt;p&gt;So I re-did the JSON file to show this, added an HttpSection struct and a SectionHeaderView and modified the data loading method and @State variable.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;headerCode&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1xx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;headerText&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Informational&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;statuses&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Continue&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;101&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Switching Protocols&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;headerCode&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2xx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;headerText&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;statuses&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked really well and I was thrilled to find that the sections automatically had Show/Hide toggles!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-outline.png&quot; alt=&quot;Outline view&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Detail View&lt;/h2&gt;
&lt;p&gt;Up until now, I had been using a standard Text view as the destination for my navigation. This is a really useful technique as you can build the interface gradually but have it work from the beginning. But now it was time to create a new view for the details.&lt;/p&gt;
&lt;p&gt;I set up the view and added a method that would download the correct cat image when the view appeared but there was no image. After some digging, I realised that sand-boxed Mac apps do not allow network access by default. I went to the Signing &amp;amp; Capabilities section of the target settings and turned on &amp;quot;Outgoing Connections (Client)&amp;quot;. And then I had my cat pictures.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUI-Mac-detail.png&quot; alt=&quot;Detail view&quot;&gt;&lt;/p&gt;
&lt;p&gt;It really should have a loading image to display while the cat image is being downloaded, but to my disappointment, I found that the SF Symbols are not available to a Mac app! But I added a Text view to say &amp;quot;Loading...&amp;quot;.&lt;/p&gt;
&lt;p&gt;Now that I have a functioning Mac app with a Master-Detail view, the next thing is to explore some more of the challenges that will need to be solved before I can write a Mac app completely using SwiftUI.&lt;/p&gt;
&lt;p&gt;If you want to check out the project at this stage, here is a link to the &lt;a href=&quot;https://github.com/trozware/swiftui-mac/tree/8ebeb615db81f9d7b6489a1920ebb08d07131ebb&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;relevant GitHub commit&lt;/a&gt;. Or if you would prefer, here is a link to the &lt;a href=&quot;https://github.com/trozware/swiftui-mac&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;final version&lt;/a&gt; of the project.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In &lt;a href=&quot;https://troz.net/post/2019/swiftui-for-mac-2/&quot;&gt;part 2 of this series&lt;/a&gt;, I will look into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how to interact with the menus&lt;/li&gt;
&lt;li&gt;how to open a secondary window&lt;/li&gt;
&lt;li&gt;more user interface controls&lt;/li&gt;
&lt;li&gt;how to pass data around between windows&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI First Thoughts</title>
    <link href="https://troz.net/post/2019/swiftui_first-thoughts/" />
    <updated>2019-08-18T05:00:05Z</updated>
    <id>https://troz.net/post/2019/swiftui_first-thoughts/</id>
    <content type="html">&lt;p&gt;At WWDC 2019, Apple surprised us all by announcing a completely new declarative UI framework called &lt;a href=&quot;https://developer.apple.com/xcode/swiftui/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftUI&lt;/a&gt;. Quoting snippets from the Apple announcements, &amp;quot;SwiftUI is an innovative, exceptionally simple way to build user interfaces across all Apple platforms with the power of Swift&amp;quot; and &amp;quot;SwiftUI uses a declarative syntax so you can simply state what your user interface should do.&amp;quot; But what does this mean and should we all adopt it now?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://developer.apple.com/assets/elements/icons/swift/swift-96x96_2x.png&quot; alt=&quot;Swift&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Swift&lt;/h2&gt;
&lt;p&gt;Before discussing whether to adopt SwiftUI, we need to consider Swift itself, since you cannot use SwiftUI without Swift.&lt;/p&gt;
&lt;p&gt;The announcement of Swift at WWDC 2014 was a complete bombshell. Here in Australia, I watch the keynote every year while half asleep since it starts at 3 am. That announcement had me wide awake and bouncing out of my seat with excitement. I was an early adopter and have never regretted that decision.&lt;/p&gt;
&lt;p&gt;Many well-respected developers have decided to ignore Swift and they have their various reasons, so let&#39;s go through the ones I have heard:&lt;/p&gt;
&lt;h4&gt;1. I&#39;m very comfortable and productive in Objective-C&lt;/h4&gt;
&lt;p&gt;This is not a bad reason and for many people, their employer may insist on the language anyway, but this argument doesn&#39;t work for me. Firstly, I think that there is never going to be the One Perfect Language (with apologies to &lt;a href=&quot;https://oleb.net/blog/2017/06/chris-lattner-wwdc-swift-panel/#in-which-fields-would-you-like-to-see-swift-in-the-future&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Chris Lattner&lt;/a&gt;) so I am always open to new ideas. And personally, I love learning new languages even if I am never going to use them in production. I feel that this makes me a better programmer overall. And there are vast benefits to Swift - it is a much safer language to write it, it is easier to read and more concise to write, it is just a much more modern language.&lt;/p&gt;
&lt;h4&gt;2. Swift is too new and changes too much between versions&lt;/h4&gt;
&lt;p&gt;Swift was new. It is now 5 years old and we are into version 5.2. Undeniably, there have been a lot of changes since 1.0 and many of them have been breaking changes. Swift 3 was especially bad in this respect. But this came with advantages too. As early adopters, we were able to influence the direction of the language. And Xcode was always pretty good about providing a converter to the next version. But either way, since Swift 4, there are supposed to be no more breaking changes, so this is an argument whose time has passed.&lt;/p&gt;
&lt;h4&gt;3. No ABI stability&lt;/h4&gt;
&lt;p&gt;ABI stability was a crutch that many people clung to as an excuse to avoid Swift. But unless you were building frameworks for distribution, I don&#39;t see that it was ever a complete deal-breaker. The &lt;a href=&quot;https://github.com/apple/swift/blob/master/docs/ABIStabilityManifesto.md#what-does-abi-stability-enable&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift ABI Stability Manifesto&lt;/a&gt; has a good review of what ABI stability is and what it will allow. As app developers, the main advantage is that the Swift libraries will no longer need to be bundled with each app. This will decrease app sizes dramatically. But either way, we now have ABI stability, so again, this argument is finished.&lt;/p&gt;
&lt;h4&gt;4. Apple may not be serious about Swift and it may not last&lt;/h4&gt;
&lt;p&gt;I think there was a certain amount of wishful thinking here. The main data behind this theory seemed to be that very few of the Apple apps included any Swift. And in the beginning this was true. I expect Apple&#39;s apps have a rather longer development time-frame than apps produced by independents. However over the last few years, more and more of Apple&#39;s own apps have started to use Swift, either completely or partially. And any doubt about Apple&#39;s commitment to Swift should have been permanently laid to rest by the announcement of SwiftUI.&lt;/p&gt;
&lt;h4&gt;5. It&#39;s too complicated&lt;/h4&gt;
&lt;p&gt;OK, this one has some validity, but then again, if Swift is to rule the world, it needs to be able to do a lot of things. I love Swift, I love writing in it and I am very productive when using it. But then I see a chunk of code sprinkled with generics and unsafe pointers and I can&#39;t make head nor tail of it. However, I think that if you are writing any standard, non-arcade-game app, you can write very good Swift using just the basics.&lt;/p&gt;
&lt;h2&gt;Why did Apple make SwiftUI?&lt;/h2&gt;
&lt;p&gt;Having demolished the arguments against using Swift, it&#39;s time to move on to SwiftUI. Let&#39;s start with the reasons why something like this had to happen.&lt;/p&gt;
&lt;p&gt;Apple now has 5 or possibly 6 user platforms: macOS, iOS, iPadOS, tvOS, watchOS and maybe CarPlay. (I&#39;ve never considered any CarPlay development so I have no idea how it works.) The screen sizes range from 38mm for the smallest watch to over 75&amp;quot; to large screen TVs (apologies for mixing my units there, but it seems that TVs have not gone metric yet). As far as user interface frameworks go, we have had AppKit, UIKit and WatchKit. AppKit is the venerable old member of this team, descended from NextStep (which is why every element name has the NS prefix) and is used to make macOS apps. UIKit was built from scratch for the iPhone and so is a lot newer and neater than AppKit, but at the same time it is more limited. It has never had to deal with the vast variability and complexity of a Mac app&#39;s interface. WatchKit is an even more slimmed down framework with a relatively small number of interface elements.&lt;/p&gt;
&lt;p&gt;Now imagine that you are trying to write a multi-platform app. You have to learn at least 3 different ways of doing everything. And there are differences that always catch me out. For example, in AppKit, to set the text in a text field, you set the field&#39;s &lt;code&gt;stringValue&lt;/code&gt; but in iOS, you can set the field&#39;s &lt;code&gt;text&lt;/code&gt; property. And in WatchKit, you call the &lt;code&gt;setText()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Want to change the background color of a view? UIView - no problem, set &lt;code&gt;backgroundColor&lt;/code&gt;. NSView - nope - do something weird with layers. I forget what, I have to look it up every time.&lt;/p&gt;
&lt;p&gt;These inconsistencies are annoying, time-consuming and a huge barrier to cross-platform development. It really annoys me when I read articles or tweets that assume Swift programming = iOS programming when it is so much more. But before SwiftUI, it was not easy to jump between the platforms.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/ipad-apps-for-mac/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Catalyst&lt;/a&gt;, previously code-named Marzipan, was supposed to be the answer. And it is a partial answer. It allows iPad apps to be ported to the Mac and that&#39;s great for iPad developers who want to expand their reach. But it isn&#39;t the answer for everybody.&lt;/p&gt;
&lt;h3&gt;Interface Builder&lt;/h3&gt;
&lt;p&gt;Before I go further into what I think is so great about SwiftUI, I would like to take a moment to discuss Interface Builder. Xcode is a brilliant tool that gets better with every release, but its weak point has always been Interface Builder. I am old enough to remember when Interface Builder was a separate app (not that we called them &amp;quot;apps&amp;quot; in those days) and although it has been folded into Xcode for many years now, the connection between interface and code has always been clunky and un-intuitive. Control-dragging from interface to code and then remembering to switch between Action and Outlet so that you don&#39;t end up naming your button &amp;quot;doneButtonWasTapped&amp;quot;. And if you get the modifier key wrong, who knows what weird stuff will happen.&lt;/p&gt;
&lt;p&gt;And then came Auto Layout... It was vastly more flexible than the old school springs &amp;amp; struts and with the ever increasing number of iPhone &amp;amp; iPad models something had to be done, but if you have never ended up swearing at Auto Layout and reverting to a previous commit or mashing Undo repeatedly, then you can&#39;t have used Auto Layout very much!&lt;/p&gt;
&lt;p&gt;WatchKit had an entirely different approach to layout and it was easy, intuitive and very refreshing. You drag in objects, they go one under another. You can group them horizontally or vertically, you can make them into table cells. WatchKit has a much more limited set of interface elements and a much more limited set of device sizes to deal with, but I think we can clearly see the origins of SwiftUI in the way it worked. Even if you still had to use Interface Builder to do it.&lt;/p&gt;
&lt;p&gt;Interface Builder&#39;s quirks lead to a category of programmers who felt the need to write all their interface in code. To my way of thinking, this is a huge waste of time and you still had to wrestle with Auto Layout, but without the visual clues! But if it makes them happy...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/xcode/swiftui/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://developer.apple.com/assets/elements/icons/swiftui/swiftui-96x96_2x.png&quot; alt=&quot;SwiftUI&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;What Is SwiftUI?&lt;/h2&gt;
&lt;p&gt;A &amp;quot;declarative UI framework&amp;quot; - what does that mean? Basically, declarative programming is a way of specifying what a program should do, rather than specifying how to do it. A common example is making a sandwich. If you want a sandwich, you say to the sandwich maker, &amp;quot;Please can I have a ham, cheese and tomato sandwich&amp;quot;. You don&#39;t say &amp;quot;Get two pieces of bread, butter both of them, cut 3 slices of ham and 2 slices of cheese...&amp;quot;. They know how to make a sandwich so you only have to ask for what you want.&lt;/p&gt;
&lt;p&gt;Back in the Apple UI world, what if you want a button? Is it for an iPhone app? Then you need an UIButton with a certain type. Set its title, size, location, layout constraints. What are the default sizes for an iPhone button? What is the default font? What color should the text be? Now make a button for a tvOS app? That has to be huge! And what other settings does it have? But the thing is, iOS, macOS, tvOS etc. already &lt;strong&gt;KNOW&lt;/strong&gt; what their buttons should look like. So why not just tell the system &amp;quot;I want a button&amp;quot;? Let the system work out the details and you can get on with the cool app ideas.&lt;/p&gt;
&lt;p&gt;I&#39;m not going to turn this post into a SwiftUI tutorial, but I would like to thank and recommend the people that have really helped me get to grips with it, especially &lt;a href=&quot;https://www.hackingwithswift.com/articles/196/learn-swiftui-with-free-tutorials&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Paul Hudson of Hacking with Swift with his Learn SwiftUI tutorials&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/user/azamsharp/videos&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mohammad Azam&#39;s great YouTube series&lt;/a&gt;, and &lt;a href=&quot;https://designcode.io/swiftui-course&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Meng To&#39;s SwiftUI course at Design+Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The other great benefit of SwiftUI is that when used with the Combine framework, it gives you a reactive interface. There have been various third-party libraries for doing this: RxSwift, ReactiveSwift, ReactiveCocoa, Bond etc, but with Combine, Apple is baking this into the ecosystem, which makes it a lot easier to set up and means that it will stay up-to-date with the operating systems. A reactive interface is one where you can bind a UI element to a piece of data and then when the data changes, the user interface updates automatically. You can even bind both ways so that the user interface updates the data without you having to do a thing. Imagine how much boiler-plate code that can eliminate?&lt;/p&gt;
&lt;h2&gt;The Good, The Bad and The Ugly&lt;/h2&gt;
&lt;h3&gt;1. The Good&lt;/h3&gt;
&lt;p&gt;You get a lot of interface for very little code. Making tables is a joy without the masses of boilerplate code needed to set up data sources and delegates. The instant preview in the canvas makes iteration much easier. Being able to create something like a Picker and having SwiftUI render it in one of multiple different styles depending on the platform is magical.&lt;/p&gt;
&lt;h3&gt;2. The Bad&lt;/h3&gt;
&lt;p&gt;It&#39;s early days yet and SwiftUI has some quirks and bugs. Sometimes the canvas just stops responding, switching into live mode in the canvas can take a while and of course it is still in beta, so there will be breaking changes to come.&lt;/p&gt;
&lt;h3&gt;3. The Ugly&lt;/h3&gt;
&lt;p&gt;It is very easy to build up the Pyramid of Doom where you have multiple levels of nesting leading to the end of your code being a seemingly endless stream of closing curly braces. In SwiftUI, one tends to build from the inside out: make a Text view, embed it in a VStack so you can add another one, embed that stack in an HStack so you can add an Image and so on... But there is an answer: Command-click on one of your outer layers and select &amp;quot;Extract Subview&amp;quot;.&lt;/p&gt;
&lt;h2&gt;Coding the Layout&lt;/h2&gt;
&lt;p&gt;Earlier in the article, I spoke about programmers ignoring Interface Builder and laying out the interface in code. I don&#39;t think this is a good approach, so how is SwiftUI different? Firstly, you get immediate feedback of the UI you are coding, so you are not losing that visual benefit of using Interface Builder. Secondly, SwiftUI does much of the layout for you, so you are not hand-coding auto layouts or every last detail of every UI element. So as far as SwiftUI is concerned, I am a convert to programmatic layouts. But if not using SwiftUI, I would still stick to Interface Builder.&lt;/p&gt;
&lt;p&gt;Another point to consider here is that developers have long argued that if Apple wants the iPad to be seen as a &amp;quot;Pro&amp;quot; device, there should be a version of Xcode for iPad. One of the problems with this was Interface Builder - it is difficult to see how it could be made to work with a touch interface. But with SwiftUI and iPadOS, is it possible that Apple can make an iPad version of Xcode?&lt;/p&gt;
&lt;h2&gt;Should you use SwiftUI?&lt;/h2&gt;
&lt;p&gt;I hope that from my previous paragraphs, you have come away with a good feeling for why Apple made SwiftUI and why it is good for us as developers. Personally, I find it a joy to use even in these early beta stages, but some of the arguments against using Swift, can now be applied again to SwiftUI.&lt;/p&gt;
&lt;p&gt;That SwiftUI is very new cannot be debated. It is extremely new and even in the handful of betas released since WWDC, we have seen some breaking changes. It feels quite buggy still but as I am running it in a beta operating system with a beta version of Xcode, it would be unfair to blame all the instability on SwiftUI.&lt;/p&gt;
&lt;p&gt;SwiftUI is not yet a complete replacement for UIKit/AppKit/WatchKit etc. There are SwiftUI equivalents for many of the interface elements and I recommend the &lt;a href=&quot;https://goshdarnswiftui.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gosh Darn SwiftUI site&lt;/a&gt; which maintains a list of UIKit equivalents as well as really useful snippets of SwiftUI code. But even when SwiftUI does not have the UI element you want, there are two easy options: use UIKit/AppKit or build your own. Apple has made sure that there is no problem about mixing and matching UIKIt/AppKit code with SwiftUI. And maybe the SwiftUI tools would allow you to build your own version of the missing element. I needed a progress bar and I built one myself with not much code and ended up with what I think is a better look than the standard one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trozware/swiftui_progressbar&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://troz.net/images/2019/SwiftUi_progressbar.png&quot; alt=&quot;Progress Bar&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Click the image for a link to the GitHub repository.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For years, Apple has been telling us to use standard interface elements in our apps and if we did this, when system updates changed the look and feel, we mostly got that change for free. Now they are giving us even more for free by saying effectively, tell us what UI element you want and we will do all the work of configuring it for the platform and for the user&#39;s settings.&lt;/p&gt;
&lt;p&gt;But with this, Apple has given us a lot of modifiers we can apply to elements as well as a great animation suite, so I expect to see a lot of more interesting design choices being made in apps, while they are still adhering to all the standard human interface guidelines for the various platforms.&lt;/p&gt;
&lt;p&gt;Apple does not promote SwiftUI as &amp;quot;write once, run everywhere&amp;quot; but as &amp;quot;learn once, apply everywhere&amp;quot; and that is an important distinction. It seems at first glance that we can write once but only on a basic level. You are still going to need to design the appropriate UI for a platform, but it will be able to re-use components from the other versions of the same app.&lt;/p&gt;
&lt;p&gt;Do I intend to use SwiftUI in my next app - YES. If I was writing code for NASA, I wouldn&#39;t, but my apps are rarely mission-critical, so I can indulge myself and learn as I go. It will be frustrating when there are breaking changes, but at the same time, my bug reports are going to make it better.&lt;/p&gt;
&lt;p&gt;One caveat is that SwiftUI apps will require the latest operating systems, iOS 13 and macOS 10.15 so if your app has to support older versions of the systems, you will have to wait until next year. But there is nothing to stop you building some test components and starting the learning process.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Learning Swift  - Tuples</title>
    <link href="https://troz.net/post/2019/learning-swift-tuples/" />
    <updated>2019-02-03T09:48:51Z</updated>
    <id>https://troz.net/post/2019/learning-swift-tuples/</id>
    <content type="html">&lt;p&gt;I sometimes like to focus on aspects of the Swift language that get less publicity, but that I think are really useful without adding a lot of complexity. This time I am looking at tuples.&lt;/p&gt;
&lt;p&gt;There are many different ways of collecting data together in Swift, but for small amounts of transitory data, I find tuples to be an extremely convenient data structure.&lt;/p&gt;

&lt;h2&gt;What is a Tuple?&lt;/h2&gt;
&lt;p&gt;A tuple is basically an array with a pre-determined length and unlike normal Swift arrays, it can hold a mix of data types.&lt;/p&gt;
&lt;p&gt;Here are some examples:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pair &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Jessie&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; coords &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; trained&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To define a tuple, you enclose the data elements in parentheses - normal brackets, not curly or square. Optionally, the elements can have a label attached as shown in the &lt;code&gt;pet&lt;/code&gt; example.&lt;/p&gt;
&lt;p&gt;Tuples can have any number of components, but if they get too long, a struct might be a better option. &lt;a href=&quot;https://github.com/realm/SwiftLint&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftLint&lt;/a&gt; will complain if you use tuples with more than 3 elements and usually I go along with that.&lt;/p&gt;
&lt;p&gt;There are various ways to access the parts of a tuple. The first way is using index numbers which works but isn&#39;t very intuitive and could be hard to decipher when you come back to a project after some time.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pair&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pair&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next way is if the tuple has used labels for the elements as in the &lt;code&gt;pet&lt;/code&gt; example above:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trained&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the way I prefer to use them is by deconstructing the parts into separate variables in a single statement:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; coords
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The elements of a tuple can be of different types but once a tuple has been defined, that type of each element is fixed, so you still get all the benefits of Swift&#39;s type safety.&lt;/p&gt;
&lt;p&gt;Option-clicking on the variable name shows the inferred type of the tuple:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/Tuple_type.png&quot; alt=&quot;The type of a tuple.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Trying to re-assign the tuple or any elements of the tuple will only work if the new assignments match the initial types for each element:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2019/Tuple_errors.png&quot; alt=&quot;Errors when mutating a tuple.&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Where could you use a tuple and why?&lt;/h2&gt;
&lt;p&gt;Let&#39;s imagine you have a function that checks a database or web service and comes back with the title of a movie and its rating.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getMovieInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; movieTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; rating&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// get the data from somewhere&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// now return both movieTitle and rating&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Having got the data, this function needs to return two pieces of data: &lt;code&gt;movieTitle&lt;/code&gt; and &lt;code&gt;rating&lt;/code&gt; - one &lt;code&gt;String&lt;/code&gt; and one &lt;code&gt;Int&lt;/code&gt;. How could this be done?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Array&lt;/strong&gt;: since there are 2 different data types, the array would have to be of type &lt;code&gt;[Any]&lt;/code&gt; or the &lt;code&gt;Int&lt;/code&gt; could be converted to a &lt;code&gt;String&lt;/code&gt; before returning. Neither of these are good options. Using &lt;code&gt;Any&lt;/code&gt; removes the protection of Swift&#39;s type safety and converting the &lt;code&gt;Int&lt;/code&gt; to and from a &lt;code&gt;String&lt;/code&gt; may fail, so then you have to deal with optionals.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dictionary&lt;/strong&gt;: the same arguments hold true. The dictionary would have to be of type &lt;code&gt;[String: Any]&lt;/code&gt; or the rating would have to be converted to a &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Struct or Class&lt;/strong&gt;: either of these would be fine but unless this is a data structure that is going to be re-used, it seems like overkill.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tuple&lt;/strong&gt;: this would get my vote, so let&#39;s see how that would work.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getMovieInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; movieTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; rating&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// get the data from somewhere&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;movieTitle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rating&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two main things to notice here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The return type of the function lists the data types of both the elements being returned, enclosed in parentheses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The returning tuple is created by wrapping the elements inside parentheses, just like in the examples above.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And then in the calling function, you can access the parts of the returned data like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;returnedMovieTitle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; returnedRating&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMovieInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tuple is being returned and immediately deconstructed to give 2 new variables so accessing the parts of the tuple is easy and type-safe.&lt;/p&gt;
&lt;p&gt;A function returning a tuple can also be configured to use named elements by specifying the names in the return type declaration.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getMovieInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; score&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; movieTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; rating&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// get the data from somewhere&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;movieTitle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rating&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMovieInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; returnedMovieTitle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; returnedRating &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My feeling is that once a tuple is complex enough to need names, you should really consider using a class or a struct, but for returning 2 or 3 chunks of data from a function, this is a very useful technique, espeicialy if the data types are different.&lt;/p&gt;
&lt;h2&gt;Looping through tuples&lt;/h2&gt;
&lt;p&gt;If you have an array of tuples, looping through the tuples is made very convenient if you use the deconstruction method of accessing the parts.&lt;/p&gt;
&lt;p&gt;As an example, suppose you are dealing with some 3D coordinate system and you want to perform an operation on each location.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; coords_array &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; coords_array &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; distanceFromOrigin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;z&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;distanceForOrigin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can deconstruct the tuple each time through the loop, assigning temporary variables so you can act on them.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;So those are the basics of tuples: how to create them, how to use them and where they might be useful. My main use is as a light-weight alternative to a struct for returning multi-part data from a function. But hopefully after reading this article, they have become another tool in your Swift tool belt that you can consider using in certain circumstances.&lt;/p&gt;
&lt;h3&gt;That leaves only one important question: how do you pronounce &#39;tuple&#39;?&lt;/h3&gt;
&lt;p&gt;I have heard two variations: &#39;toople&#39; using a long U as in universe or &#39;tupple&#39; using a short U as in cup. There does not appear to be any strict geographical differentiation, so take your pick. I prefer &#39;toople&#39; myself, but if somebody says &#39;tupple&#39;, I know what they mean.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The End of Pic-a-POD</title>
    <link href="https://troz.net/post/2018/the-end-of-picapod/" />
    <updated>2018-10-05T23:38:20Z</updated>
    <id>https://troz.net/post/2018/the-end-of-picapod/</id>
    <content type="html">&lt;p&gt;I think I wrote the first version of &lt;a href=&quot;https://picapod.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Pic-a-POD&lt;/a&gt; in 2003 and have been updating and maintaining it ever since, but the time has come to shut it down.&lt;/p&gt;

&lt;p&gt;I haven&#39;t been able to find a picture of the original version, but here is one from 2005:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/POD2005.png&quot; alt=&quot;Pic-a-POD 2005&quot;&gt;&lt;/p&gt;
&lt;p&gt;And here it is today:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/POD2018.webp&quot; alt=&quot;Pic-a-POD 2018&quot;&gt;&lt;/p&gt;
&lt;p&gt;It was written originally as a convenient way to download the daily picture-of-the-day from &lt;a href=&quot;https://www.nationalgeographic.com/photography/photo-of-the-day/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;National Geographic&lt;/a&gt; and use it to set the Mac&#39;s Desktop Picture automatically. From there is just grew with more sites added (9 at the moment although I think there have been up to 12 at certain times).&lt;/p&gt;
&lt;p&gt;The main issue was keeping up with the site changes of the various sources. Early versions had each copy of the app interrogate the sites directly which meant that any change to the site broke the app until an update could be released. And without the App Store making updates easy to distribute, this was a problem. Later I switched to having PHP scripts on my server do the data retrieval and store the results in a database. The app then just had to request the data from my server. That way if there was a change, I could react quickly and a fix to the server-side scripts allowed all users to get the new data.&lt;/p&gt;
&lt;p&gt;As my first Objective-C app and my first app on the Mac App Store, Pic-a-POD has always held a place in my affections, and until last week, it was the one app that was always running on my Mac. So what changed? macOS Mojave&#39;s dynamic desktops! I turned off Pic-a-POD, and set the Desktop picture to change dynamically through the day and I loved it.&lt;/p&gt;
&lt;p&gt;On the server, I have kept Pic-a-POD updated regularly, adding and removing sites, fixing the scripts to accommodate changes and so on. But the desktop app has languished and has needed an update for many years now. It&#39;s networking is primitive and I know so much more about programming for the Mac now that looking at the code is positiviely embarrassing! But it has never been a big seller so a re-write is definitely not economically viable, it would only be for my satisfaction. And if even I am not using it any more, what is the point?&lt;/p&gt;
&lt;p&gt;So I have made the sad decision to shut it down. But what does this mean for users of Pic-a-POD right now?&lt;/p&gt;
&lt;p&gt;Firstly, it is not going away immediately. I will remove it from the App Store, but existing copies are still going to work. If any of the source sites change their data structure, I will not be monitoring this and I will not update the server-side scripts - just turn off that source and carry on with whatever still works. And finally, when the picapod.com domain name expires in 2021, I will not renew it and the app will cease to operate.&lt;/p&gt;
&lt;p&gt;What can you use instead? The default dynamic desktops in Mojave are great although there are only two of them, but I have bought myself an app called &lt;a href=&quot;https://itunes.apple.com/au/app/24-hour-wallpaper/id1226087575?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;24 Hour Wallpaper&lt;/a&gt; which includes a great range of dynamic desktop pictures.&lt;/p&gt;
&lt;p&gt;So thank you to everyone who has used Pic-a-POD over the years and especially those of you who took the time and trouble to contact me. It has been a fun journey, but there are other apps in my future now.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Moving to the Dark Side - Part 2</title>
    <link href="https://troz.net/post/2018/moving-to-the-dark-side-2/" />
    <updated>2018-10-05T22:29:35Z</updated>
    <id>https://troz.net/post/2018/moving-to-the-dark-side-2/</id>
    <content type="html">&lt;p&gt;Now that most of my Mac apps have been updated to support macOS Mojave&#39;s dark mode, it was time to turn my attention to this web site.&lt;/p&gt;

&lt;p&gt;I build this site using &lt;a href=&quot;https://gohugo.io/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hugo&lt;/a&gt; and up until recently I used a theme called &lt;a href=&quot;https://github.com/olOwOlo/hugo-theme-even&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Even&lt;/a&gt; which I had been able to modify to suit my requirements. The story of how I switched to Hugo, what I wanted in a theme and how I went about, is in a &lt;a href=&quot;https://troz.net/post/2017/moving-to-hugo/&quot;&gt;post from last year&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But Evan is a theme with a white background and dark text, which looked great in light mode, but rather glaring in dark mode. So it was time to head for the &lt;a href=&quot;https://themes.gohugo.io&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hugo Themes page&lt;/a&gt; to look for an alternative.&lt;/p&gt;
&lt;p&gt;But which way to go? In my opinion, dark themes look better in Mojave&#39;s light mode than light themes look in dark mode, so I decided to go with a dark theme. But to my delight, I found a theme that allowed users to switch between light and dark modes: &lt;a href=&quot;https://themes.gohugo.io/hugo-theme-hello-friend/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hello Friend&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/panr&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;panr&lt;/a&gt;. Click the light bulb icon in the top right and the web site instantly toggles between modes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/Dark_Light_web.png&quot; alt=&quot;Dark and light versions of homepage&quot;&gt;&lt;/p&gt;
&lt;p&gt;With the spread of dark mode, I would expect more web sites and themes to implement this sort of switching in the future. Thanks to &lt;a href=&quot;https://twitter.com/panr&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;panr&lt;/a&gt; for being an early adopter.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As I said in my &lt;a href=&quot;https://troz.net/post/2018/moving-to-the-dark-side/&quot;&gt;previous post&lt;/a&gt;, I have already updated several of my macOS apps to support dark mode. But after reading some comments on Twitter, I have updated &lt;a href=&quot;https://itunes.apple.com/app/apple-store/id552293482&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Icon Builder&lt;/a&gt; and &lt;a href=&quot;https://itunes.apple.com/app/apple-store/id997688302&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Dice Pass&lt;/a&gt; to allow the user to decide which mode to use. Make your choice in the Window menu as to whether these apps use whatever mode is set for the system, or select your preferred mode.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/SelectMode.png&quot; alt=&quot;Select mode in Window menu&quot;&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Moving to the Dark Side</title>
    <link href="https://troz.net/post/2018/moving-to-the-dark-side/" />
    <updated>2018-09-26T06:49:43Z</updated>
    <id>https://troz.net/post/2018/moving-to-the-dark-side/</id>
    <content type="html">&lt;p&gt;With the release of macOS Mojave on 24th September 2018, I have started updating my Mac apps, mainly to enable support for dark mode if appropriate.&lt;/p&gt;
&lt;p&gt;And now I have updated this site too. Click the light bulb icon at the top right of the page to toggle between light and dark modes for this site.&lt;/p&gt;

&lt;p&gt;And with my apps, so far (1 st October 2018), I have updated &lt;a href=&quot;https://itunes.apple.com/app/apple-store/id552293482&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Icon Builder&lt;/a&gt;, &lt;a href=&quot;https://itunes.apple.com/app/apple-store/id509085586&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;World Time in Words&lt;/a&gt;, &lt;a href=&quot;https://itunes.apple.com/app/apple-store/id997688302&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Dice Pass&lt;/a&gt; and &lt;a href=&quot;https://itunes.apple.com/app/apple-store/id533321133&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;A Knight&#39;s Move&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/icon-builder/&quot;&gt;&lt;img src=&quot;https://troz.net/images/2018/IconBuilder5.1.png&quot; alt=&quot;Icon Builder 5.1&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[&lt;img src=&quot;https://troz.net/images/2018/WTiW_dark.png&quot; alt=&quot;World Time in Words 3.2&quot;&gt;&lt;a href=&quot;https://troz.net/time-in-words-for-mac/&quot;&gt;2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/dicepass/&quot;&gt;&lt;img src=&quot;https://troz.net/images/2018/DicePass1.5.png&quot; alt=&quot;Dice Pass 3.2&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/knightsmove/&quot;&gt;&lt;img src=&quot;https://troz.net/images/2018/KnightsMoveMac1.png&quot; alt=&quot;Knights Move 1.6&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For A Knight&#39;s Move, I wanted to make sure the wood-look background didn&#39;t change, so I actually disabled dark mode.
For any developers looking for the way to do that, I added the following chunk to the &lt;code&gt;applicationDidFinishLaunching(_:)&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token other-directive property&quot;&gt;#available&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OSX&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;NSApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appearance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSAppearance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;named&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aqua&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similar code could presumably be used to change the appearance of a running app. I may add the ability to switch to my apps later.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Swift Strings Helper</title>
    <link href="https://troz.net/post/2018/swift-strings-helper/" />
    <updated>2018-06-21T04:45:59Z</updated>
    <id>https://troz.net/post/2018/swift-strings-helper/</id>
    <content type="html">&lt;p&gt;Hands up everyone who can remember how to work with Swift strings, especially when it comes to substrings and ranges? ....&lt;/p&gt;
&lt;p&gt;Nobody?&lt;/p&gt;
&lt;p&gt;Me neither. So I decided that I would work it out once and create myself a library to make it easy for myself in the future. And then I thought that other people might like it too. So I have created my first open sourced project.&lt;/p&gt;

&lt;h2&gt;Strings in Swift&lt;/h2&gt;
&lt;p&gt;Swift&#39;s string handling is amazing. It is truly Unicode-compliant, so a Swift &lt;code&gt;String&lt;/code&gt; doesn&#39;t care whether your string is made up of basic alphanumerics, accented characters, or emojis that might be composed of several different emojis joined together.&lt;/p&gt;
&lt;p&gt;But this power comes at a price, and every version of Swift has changed the way we interact with strings, seemingly making it more and more confusing for the poor programmers trying to stay current. I have got to the stage where each year I read up on the new String features and then promptly forget them. For every use, I have to go back and search how to do what should be simple.&lt;/p&gt;
&lt;h2&gt;The Problems&lt;/h2&gt;
&lt;p&gt;To my mind, there are two main problems: indexes and substrings.&lt;/p&gt;
&lt;p&gt;In most languages, you can get the n-th character of a string, but not in Swift. In Swift, you have to ask the string for its startIndex (or endIndex), then use an offset to adjust that index by a certain number to give you a &lt;code&gt;String.Index&lt;/code&gt;. Do much the same to get a second index and then you can grab the string between those two indices.&lt;/p&gt;
&lt;p&gt;To get a sub-string from the 7th up to the 11th character of a string, this is what you have to do:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; str &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, playground&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStart &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; offsetBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subEnd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; offsetBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; str&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;subStart &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; subEnd&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// &quot;play&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Splitting the lines up like that at least helps to show what is happening, but then you see abominations like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sub &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; str&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; offsetBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; offsetBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And how anyone is supposed to read that, I really do not know.&lt;/p&gt;
&lt;p&gt;And now here is the kicker: the results (&lt;code&gt;subStr&lt;/code&gt; and &lt;code&gt;sub&lt;/code&gt; in the examples above) are not of type &lt;code&gt;String&lt;/code&gt;! They are of type &lt;code&gt;Substring&lt;/code&gt; so when you go to use them, they don&#39;t work as expected.&lt;/p&gt;
&lt;p&gt;I guess there are cases for using &lt;code&gt;Substring&lt;/code&gt; but I always just end up casting to &lt;code&gt;String&lt;/code&gt; to solve this, but only after the compiler has choked on what I thought was a &lt;code&gt;String&lt;/code&gt; all along.&lt;/p&gt;
&lt;h2&gt;The Solution&lt;/h2&gt;
&lt;p&gt;To make my life easier, I worked out all the ways that I wanted to be able to split up strings. Basically just two ways: by character number or by substring. Swift already has a &lt;code&gt;substring&lt;/code&gt; method but it uses &lt;code&gt;String.Index&lt;/code&gt; as shown above. To avoid confusion, I named my functions all &lt;code&gt;sub&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But now I can do the following to split strings by character number:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;upTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;upTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; upTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; upTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Negative numbers count back from the end of the string. And they all return &lt;code&gt;String&lt;/code&gt; objects!&lt;/p&gt;
&lt;p&gt;Or to split strings by their own sub-strings, I can do this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;upTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;xyz&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startingString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; upTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;xyz&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Extras&lt;/h2&gt;
&lt;p&gt;Once I had created a string helper library, I started thinking about all the other string utilities that would be useful. So I started adding all sorts of facilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Computed properties:
&lt;ul&gt;
&lt;li&gt;length (why should the length of a string be called count - that makes no sense)&lt;/li&gt;
&lt;li&gt;words&lt;/li&gt;
&lt;li&gt;lines&lt;/li&gt;
&lt;li&gt;word count&lt;/li&gt;
&lt;li&gt;line count&lt;/li&gt;
&lt;li&gt;title case&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encoding:
&lt;ul&gt;
&lt;li&gt;URL encoding &amp;amp; decoding for queries or forms&lt;/li&gt;
&lt;li&gt;base64 encoding &amp;amp; decoding&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Trim:
&lt;ul&gt;
&lt;li&gt;trim&lt;/li&gt;
&lt;li&gt;trim left&lt;/li&gt;
&lt;li&gt;trim right&lt;/li&gt;
&lt;li&gt;trim using extra characters&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pad:
&lt;ul&gt;
&lt;li&gt;pad left&lt;/li&gt;
&lt;li&gt;pad right&lt;/li&gt;
&lt;li&gt;with default space or specified other character&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s as far as I have got so far, but I am hoping for some community involvement that will expand or edit this library to make it more broadly applicable.&lt;/p&gt;
&lt;h2&gt;Open Source&lt;/h2&gt;
&lt;p&gt;I have never created an open source project before and I have rarely contributed to open source. But I have now published this library on GitHub under an MIT license. Please check it out at &lt;a href=&quot;https://github.com/trozware/swift-strings-helper&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;https://github.com/trozware/swift-strings-helper&lt;/a&gt;. The GitHub repo contains an Xcode project with all the source files, plus the targets to build frameworks for macOS or iOS, and a playground as documentation and to test it all out.&lt;/p&gt;
&lt;p&gt;I would love to get as many stars as possible and it would be fantastic if anyone wanted to log an issue or contribute directly. As someone who finds the whole open source world rather intimidating, I would like to assure everyone that there will be no flaming and no shooting down of ANY ideas. I look forward to hearing from you.&lt;/p&gt;
&lt;p&gt;I am sure there are other Swift libraries out there dedicated to solving the same problems, but I hope that mine can prove useful to some. And if you just want to use it without contributing, feel free. The usage instructions are all in the ReadMe on the GitHub page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Functional Programming in Swift</title>
    <link href="https://troz.net/post/2018/functional-programming/" />
    <updated>2018-06-14T04:18:53Z</updated>
    <id>https://troz.net/post/2018/functional-programming/</id>
    <content type="html">&lt;p&gt;What is Functional Programming and how can we use it in Swift?&lt;/p&gt;
&lt;p&gt;Search online for any definition of functional programming and you will find many different definitions, few of which are practically helpful. I have no claim to be an expert, but as a Swift enthusiast, this is what I have distilled out of the morass.&lt;/p&gt;

&lt;h2&gt;What is Functional Programming?&lt;/h2&gt;
&lt;p&gt;Without providing a concrete definition, here are what I see as the 3 main goals of functional programming:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use pure functions where possible&lt;/li&gt;
&lt;li&gt;avoid mutability where possible&lt;/li&gt;
&lt;li&gt;use functions as the basic building blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So let&#39;s go through those one by one and see how they fit into the Swift language.&lt;/p&gt;
&lt;h2&gt;Functional Programming in Swift&lt;/h2&gt;
&lt;p&gt;You can &lt;a href=&quot;https://github.com/trozware/functional-programming&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;download a playground containing all these examples from GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Pure functions&lt;/h3&gt;
&lt;p&gt;A function is considered pure if it will always produce the same result for the same input, regardless of where it is and what calls it.&lt;/p&gt;
&lt;p&gt;Imagine you are writing a role-playing game and for a given fight, you need to be able to calculate the damage per second caused by a character.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DamageDealer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; damageDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; timeTaken&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeInterval&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;damagePerSecond&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; timeTaken &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; dps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damageDone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; timeTaken
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; dps &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; dps
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; mage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DamageDealer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
mage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;damageDone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;
mage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timeTaken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;

mage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;damagePerSecond&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;damagePerSecond&lt;/code&gt; function takes no parameters but uses the properties of its containing object. This works in this class, but there are 3 big problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The function is not transportable - you cannot copy it into another class as it is totally dependent on the structure of the properties in the containing class.&lt;/li&gt;
&lt;li&gt;When calling the function, it is not clear what data it is going to use.&lt;/li&gt;
&lt;li&gt;This function is difficult to test as calling the function with the same parameters (none) will produce different results depending on the setup.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So for a version that uses a pure function, we could replace &lt;code&gt;damagePerSecond()&lt;/code&gt; with this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;damagePerSecondPure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; time &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; dps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; time
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; dps &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; dps
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

mage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;damagePerSecondPure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;damageDone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timeTaken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling the function is now more verbose, but reading the call gives you much more information about what is going to happen. Testing is easy, and the function is completely self-contained so can be copied into any class or struct.&lt;/p&gt;
&lt;h3&gt;Avoid mutability&lt;/h3&gt;
&lt;p&gt;This one has become the poster child of Swift Functional Programming as Swift provides some very convenient ways to avoid mutability.&lt;/p&gt;
&lt;p&gt;The first is &lt;code&gt;let&lt;/code&gt; versus &lt;code&gt;var&lt;/code&gt;. My rule is always to start defining any variable/constant with &lt;code&gt;let&lt;/code&gt; and only changing to &lt;code&gt;var&lt;/code&gt; if the compiler raises an error. In the current versions of Xcode, it will give a warning if you use &lt;code&gt;var&lt;/code&gt; unnecessarily which is great, but I still stick to using &lt;code&gt;let&lt;/code&gt; first.&lt;/p&gt;
&lt;p&gt;The most powerful way Swift lets us avoid mutability with Functional Programming is with &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;reduce&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Filter&lt;/h4&gt;
&lt;p&gt;Consider this function that checks possible player names:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;checkPlayerNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; validNames&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; names &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            validNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; validNames
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; allNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Slinky&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Buzz Lightyear&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hamm&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; checkedNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkPlayerNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; allNames&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only names with more than 3 characters and no spaces are considered valid. So this function creates an empty array and then loops through each member of the supplied array and appends any valid names to the new array before returning it.&lt;/p&gt;
&lt;p&gt;This function is a pure function and it works as expected. But the &lt;code&gt;validNames&lt;/code&gt; array is mutable and there is no need for it to be.&lt;/p&gt;
&lt;p&gt;Converting this to avoid mutability, we get:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;checkPlayerNamesUsingFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; validNames
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the &lt;code&gt;filter&lt;/code&gt; closure delimited by the curly braces after the word &lt;code&gt;filter&lt;/code&gt;, (more about closures below), the element in the array being evaluated is stored in the &lt;code&gt;name&lt;/code&gt; constant. The checks are done and this implicitly returns a &lt;code&gt;Bool&lt;/code&gt; - true if the checks pass, false if they do not. If the closure returns true, the name is valid and will be part of the validNames array.&lt;/p&gt;
&lt;p&gt;And if you really want to be concise:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;checkPlayerNamesUsingFilterShort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I recommend the first method even if it is a bit more verbose. Storing the result in a constant before returning it makes debugging much easier. Using $0 instead of using a named parameter is convenient, but I prefer not to do this unless the closure is very simple.&lt;/p&gt;
&lt;h4&gt;Map&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;filter&lt;/code&gt; takes an array of objects and returns a sub-array containing every element which returned true for the checks inside the filter body.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt; changes the elements in an array and can return an array of the same type or an array of different types.&lt;/p&gt;
&lt;p&gt;Here is a function to square every integer in an array in the old style, using a mutable array to accumulate the result:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;squareNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; squares&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; number &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; numbers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        squares&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; squares
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; numbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;squareNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And doing the same thing using &lt;code&gt;map&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;squareNumbersUsingMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; squares &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; squares
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, the type of the data did not change: integers went in, integers came out.
But &lt;code&gt;map&lt;/code&gt; can change the type as well.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;squareRoots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; roots &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; number &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; roots
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;squareRoots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there is a final twist to &lt;code&gt;map&lt;/code&gt; that used to be called &lt;code&gt;flatMap&lt;/code&gt; but is now called &lt;code&gt;compactMap&lt;/code&gt; and that allows us to get rid of optionals as we map through an array.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;convertStringsToInts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; strings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; strings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compactMap &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ints
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; strings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;0.34&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;65&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-93&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4e8&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;convertStringsToInts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strings&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The conversion of &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;Int&lt;/code&gt; may fail and so returns an optional. If this function had used &lt;code&gt;map&lt;/code&gt; instead of &lt;code&gt;compactMap&lt;/code&gt;, the result would have been an array of optional Ints: &lt;code&gt;[Int?]&lt;/code&gt;. By using &lt;code&gt;compactMap&lt;/code&gt;, every &lt;code&gt;nil&lt;/code&gt; value was dropped and only valid integers are included.&lt;/p&gt;
&lt;h4&gt;Reduce&lt;/h4&gt;
&lt;p&gt;The final tool in the immutability toolbox is &lt;code&gt;reduce&lt;/code&gt; and this is one that took me a while to wrap my head around.&lt;/p&gt;
&lt;p&gt;Imagine that you wanted to add up all the integers in an array. Here is a way to do it using a mutable variable and a loop:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sumNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; num &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; numbers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        total &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; num
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; total
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; numbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sumNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can&#39;t use &lt;code&gt;filter&lt;/code&gt; or &lt;code&gt;map&lt;/code&gt; here because I want to end up with a single value after applying some logic to every item in the array. So here is where I use &lt;code&gt;reduce&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sumNumbersUsingReduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cumulativeTotal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nextValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cumulativeTotal &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; nextValue
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; total
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sumNumbersUsingReduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;reduce&lt;/code&gt; function takes 2 parameters. The first is the starting value - in this case it is zero. The second paramter is a function (I am using a closure) this in turn takes 2 parameters and here is where it gets complicated. Inside the closure function, the 2 parameters are the current result and the next value from the loop. And what you return from this closure is going to be the new cumulative value which will either be fed back into the loop, or returned as the final result. The first time through the loop, the first parameter will be the initial value as set in the reduce function call.&lt;/p&gt;
&lt;p&gt;To see how this happens, here is a version sprinkled with &lt;code&gt;print&lt;/code&gt; statements showing what happens each time through the loop:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sumNumbersReduceDebug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cumulativeTotal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nextValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cumulativeTotal = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;cumulativeTotal&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;nextValue = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;nextValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;about to return &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;cumulativeTotal&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; + &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;nextValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;cumulativeTotal &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; nextValue&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; which will become the next culmulative or the final value&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cumulativeTotal &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; nextValue
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;final result = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; total
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; shortNumbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sumNumbersReduceDebug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;shortNumbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produces a log showing:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cumulativeTotal = 0
nextValue = 5
about to return 0 + 5 = 5 which will become the next culmulative or the final value
cumulativeTotal = 5
nextValue = 3
about to return 5 + 3 = 8 which will become the next culmulative or the final value
cumulativeTotal = 8
nextValue = 8
about to return 8 + 8 = 16 which will become the next culmulative or the final value
final result = 16&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using functions as building blocks&lt;/h3&gt;
&lt;p&gt;This one is more a matter of style than of any particular programming technique. Basically, keep each function small and break your code into small chunks with obvious naming. This makes your code easier to read, test and debug and it beomes vastly more reusable.&lt;/p&gt;
&lt;p&gt;Consider this totally made-up function:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;configureDisplay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; userId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userId &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;showLoginScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;displayUserData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; userType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPermissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;populateMenus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; userType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;loadInitialData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;playSound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;welcome&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;configureDisplay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc123&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;configureDisplay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is it easy to read?
Can you work out what it does? Now imagine all that functionality in a single huge function - would that be as good to use?&lt;/p&gt;
&lt;p&gt;As a way of encouraging shorter functions, which leads inevitably to this sort of structured code, I strongly recommend using SwiftLint to check your code. I wrote a &lt;a href=&quot;https://troz.net/post/2018/swiftlint/&quot;&gt;post about this&lt;/a&gt; a while ago which you might find useful.&lt;/p&gt;
&lt;h4&gt;Naming&lt;/h4&gt;
&lt;p&gt;The other key thing to mention and it is a point that Apple makes very strongly, is to name your functions and their parameters so as to make them as readable as possible from the calling site. You write a function once, but you most likely call it multiple times, so it is the calling site that needs to be really easy to read.&lt;/p&gt;
&lt;p&gt;Returning to the game example, here is a dummy function to show damage caused to a target:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;displayDamage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;displayDamage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ogre&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is nothing really wrong with the function, but calling it is a bit clunky and doesn&#39;t read well with the repeated use of the word &#39;damage&#39;.&lt;/p&gt;
&lt;p&gt;What about this version?&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doneTo target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;damage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doneTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Wolf&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are no repeated words in the caller and by using two labels for the second parameter, the calling site can read almost like a sentence, but inside the function, &lt;code&gt;target&lt;/code&gt; is still a more logical name.&lt;/p&gt;
&lt;p&gt;A third alternative is to use an un-named parameter if the naming logic is implicit in the function name itself:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;displayDamage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; damage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doneTo target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;displayDamage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doneTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Orc&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Closures&lt;/h2&gt;
&lt;p&gt;As promised above, a very quick explanation of closures, which really deserve their own post...&lt;/p&gt;
&lt;p&gt;In Swift, as in many languages, functions can be passed as parameters to other functions. As an example, I have set up 2 functions to perform a simple calculation on a given integer:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cube&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; number
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cube&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; number
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now imagine that you wanted to create a more general function that could call either one of these functions with any number:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doCalculation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; calculation&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;doCalculation&lt;/code&gt; takes 2 parameters. The first one is easy - it is just an integer. The second one is weird! For every parameter of a function, you have to supply the type of that parameter. Usually this is quite straight-forward: Int, String, MyCustomClass etc. But what is the &lt;code&gt;type&lt;/code&gt; of a function? Option-clicking on the word &lt;code&gt;cube&lt;/code&gt; in my function definition, I see this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/FunctionType.png&quot; alt=&quot;Function type&quot;&gt;&lt;/p&gt;
&lt;p&gt;And ignoring the parameter labels, this basically provides the function type: Int inside parentheses for the input, then the arrow, then Int again for the return type. So the type definition for the &lt;code&gt;cube&lt;/code&gt; function is &lt;code&gt;(Int) -&amp;gt; Int&lt;/code&gt;. And when I define the type for the calculation parameter in the &lt;code&gt;doCalculation&lt;/code&gt; function, this is exactly what I put. The last part of the function definition is specifiying the overall return type as an Int.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;cube&lt;/code&gt; and &lt;code&gt;square&lt;/code&gt; functions inside &lt;code&gt;doCalculation&lt;/code&gt; works like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token function&quot;&gt;doCalculation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; calculation&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; square&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;doCalculation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; calculation&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cube&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But what if I didn&#39;t want to define all the functions I might call in advance? Then I can send the function body to the &lt;code&gt;doCalculation&lt;/code&gt; function instead of using a pre-built function. This way of using a function inside another function is referred to as a closure.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token function&quot;&gt;doCalculation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; calculation&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; number &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;doCalculation&lt;/code&gt; function in unchanged, but instead of passing it a reference to a function, I am directly passing it the instructions it should use to get the result. As with any function, the instructions are contained within a set of curly braces. The input to this function is listed after the opening curly brace followed by the keyword &lt;code&gt;in&lt;/code&gt;. Then the function body does whatever it needs to and returns the result.&lt;/p&gt;
&lt;p&gt;You may have heard the term &lt;code&gt;trailing closure&lt;/code&gt;. This refers to a function where the last parameter is a function. If that function is called using a closure, there is a short-hand way of writing this, omitting the closure&#39;s parameter name and moving the closing parenthesis to before the opening curly brace.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token function&quot;&gt;doCalculation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; number &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;reduce&lt;/code&gt; functions I showed above, this is the way their logic was supplied but here is how the filter example would look without using a closure:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;checkName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;checkPlayerNamesUsingFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;checkName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; validNames
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; allNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Slinky&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Buzz Lightyear&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hamm&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; checkedNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkPlayerNamesUsingFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; allNames&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which methods you use are up to you - they all work. If you have a function that will be called in many different places, maybe it makes more sense to define it once and pass around a reference to that function. If not, a closure has the advantage that it keeps everything together. There is more to closures, particularly to do with variable scope, but I think this post has gone on long enough already.... maybe next time.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>World Time In Words iOS Update</title>
    <link href="https://troz.net/post/2018/world-time-in-words-ios-update/" />
    <updated>2018-06-14T00:41:46Z</updated>
    <id>https://troz.net/post/2018/world-time-in-words-ios-update/</id>
    <content type="html">&lt;p&gt;Its been a long time since the last update to Time In Words for iOS but after prompting by Apple (&lt;em&gt;update or we will remove it from the App Store&lt;/em&gt;), I finally did it. I was never really happy with the last design of Time in Words for iOS, particularly for the iPad, so this was a good opportunity to do a complete re-design, bringing the app more into alignment with its Mac counterpart. And it got a new name to match its new direction and is now called World Time in Words.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://itunes.apple.com/app/time-in-words/id498403851&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;World Time in Words v 5.0&lt;/a&gt; is available from the App Store.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In 2015, I released &lt;a href=&quot;https://troz.net/post/2015/time-in-words-4.0/&quot;&gt;Time In Words v 4.0&lt;/a&gt; for iOS. This was a big change as I had dropped the original QlockTwo concept and switched the emphasis to world time conversions that would be easy to read and understand. This bit worked out well, but the paging interface was not a great success - it made it too tedious to find the zone you were interested in.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://troz.net/time-in-words-for-mac/&quot;&gt;Mac version&lt;/a&gt; is a menu bar app that shows the selected time zones as separate menu items. iOS apps don&#39;t have menus, but the closest thing to this is a table, so that is what I went with.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/WTiW-iOS.jpg&quot; alt=&quot;Basic display&quot;&gt;&lt;/p&gt;
&lt;p&gt;As before, you can add, remove or re-order the time zones and the top three zones can be shown in your Today widgets.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/WTiW-iOS-1.jpg&quot; alt=&quot;Edit zones&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/WTiW-iOS-4.jpg&quot; alt=&quot;Today widget&quot;&gt;&lt;/p&gt;
&lt;p&gt;But the feature that I use most in the Mac app is &amp;quot;What time will it be when...&amp;quot;. In the iOS app, for reasons of space, the button is labelled &amp;quot;What time will it be?&amp;quot; but the functionality is the same: drag the slider to adjust the hour in your local time zone and see what that hour will be in your selected world zones. This is incredibly useful when you have family, friends and colleagues all over the world and really don&#39;t want to call them at the wrong time of day.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/WTiW-iOS-2.jpg&quot; alt=&quot;What time will it be&quot;&gt;&lt;/p&gt;
&lt;p&gt;The iPad version of the app is identical at this stage, but I am thinking of adjusting the interface so as to take advantage of the increased real estate. If other work doesn&#39;t get in the way, this may happen...&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Privacy Policy</title>
    <link href="https://troz.net/post/2018/privacy-policy/" />
    <updated>2018-05-25T23:51:56Z</updated>
    <id>https://troz.net/post/2018/privacy-policy/</id>
    <content type="html">&lt;p&gt;TL;DR: I don&#39;t track you either on my web site or through my apps. If you want to contact me, please do so but I will not initiate any contacts.&lt;/p&gt;

&lt;p&gt;As anyone who gets email would realise by now, the EU has introduced a &lt;a href=&quot;https://www.eugdpr.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;General Data Protection Regulation&lt;/a&gt; designed to enhance online privacy. You will have been getting lots of emails announcing new privacy policies or asking you to opt-in to existing arrangements.&lt;/p&gt;
&lt;p&gt;So here is my version of those emails (which you will not get by email because I do not store any user data):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;None of my apps store data off your device.&lt;/li&gt;
&lt;li&gt;I do not have any user data except for data specifically sent to me via web forms, direct email or in-app email.&lt;/li&gt;
&lt;li&gt;If you have signed up to beta test one of my apps, you did give me your email address. No tests are currently on-going, but please contact me if you would rather I removed you from any future beta tests.&lt;/li&gt;
&lt;li&gt;My web sites are now totally free of trackers and analytics software.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of my apps have a &amp;quot;Contact the Developer&amp;quot; button that creates an email that I will answer. If you do not want me to have your name and email address, do not use these buttons.&lt;/p&gt;
&lt;p&gt;My web site has a &lt;a href=&quot;http://localhost:1313/contact/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;contact page&lt;/a&gt; - if you use it, I will see your name and email address and I will respond. Again, if you do not want me to have this data, do not use this page.&lt;/p&gt;
&lt;p&gt;Since I no longer have any analytics, I would really appreciate hearing if you find any of my posts useful or interesting.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>App Updates</title>
    <link href="https://troz.net/post/2018/app-updates/" />
    <updated>2018-05-25T23:22:57Z</updated>
    <id>https://troz.net/post/2018/app-updates/</id>
    <content type="html">&lt;p&gt;Just a quick post to let you know about some recent app updates...&lt;/p&gt;

&lt;h2&gt;Man Reader&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Man Reader v 1.10&lt;/a&gt; was released on 11 May 2018.&lt;/p&gt;
&lt;p&gt;I had to update &lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader&lt;/a&gt; in November 2017 because of a really strange &amp;quot;feature&amp;quot; that arrived with macOS High Sierra. Man Reader displays the man pages as HTML which allows the tabs and internal links to work using anchor tags. These had worked for years, but under High Sierra, they just stopped working.&lt;/p&gt;
&lt;p&gt;I eventually discovered that it was because I was loading the text directly into the WebView using:&lt;/p&gt;
&lt;pre class=&quot;language-objectivec&quot;&gt;&lt;code class=&quot;language-objectivec&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;web&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mainFrame loadHTMLString&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;manText baseURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;nil&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With no baseURL, the anchor links were all defaulting to &lt;code&gt;about:blank&lt;/code&gt; and going nowhere. The solution was to save the text to a temporary file and then load using:&lt;/p&gt;
&lt;pre class=&quot;language-objectivec&quot;&gt;&lt;code class=&quot;language-objectivec&quot;&gt;NSURLRequest &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;NSURLRequest requestWithURL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tempFile&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;web&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mainFrame loadRequest&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Needless to say, this took me ages to work out, including a lengthy excursion into using &lt;code&gt;WKWebView&lt;/code&gt; to see if the more modern web view would solve the problem.&lt;/p&gt;
&lt;p&gt;The second update (May 2018) was in response to a crash report from a user. I had great difficulty tracking this one down and even now, I cannot see how it can have happened, but it was in relation to the utility apps that are displayed in the toolbar.&lt;/p&gt;
&lt;p&gt;For each app, Man Reader checks to see if the app bundle exists, then it checks for a bundle identifier. It appears that this can come back as nil which I had not realised. And trying to insert an item with a identifier of &lt;code&gt;nil&lt;/code&gt; into the toolbar caused a crash.&lt;/p&gt;
&lt;p&gt;As you can see from the code, ManReader is written in Objective-C. Going back to Objective-C from Swift is painful! And I know that this crash could not have happened if the app had been written in Swift as the identifier would have been an optional and I would have been forced to check that it was not nil before using.&lt;/p&gt;
&lt;p&gt;On the plus side, Objective-C apps are tiny in comparison to Swift apps. And the Mac App Store review process set a new record for me. I submitted the app at 10:58 am and it was on sale at 12:03 pm the same day - 66 minutes from start to finish.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Sequenza VII&lt;/h2&gt;
&lt;p&gt;The other app that I updated recently is &lt;a href=&quot;https://itunes.apple.com/us/app/sequenza-vii/id730234638?mt=8&amp;amp;uo=4&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Sequenza VII&lt;/a&gt;. Version 1.2 was released on 23 April 2018.&lt;/p&gt;
&lt;p&gt;This is an app with a very limited audience - specifically oboe players who want to learn to play Berio&#39;s weird music. However Apple sent me a notice saying that as it hadn&#39;t been updated for ages, it would be removed from the store within 30 days unless I did something.&lt;/p&gt;
&lt;p&gt;Updating it to use the iPhone X display was surprisingly difficult. I set the minimum system version to 11.0 and changed the storyboards to use the latest Xcode but the horns on either side of the notch were still being left blank. In the end, the trick I found was to create a new Launch storyboard - that seemed to fool the system into re-considering all the layouts.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Consistent Swift Style</title>
    <link href="https://troz.net/post/2018/swiftlint/" />
    <updated>2018-03-30T23:35:21Z</updated>
    <id>https://troz.net/post/2018/swiftlint/</id>
    <content type="html">&lt;ul&gt;
&lt;li&gt;Why is important to style your code?&lt;/li&gt;
&lt;li&gt;How do you decide on a style?&lt;/li&gt;
&lt;li&gt;Is there a way to enforce this style automatically?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;What is style in Swift?&lt;/h2&gt;
&lt;p&gt;Style applies to writing in any programming language and refers to the way you structure your code.&lt;/p&gt;
&lt;p&gt;For example, how to you arrange braces?&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; theValue &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do one thing&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do the other thing&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; theValue &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do one thing&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do the other thing&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or anything in between?&lt;/p&gt;
&lt;p&gt;Do you use camelCase or snake_case?&lt;/p&gt;
&lt;p&gt;Do you always start class names with an uppercase letter?&lt;/p&gt;
&lt;p&gt;And of course there is the Great Debate: tabs or spaces? And how many?&lt;/p&gt;
&lt;h2&gt;Why is important to style your code?&lt;/h2&gt;
&lt;p&gt;You only write code once, but you &amp;amp; others will read it many times.&lt;/p&gt;
&lt;p&gt;As I tell students, in six months time some poor sucker is going to have to read this code, and worse still, that poor sucker might be you, so make it readable.&lt;/p&gt;
&lt;p&gt;There is a cognitive load involved in reading code but you can lighten that load by using consistent patterns that your brain can recognise quickly without having to re-analyse them each time.&lt;/p&gt;
&lt;h2&gt;How do you decide on a style?&lt;/h2&gt;
&lt;p&gt;Do a search online for &lt;a href=&quot;https://encrypted.google.com/search?hl=en&amp;amp;q=Swift%20style%20guide&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift style guide&lt;/a&gt; and you will get numerous results, although you do have to dodge the sites dedicated to Taylor Swift&#39;s dress sense! I like the &lt;a href=&quot;https://github.com/raywenderlich/swift-style-guide&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ray Wenderlich guide&lt;/a&gt; although its emphasis is on styles that read well in print and in web pages. The &lt;a href=&quot;https://github.com/linkedin/swift-style-guide&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;LinkedIn guide&lt;/a&gt; is also good. &lt;a href=&quot;https://github.com/github/swift-style-guide&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; also has a style guide but it hasn&#39;t been updated recently. However it contains some good general advice.&lt;/p&gt;
&lt;p&gt;But in the end, you have to decide your own style. The important thing then is to be consistent. I don&#39;t care whether you use camelCase or snake_case, tabs or spaces, but if you use a mixture your code will be harder to read, so pick a style and stick to it.&lt;/p&gt;
&lt;h2&gt;Is there a way to enforce this style automatically?&lt;/h2&gt;
&lt;p&gt;This is where &lt;a href=&quot;https://github.com/realm/SwiftLint&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftLint&lt;/a&gt; comes into the picture. SwiftLint is a tool that can check your code and look for style violations. While it comes with a default set of rules, you can configure these rules to suit your own style.&lt;/p&gt;
&lt;h3&gt;Installing and Running SwiftLint&lt;/h3&gt;
&lt;p&gt;To install SwiftLint, I recommend using &lt;a href=&quot;https://brew.sh&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;HomeBrew&lt;/a&gt;. Once you have HomeBrew installed, open Terminal and type &lt;code&gt;brew install swiftlint&lt;/code&gt;. You can update it any time using &lt;code&gt;brew upgrade swiftlint&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can run SwiftLint directly from the Terminal. &lt;code&gt;cd&lt;/code&gt; to the folder containing your Xcode project and type &lt;code&gt;swiftlint&lt;/code&gt;. For a brand new iOS project, you will probably get a result similar to this:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Linting Swift files in current working directory
Linting &#39;ViewController.swift&#39; (1/2)
Linting &#39;AppDelegate.swift&#39; (2/2)
ViewController.swift:23: warning: Vertical Whitespace Violation: Limit vertical whitespace to a single empty line. Currently 2. (vertical_whitespace)
ViewController.swift:25: warning: Trailing Newline Violation: Files should have a single trailing newline. (trailing_newline)
AppDelegate.swift:16: warning: Vertical Whitespace Violation: Limit vertical whitespace to a single empty line. Currently 2. (vertical_whitespace)
AppDelegate.swift:44: warning: Vertical Whitespace Violation: Limit vertical whitespace to a single empty line. Currently 2. (vertical_whitespace)
AppDelegate.swift:46: warning: Trailing Newline Violation: Files should have a single trailing newline. (trailing_newline)
AppDelegate.swift:17: warning: Line Length Violation: Line should be 120 characters or less: currently 144 characters (line_length)
AppDelegate.swift:23: error: Line Length Violation: Line should be 120 characters or less: currently 285 characters (line_length)
AppDelegate.swift:24: warning: Line Length Violation: Line should be 120 characters or less: currently 159 characters (line_length)
AppDelegate.swift:28: error: Line Length Violation: Line should be 120 characters or less: currently 218 characters (line_length)
AppDelegate.swift:29: warning: Line Length Violation: Line should be 120 characters or less: currently 141 characters (line_length)
AppDelegate.swift:33: warning: Line Length Violation: Line should be 120 characters or less: currently 155 characters (line_length)
AppDelegate.swift:37: warning: Line Length Violation: Line should be 120 characters or less: currently 194 characters (line_length)
AppDelegate.swift:41: warning: Line Length Violation: Line should be 120 characters or less: currently 128 characters (line_length)
Done linting! Found 13 violations, 2 serious in 2 files.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;I have removed the full path that will be listed for each file just to make this fit better.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;These reports show the file and line number, whether this is an error or a warning, a description of the error and the name of the SwiftLint rule that caused the warning or error.&lt;/p&gt;
&lt;p&gt;You could now go through and fix each of these but there are 2 solutions to make things easier:
Try entering this in the Terminal window: &lt;code&gt;swiftlint autocorrect&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Correcting Swift files in current working directory
Correcting &#39;ViewController.swift&#39; (1/2)
Correcting &#39;AppDelegate.swift&#39; (2/2)
ViewController.swift:22:1 Corrected Vertical Whitespace
ViewController.swift:23 Corrected Trailing Newline
AppDelegate.swift:15:1 Corrected Vertical Whitespace
AppDelegate.swift:43:1 Corrected Vertical Whitespace
AppDelegate.swift:43 Corrected Trailing Newline
Done correcting 2 files!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now if you run &lt;code&gt;swiftlint&lt;/code&gt; again you will only get &lt;code&gt;Done linting! Found 8 violations, 2 serious in 2 files.&lt;/code&gt; and all the remaining issues are for &lt;code&gt;Line Length Violation&lt;/code&gt; This shows that autocorrect cannot fix everything and while it is good on spacing, it has trouble with more complex issues. But it is still worth doing as it can do a lot of the tedious fixes that creep into Xcode projects.&lt;/p&gt;
&lt;h3&gt;Incorporating SwiftLint into Xcode&lt;/h3&gt;
&lt;p&gt;The next thing to do to make your life easier is to add SwiftLint to your Xcode project so that it can run automatically whenever you do a build.&lt;/p&gt;
&lt;p&gt;Open your project in Xcode and select the project itself at the top of the Navigator on the left. Select the target for the main app and click the Build Phases tab along the top. Click the + button at the top left of the existing build phases and choose &#39;New Run Script Phase&#39;. Expand the newly added phase and paste in the following script:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;which&lt;/span&gt; swiftlint &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  swiftlint autocorrect
  swiftlint
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your Xcode window should look something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/images/2018/SwiftLineRunScript.png&quot;&gt;&lt;img src=&quot;https://troz.net/images/2018/SwiftLineRunScript.png&quot; alt=&quot;SwiftLint Run Script phase&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you think this looks like it is running SwiftLint twice, you are correct. The first time through it corrects what it can and the second pass reports all remaining issues. This is still faster than manually correcting the easy stuff. So now you will see any violations in the Xcode issue navigator when you do a build and you can click the entry to go directly to the code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/IssuesNavigator.png&quot; alt=&quot;SwiftLint errors &amp;amp; warnings in Xcode issues navigator&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Configuring SwiftLint&lt;/h3&gt;
&lt;p&gt;Now you are all set to SwiftLint all your code, but how do you decide what styles SwiftLint should enforce? Go back to Terminal, make the Terminal window as wide as possible and run &lt;code&gt;swiftlint rules&lt;/code&gt; which will return a table like this (click to see full-sized image):&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/images/2018/swiftlint_rules.png&quot;&gt;&lt;img src=&quot;https://troz.net/images/2018/swiftlint_rules.png&quot; alt=&quot;SwiftLint Rules&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For details on what each rule does, check out the entries in &lt;a href=&quot;https://github.com/realm/SwiftLint/blob/master/Rules.md&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Rules.md&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As an example, look at the rules for line_length:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;+------------------------------------------+--------+-------------+------------------------+-------------+---------------------+
| identifier                               | opt-in | correctable | enabled in your config | kind        | configuration       |
+------------------------------------------+--------+-------------+------------------------+-------------+---------------------+
| line_length                              | no     | no          | yes                    | metrics     | warning: 120, er... |
+------------------------------------------+--------+-------------+------------------------+-------------+---------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And by making my Terminal window full width, I can see the configuration column contains &lt;code&gt;warning: 120, error: 200, ignores urls: false, ignores function declarations: false, ignores comments: false&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The rule name (identifier) is line_length - this is what appears in the issue navigator or Terminal.&lt;/li&gt;
&lt;li&gt;It is not an opt-in rule, meaning that it is enabled by default.&lt;/li&gt;
&lt;li&gt;It is not correctable by autocorrect.&lt;/li&gt;
&lt;li&gt;It is enabled in this configuration.&lt;/li&gt;
&lt;li&gt;And then comes the configuration:
&lt;ul&gt;
&lt;li&gt;a line of more than 120 characters will generate a warning&lt;/li&gt;
&lt;li&gt;a line with more than 200 characters will generate an error and stop the build&lt;/li&gt;
&lt;li&gt;this rule will not ignore URLs, function declarations or comments&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To set your own configurations, you need to create a &lt;code&gt;.swiftlint.yml&lt;/code&gt; file in the same folder as your Xcode project. The leading period makes the file invisible, so you will need to create and edit it using Terminal.&lt;/p&gt;
&lt;p&gt;In the Terminal, &lt;code&gt;cd&lt;/code&gt; to the folder containing your Xcode project file and run &lt;code&gt;touch .swiftlint.yml&lt;/code&gt; to create the invisible file. You can edit the file in Terminal using &lt;code&gt;nano&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt; or whatever you prefer, but you can also open it in any text editor. I prefer to use Atom so I open the file by running this command: &lt;code&gt;atom .swiftlint.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The SwiftLint ReadMe has a section about &lt;a href=&quot;https://github.com/realm/SwiftLint#configuration&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;configuration&lt;/a&gt; which shows the format and the various general headings you can use, as well as how to configure an individual rule.&lt;/p&gt;
&lt;p&gt;Here is my .swiftlint.yml file:&lt;/p&gt;
&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;disabled_rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# rule identifiers to exclude from running&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;included&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;excluded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# paths to ignore during linting. overridden by `included`.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; Carthage
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; Pods
&lt;span class=&quot;token key atrule&quot;&gt;opt_in_rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; closure_end_indentation
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; closure_spacing
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; contains_over_first_not_nil
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; empty_count
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; explicit_init
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; fatal_error_message
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; force_unwrapping
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; literal_expression_end_indentation
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; multiline_arguments
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; multiline_parameters
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; operator_usage_whitespace
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; overridden_super_call
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; private_outlet
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; prohibited_super_call
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; trailing_closure
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; unneeded_parentheses_in_closure_argument
&lt;span class=&quot;token key atrule&quot;&gt;function_body_length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# warning&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# error&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;line_length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# warning&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# error&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;cyclomatic_complexity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ignores_case_statements&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;No rules are disabled by default.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;included&lt;/code&gt; is blank which means it checks every folder...&lt;/li&gt;
&lt;li&gt;... except for the ones listed in the &lt;code&gt;excluded&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opt-in-rules&lt;/code&gt;: I went through the list of rules with &lt;code&gt;opt-in&lt;/code&gt; equal to &lt;code&gt;yes&lt;/code&gt; and enabled all I thought useful.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;function_body_length&lt;/code&gt; - the default warning length is 40 but I allow myself 60 as that fits on my screen so I can always see the entire function without scrolling.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;line_length&lt;/code&gt; - with a bigger screen, I lengthen both the warning and error lengths.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cyclomatic_complexity&lt;/code&gt; checks for functions that are getting so complex that the compiler might run into trouble. I have this on with the default warning &amp;amp; error limits, but I set it to ignore &lt;code&gt;case&lt;/code&gt; statements as they can often trigger this without really being too complex.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that my .swiftlint.yml file is edited, doing a build uses those settings and for a new iOS project, I am now down to 2 errors and 3 warnings, all due to long lines in AppDelegate.swift.&lt;/p&gt;
&lt;p&gt;The other main way to configure SwiftLint is by disabling specific rules in my code.&lt;/p&gt;
&lt;p&gt;As an example, I have an &lt;code&gt;enum&lt;/code&gt; for direction:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Direction&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; up&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; down&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;right&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I build this, SwiftLint gives a warning:
&lt;code&gt;Identifier Name Violation: Enum element name should be between 3 and 40 characters long: &#39;up&#39; (identifier_name)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This tells me that &lt;code&gt;up&lt;/code&gt; is too short a word to use as an identifier according to the &lt;code&gt;identifier_name&lt;/code&gt; rule. In this case, &lt;code&gt;up&lt;/code&gt; is the obvious identifier to use, so I want SwiftLint to let it pass. But I don&#39;t want to check my configuration file, because I want it to catch other short identifier names.&lt;/p&gt;
&lt;p&gt;The solution is to add this comment to my code, before the &lt;code&gt;case&lt;/code&gt; line:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;// swiftlint:disable next identifier_name&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This tells SwiftLint to ignore the next &lt;code&gt;identifier_name&lt;/code&gt; warning or error it sees and now my code builds without error.&lt;/p&gt;
&lt;p&gt;Rules can also be disabled or enabled by file. Check out the &lt;a href=&quot;https://github.com/realm/SwiftLint#disable-rules-in-code&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Disable rules in code&lt;/a&gt; section of the SwiftLint ReadMe for more details.&lt;/p&gt;
&lt;h2&gt;What are the benefits?&lt;/h2&gt;
&lt;p&gt;For me, SwiftLint enforces a number of good habits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Files are kept relatively short.&lt;/li&gt;
&lt;li&gt;Functions are always small enough to fit on a single screen.&lt;/li&gt;
&lt;li&gt;Variable names &amp;amp; function names cannot be too short and non-descriptive.&lt;/li&gt;
&lt;li&gt;Braces, indentation and white-space are always consistent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And sometimes the warnings make me think of a better way to do things.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Icon Builder 5</title>
    <link href="https://troz.net/post/2018/icon-builder-5/" />
    <updated>2018-02-18T00:19:18Z</updated>
    <id>https://troz.net/post/2018/icon-builder-5/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://troz.net/icon-builder/&quot;&gt;Icon Builder 5.0&lt;/a&gt; is now available from the &lt;a href=&quot;http://itunes.apple.com/app/icon-builder/id552293482&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;. This is a complete re-write for better compatibility with Apple&#39;s latest icon requirements. Read on to see what I have fixed and how...&lt;/p&gt;

&lt;hr&gt;
&lt;h2&gt;Problems&lt;/h2&gt;
&lt;p&gt;When I came to create a new iOS app recently, I found out that Icon Builder had fallen behind Apple&#39;s requirements in three ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The 1024 x 1024 marketing icon is now supposed to be inside the app&#39;s icon set.&lt;/li&gt;
&lt;li&gt;Icon files must have their color profile set to sRGB (P3 is also valid for iOS apps).&lt;/li&gt;
&lt;li&gt;iOS icon files must have no transparent pixels and the alpha channel must be removed from the files.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When I set to work fixing these problems I soon ran into issues with the existing version of Icon Builder which was created 6 years ago.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It was written in Objective-C which I am increasingly finding difficult and un-safe to write.&lt;/li&gt;
&lt;li&gt;The app was written when I was very much a beginner in Mac apps and this is obvious from the code...&lt;/li&gt;
&lt;li&gt;There was a lot of legacy code left over from previous changes and extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I decided that the most interesting thing to do would be to start almost from scratch and re-write the app in Swift using better techniques.&lt;/p&gt;
&lt;h2&gt;The Re-write&lt;/h2&gt;
&lt;p&gt;Now instead of the Massive View Controller, I have a larger set of small files, each with their own responsibility. Enums and structs dictate the various requirements for the different devices or app types. Other structs deal with creating the images, the folder management and writing out the files. An NSImage extension handles the resizing and reformatting of the images. This is now an app that I would not be ashamed to show anyone, except perhaps for the need to add more unit tests.&lt;/p&gt;
&lt;h2&gt;Adding a color profile&lt;/h2&gt;
&lt;p&gt;But then we get to the new features needed. Adding the 1024x1024 icon to the app icon set was easy, especially after the re-factoring. But what about the color profile?&lt;/p&gt;
&lt;p&gt;This was not as easy as I expected - there is no built in command to apply a profile but here is the solution that I finally found:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;convertImageTo_sRGB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; colorSpace &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGColorSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGColorSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sRGB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cgi &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cgImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forProposedRect&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                 context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                 hints&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cantMakeCgImage
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ci &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CIImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cgImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cgi&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pngData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CIContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pngRepresentation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ci&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                        format&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kCIFormatRGBA8&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                        colorSpace&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; colorSpace&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cantConvertToPng
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; pngData
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;This takes the NSImage and converts it to a CGImage, first checking that the appropriate color space exists.&lt;/li&gt;
&lt;li&gt;Then it uses the Core Graphics CGImage to create a Core Image CIImage.&lt;/li&gt;
&lt;li&gt;There is a new API in macOS 10.13 to extract the png data from a CIImage while assigning a color profile.&lt;/li&gt;
&lt;li&gt;This Data can then be written directly to a file and there you have a PNG with an attached color profile.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Transparency&lt;/h2&gt;
&lt;p&gt;Now problems 1 &amp;amp; 2 have been solved. Problem 3 was the most difficult. It turned out to be a two-part problem because an image file can have no transparent pixels but still have an alpha channel in the file data.&lt;/p&gt;
&lt;p&gt;At first, I thought maybe I could just circumvent the whole problem by converting the images to JPEGs which have no transparency or alpha channel. Using the code above, I just changed it to getting the &lt;code&gt;jpegRepresentation&lt;/code&gt; instead and saving with a .jpg file extension.&lt;/p&gt;
&lt;p&gt;While this solved the alpha channel problem, the transparent parts of the icon just went black which was a not a good look.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/Transparent-Jpeg.png&quot; alt=&quot;Transparent image converted to JPEG&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For anyone horrified at my use of force-unwrapping, I never do this in a production app but in a playground, it makes the code shorter and it doesn&#39;t really matter if I get a crash there.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Converting transparent pixels to white&lt;/h3&gt;
&lt;p&gt;So the first step must be to set the transparent parts of the image to another color. Searching for solutions online, most of the ones I came up with were very slow (processing each pixel) or so complicated that I didn&#39;t understand them, and I hate just copy-pasting code that I don&#39;t understand at all.&lt;/p&gt;
&lt;p&gt;But eventually I found something that I morphed into this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;makeAlphaWhite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tiffRepresentation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageRep &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSBitmapImageRep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jpegData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageRep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;representation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;jpeg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; properties&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;NSBitmapImageRep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PropertyKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compressionFactor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jpegImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jpegData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; image
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; jpegImage
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It used basically the same trick of converting the image into a JPEG but doing it this way via &lt;code&gt;NSBitmapImageRep&lt;/code&gt; turned the transparent pixels white instead of black. And as you can see, this gave a much better looking image:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/MakeAlphaWhite.png&quot; alt=&quot;Transparent portions converted to white&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now I was able to continue with my plans to have JPEGs rule the world! This worked really well in my early tests but then I came to try a Stickers app and the icons didn’t work. I couldn&#39;t even drag them in manually! Back to the Apple docs and I see that icons must be PNGs.&lt;/p&gt;
&lt;p&gt;When I changed the transparent pixels to white, added the color space and then saved the PNG data, I got an image that looked correct but the file still contained an alpha channel. So I had to come up with a method that re-wrote the PNG data in such a way that it never contained any alpha data at all.&lt;/p&gt;
&lt;h3&gt;Removing the alpha channel&lt;/h3&gt;
&lt;p&gt;Graphics experts are probably groaning aloud by now, but I did eventually arrive at a solution, however hacky:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;convertImageTo_sRGB_noAlpha&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; colorSpace &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGColorSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGColorSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sRGB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cgi &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cgImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forProposedRect&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   context&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   hints&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                                    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cantMakeCgImage
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ci &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CIImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cgImage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cgi&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jpgData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CIContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;jpegRepresentation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ci&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                                           colorSpace&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; colorSpace&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cantConvertToJpg
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jpegImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jpgData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cantConvertToJpgImage
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pngData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; jpegImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;convertImageTo_sRGB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; pngData
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Take the image &lt;strong&gt;after&lt;/strong&gt; changing the transparent pixels to white.&lt;/li&gt;
&lt;li&gt;Convert it to JPEG data with the required color space.&lt;/li&gt;
&lt;li&gt;Convert the JPEG data back to an image - this will contain &lt;strong&gt;NO&lt;/strong&gt; alpha data.&lt;/li&gt;
&lt;li&gt;Use the original routine to convert this JPEG into PNG data with the correct color space.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Running this in the playground looks like this:
&lt;img src=&quot;https://troz.net/images/2018/TransparentPng.png&quot; alt=&quot;Creating non-transparent PNG in playground&quot;&gt;&lt;/p&gt;
&lt;p&gt;And as you can see from the file info, it results in a file with the correctly assigned color profile and no alpha channel:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2018/FileInfo.png&quot; alt=&quot;Final result file info&quot;&gt;&lt;/p&gt;
&lt;p&gt;The double shuffle sounds time-consuming and in-efficient but it really doesn&#39;t take long. In my tests, by far the longest part of creating an icon set is opening the file dialog.&lt;/p&gt;
&lt;h2&gt;Future plans&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Add more unit tests.&lt;/li&gt;
&lt;li&gt;Work out how to replace the transparent pixels with a selected color.&lt;/li&gt;
&lt;li&gt;Offer better cropping and image positioning options.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;p&gt;For resizing and cropping images, I use &lt;a href=&quot;https://mattgemmell.com/imagecrop-source-code/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Matt Gemmell&#39;s NSImage+MGCropExtensions&lt;/a&gt; and for further reading, I recommend Apple&#39;s Human Interface Guidelines concerning app icons for &lt;a href=&quot;https://developer.apple.com/ios/human-interface-guidelines/icons-and-images/app-icon/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;iOS&lt;/a&gt; and &lt;a href=&quot;https://developer.apple.com/macos/human-interface-guidelines/icons-and-images/app-icon/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;macOS&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: here is Australia we use the spelling &lt;strong&gt;colour&lt;/strong&gt; but for consistency with the code samples, I have used &lt;strong&gt;color&lt;/strong&gt; throughout the text.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  <entry>
    <title>NCSS 2018</title>
    <link href="https://troz.net/post/2018/ncss-2018/" />
    <updated>2018-01-05T01:33:35Z</updated>
    <id>https://troz.net/post/2018/ncss-2018/</id>
    <content type="html">&lt;p&gt;I gave a Masterclass on Swift at the NCSS Summer School 2018 in the University of Sydney on 9th January 2018. This post contains useful links for use in relation to the material covered during my class.&lt;/p&gt;

&lt;hr&gt;
&lt;h3&gt;Online Sandboxes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://eval.weheartswift.com/eval/playground/swift-sandbox&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;We ❤️ Swift Sandbox&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://swiftlang.ng.bluemix.net/#/repl&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;IBM Swift Sandbox&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/trozware/bc582b9a49feb7bbd3adce8ea049c9d3&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Gist of Code Samples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trozware/ncss2018/blob/master/Presentation.pdf&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Slides from Presentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;Contact Details&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Email: &lt;a href=&quot;mailto:sarah@troz.net?subject=NCSS%202018&quot;&gt;sarah@troz.net&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Twitter: &lt;a href=&quot;https://twitter.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@trozware&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;Useful Links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://itunes.apple.com/au/book/swift-programming-language/id881256329?mt=11&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;The Swift Programming Language&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://swift.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift.org&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.raywenderlich.com/132258/top-5-ios-swift-newsletters&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Newsletters&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.raywenderlich.com/category/swift&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Tutorials&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Mark Time</title>
    <link href="https://troz.net/post/2017/marktime/" />
    <updated>2017-12-11T23:14:07Z</updated>
    <id>https://troz.net/post/2017/marktime/</id>
    <content type="html">&lt;style&gt;img {width: 300px; } &lt;/style&gt;
&lt;p&gt;&lt;a href=&quot;https://itunes.apple.com/us/app/mark-time/id1305580742?mt=8&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mark Time&lt;/a&gt; is an application for Celestial Navigation that solves the
problem of how to take an accurate time reading to go with each sextant
sighting. Here is how it was developed...&lt;/p&gt;

&lt;hr&gt;
&lt;p&gt;A day in the life of a software developer - &amp;quot;it would be great if there was an
app that did...&amp;quot;&lt;/p&gt;
&lt;p&gt;One of the family is learning celestial navigation and was struggling with the
problem that you either need a partner or more than 2 hands. The key factor is
that you need to record the exact time at the moment when you take your
sighting. And this time has to be in UTC to make your subsequent calculations
work.&lt;/p&gt;
&lt;p&gt;Since this is a very simple app, I decided it would be fun to describe the
development process from first specifications to release in the App Store.&lt;/p&gt;
&lt;h3&gt;The Basic Specs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;tap anywhere on the screen to record the time&lt;/li&gt;
&lt;li&gt;convert the time to Universal Time (UT)&lt;/li&gt;
&lt;li&gt;display that time in a specific format: DD:HH:MM:SS&lt;/li&gt;
&lt;li&gt;store the last 5 tap times&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;code&gt;UITapGestureRecognizer&lt;/code&gt; over the entire view solved the first requirement.
The native &lt;code&gt;Date&lt;/code&gt; object handles time zone conversions perfectly, and then it
was a matter of configuring a &lt;code&gt;DateFormatter&lt;/code&gt; with a custom format string to
provide the required date display.&lt;/p&gt;
&lt;p&gt;The last 5 times are stored in &lt;code&gt;UserDefaults&lt;/code&gt; and displayed in reverse order so
the most recent is always at the top.&lt;/p&gt;
&lt;h3&gt;UI considerations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;colour scheme must work in bright sunlight&lt;/li&gt;
&lt;li&gt;text should use dynamic sizing so it works if people adjust the text size on
their iPhones&lt;/li&gt;
&lt;li&gt;the UI elements must be out of the way of the new iPhone X system gestures.&lt;/li&gt;
&lt;li&gt;while it will most likely be used on iPhones, allow for iPads as well&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had done extensive tests of colour schemes when developing my golf scoring app,
so I already had the answer to the colours question: dark grey
background with white text.&lt;/p&gt;
&lt;p&gt;Allowing for dynamic type should have been easy - choose a font style e.g. body,
headline, title 1 and set it to adjust automatically. But with a display that is
mainly numeric, the display looked terrible because the numbers in the default
San Francisco are non-proportional. So I switched to Helvetica Neue and watched
for dynamic font size changes manually.&lt;/p&gt;
&lt;p&gt;The iPhone X should be fine since I used the new safe area layout guides.&lt;/p&gt;
&lt;p&gt;For iPad, I didn&#39;t make a lot of UI changes since I don&#39;t expect it to be used
much, but the display is bigger and probably easier to read.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2017/MarkTime1.png&quot; alt=&quot;Mark Time&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Refinements&lt;/h3&gt;
&lt;p&gt;As always, once the first few iterations have been through the testing process,
there were a few things that needed changing and then feature creep set in...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;change the date format to be more readable&lt;/li&gt;
&lt;li&gt;change the basic tap gesture so that it triggers on tap down, not tap up&lt;/li&gt;
&lt;li&gt;add a button to clear all the stored data&lt;/li&gt;
&lt;li&gt;display the current time in UTC and in the same format&lt;/li&gt;
&lt;li&gt;cheat mode - record latitude and longitude for each time so the calculations
can be checked&lt;/li&gt;
&lt;li&gt;change the format for showing the latitude and longitude&lt;/li&gt;
&lt;li&gt;sound &amp;amp; haptics to provide feedback&lt;/li&gt;
&lt;li&gt;settings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The date format was DD:HH:MM:SS so for 12th December at 1:01:46 pm, this would
show 12:13:01:46 which we decided was a bit confusing. One tended to assume the
first section was actually the hours. Under the new scheme, that same date
displays as 12d 13h 01m 46.654s. The extra precision on the seconds was to show
that the time really was changing if you tapped multiple times quickly.&lt;/p&gt;
&lt;p&gt;To make the time record immediately, I removed the &lt;code&gt;UITapGestureRecognizer&lt;/code&gt; and
placed &lt;code&gt;UIButtons&lt;/code&gt; covering all the relevant active areas. They respond on Touch
Down for a much faster reaction.&lt;/p&gt;
&lt;p&gt;Usually, I make a separate View Controller for preferences. This time, because
the app needed access to the standard Settings app for Location services, I
decided to add the app&#39;s preferences to the app&#39;s page in the Settings app. This
worked really well, and made the app itself smaller and simpler.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2017/MarkTime3.png&quot; alt=&quot;Mark Time&quot;&gt;&lt;/p&gt;
&lt;p&gt;In cheat mode, the location is recorded at every tap and then the info button
beside each time shows the details. This needed to handle the permissions
required for accessing the user&#39;s location. The latitude and longitude are shown
in decimal format and at first, I also showed them in DMS (degrees, minutes,
seconds) format. I found out that for navigation calculations, they prefer to
see degrees and decimal minutes, no seconds. And rather oddly, this is displayed
something like this: 153° 22&#39;.84 E&lt;/p&gt;
&lt;p&gt;Sounds and haptics were added, configurable through Settings. The sound was a
nice loud double-beep designed to be clearly audible to tell you when you had
recorded a time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2017/MarkTime2.png&quot; alt=&quot;Mark Time&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Release&lt;/h3&gt;
&lt;p&gt;After numerous rounds of TestFlight, I was ready to release. The app was
submitted to iTunes Connect and ready for review on Dec 9, 2017 at 7:36 PM. It
moved into review on Dec 11, 2017 at 8:07 AM and was ready for sale on Dec 11,
2017 at 10:19 AM. So less than 39 hours from start to finish of the review
process for a new app - things have certainly changed!&lt;/p&gt;
&lt;p&gt;The app is free - you can get it from the &lt;a href=&quot;https://itunes.apple.com/us/app/mark-time/id1305580742?mt=8&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;iTunes App Store&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Moving to Hugo</title>
    <link href="https://troz.net/post/2017/moving-to-hugo/" />
    <updated>2017-11-29T07:42:57Z</updated>
    <id>https://troz.net/post/2017/moving-to-hugo/</id>
    <content type="html">&lt;p&gt;Yesterday, I came to update my blog: I had a new post in draft form and I wanted
to update the Swift code to version 4. However installing High Sierra had
removed &lt;a href=&quot;https://jekyllrb.com&quot; title=&quot;Jekyll&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jekyll&lt;/a&gt; which is the site generator that I had been using. I
re-installed Jekyll but found that it had been updated from version 2 to version
3.&lt;/p&gt;

&lt;hr&gt;
&lt;h2&gt;Jekyll Problems&lt;/h2&gt;
&lt;p&gt;Then my problems started: missing dependencies due to the basic Jekyll install
no longer including certain features, and then syntax errors due to changes in
the way things are done, particularly to do with pagination.&lt;/p&gt;
&lt;p&gt;I tried to fix this myself, then thought that maybe the theme I was using had
already solved these issues so I tried re-installing that. It wanted to add
about 25 more gems and then failed to install - sigh - spend some time rolling
that back...&lt;/p&gt;
&lt;p&gt;At this point I decided that if Jekyll was going to cause me a lot of trouble,
it might be worth looking at an alternative.&lt;/p&gt;
&lt;p&gt;My main criteria were the ability to import my Jekyll site and the ability to
host on &lt;a href=&quot;https://pages.github.com/&quot; title=&quot;GitHub Pages&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub Pages&lt;/a&gt;. After checking out the options, I decided to have a
look at &lt;a href=&quot;https://gohugo.io&quot; title=&quot;Hugo&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Moving to Hugo&lt;/h2&gt;
&lt;p&gt;One of the most lauded features of Hugo is speed. I had found with Jekyll that
when I saved an edited page, it took about 5 seconds before the edits could be
reloaded into the browser. Hugo is supposedly fast and does auto reloads.&lt;/p&gt;
&lt;p&gt;Following the &lt;a href=&quot;https://gohugo.io/getting-started/quick-start/&quot; title=&quot;Hugo Quick Start&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Quick Start&lt;/a&gt; guide, I installed Hugo. Importing my Jekyll site
worked easily and so fast I wasn&#39;t sure anything had happened, transferring my
posts to &lt;code&gt;contents/posts&lt;/code&gt; and my other files (status pages, images etc.) to
&lt;code&gt;static&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then the main exercise appeared to be choosing a theme which would provide not
only the visuals but the capabilities of the site.&lt;/p&gt;
&lt;p&gt;So I drew up a list of demands for any theme I chose:&lt;/p&gt;
&lt;h3&gt;Must Have Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Responsive&lt;/li&gt;
&lt;li&gt;Blog style:
&lt;ul&gt;
&lt;li&gt;front page with recent posts&lt;/li&gt;
&lt;li&gt;tags&lt;/li&gt;
&lt;li&gt;archive page&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Written using Markdown&lt;/li&gt;
&lt;li&gt;Static pages for apps listing, about, contact etc.&lt;/li&gt;
&lt;li&gt;Syntax highlighting&lt;/li&gt;
&lt;li&gt;Social links&lt;/li&gt;
&lt;li&gt;Google Analytics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Would Be Nice Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Disqus commenting&lt;/li&gt;
&lt;li&gt;Twitter cards&lt;/li&gt;
&lt;li&gt;JSON Feed&lt;/li&gt;
&lt;li&gt;Searching within the site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Hugo themes page https://themes.gohugo.io has themes listed by capabilities,
but no way that I could see to combine a selection of capabilities.&lt;/p&gt;
&lt;p&gt;Some of the features like Google Analytics or Disqus comments, I could probably
add myself using templates from theme that did include them, if the template I
chose did not.&lt;/p&gt;
&lt;p&gt;Conveniently, nearly all these seem to come with an exampleSite folder that
contains the configuration info.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Configuring a Theme&lt;/h2&gt;
&lt;p&gt;After testing out several alternatives, I decided to go with &lt;a href=&quot;https://github.com/olOwOlo/hugo-theme-even&quot; title=&quot;Even&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Even&lt;/a&gt; - at
least to start with. I installed the theme and copied the contents of the
example &lt;code&gt;config.toml&lt;/code&gt; to my own &lt;code&gt;config.toml&lt;/code&gt; file and started configuring.&lt;/p&gt;
&lt;p&gt;I wasn&#39;t happy with some of the fonts, but soon worked out how to change the CSS
in the theme and then re-build it to apply the changes. And I changed the theme
colour from the default red to &amp;quot;Cobalt Blue&amp;quot;.&lt;/p&gt;
&lt;p&gt;Most of the other changes were done using the &lt;code&gt;config.toml&lt;/code&gt; file. I was able to
set up menus, configure the social media links I wanted to show, set date
formats, set the number of items to show per page and so on.&lt;/p&gt;
&lt;p&gt;I had a couple of more static pages that I wanted to show, so I added their
Markdown files to the content folder.&lt;/p&gt;
&lt;p&gt;One nice feature which I had missed in Jekyll (or at least the way I had it set
up) was the ability to separate blog posts from more static web pages. Each of
my apps has an information/support page that does not accept comments and
doesn&#39;t need to be listed in the Archives. By moving these Markdown files from
&lt;code&gt;contents/posts&lt;/code&gt; to &lt;code&gt;contents/pages&lt;/code&gt;, I was able to achieve this.&lt;/p&gt;
&lt;p&gt;Adding Google Analytics and Disqus commenting was as easy as entering my IDs in
the relevant places in the &lt;code&gt;config.toml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;I had to do some editing of my imported posts from Jekyll - mainly getting rid
of references to &lt;code&gt;https://troz.net&lt;/code&gt; so that images and internal links would work.
And sometimes I just needed to re-save a file to make Hugo re-evaluate it (there
is probably a command to force a complete re-build if I looked for it.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Working out the Structure&lt;/h2&gt;
&lt;p&gt;One thing that I was puzzled by was what templates the various pages are
generated from.&lt;/p&gt;
&lt;p&gt;The main page is easy enough as the theme folder has a layouts folder with an
&lt;code&gt;index.html&lt;/code&gt; file containing the templating. When a post is displayed, that
looks like it comes from &lt;code&gt;layouts/posts/single.html&lt;/code&gt; while
&lt;code&gt;layouts/posts/summary.html&lt;/code&gt; contains the templating for each entry in the index
page.&lt;/p&gt;
&lt;p&gt;But clicking on the Archives link in the me takes me to https://troz.net/post/
and there is no &lt;code&gt;post.html&lt;/code&gt; file in layouts. There is a &lt;code&gt;post&lt;/code&gt; folder, but it
contains the &lt;code&gt;single.html&lt;/code&gt; &amp;amp; &lt;code&gt;summary.html&lt;/code&gt; files described already.&lt;/p&gt;
&lt;p&gt;Delving ever deeper, &lt;code&gt;layouts/_default/section.html&lt;/code&gt; appears to contains the
Archives page template. But how does that relate to the https://troz.net/post/
link? And the Tags template is in &lt;code&gt;layouts/_default/terms.html&lt;/code&gt; but appears in
https://troz.net/tags/. Checking the Hugo documentation for creating a theme, I
find the information I need. Hugo has a selection of file paths that it checks
for such templates, and these file paths match some of those. So now I know
where to go to customise further.&lt;/p&gt;
&lt;p&gt;One feature that I really like about the Even theme is the ability to have a
table of contents displayed beside each blog post if your page is wide enough.
It takes the headers in the post and uses them to make the table. I may need to
add or edit the headers in some posts, but I really like this feature.&lt;/p&gt;
&lt;p&gt;And if you want a laugh, test out the 404 page - here is a &lt;a href=&quot;https://troz.net/bad-link/&quot; title=&quot;404&quot;&gt;bad link&lt;/a&gt; - which
generates a random text emoji each time!&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Publishing&lt;/h2&gt;
&lt;p&gt;I wanted to use my existing GitHub Pages setup, so I replaced the site files in
my local repository with the new Hugo site files. I generated the static site
files using &lt;code&gt;hugo&lt;/code&gt; in Terminal, then pushed the pages to GitHub. All I had to do
then was change the settings so that GitHub Pages knew my site was coming from
the &lt;code&gt;docs&lt;/code&gt; folder and I was live with the new site.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;So how am I going with my check list?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Responsive - ALMOST, not Hugo&#39;s fault&lt;/li&gt;
&lt;li&gt;Blog style - YES&lt;/li&gt;
&lt;li&gt;Written using Markdown - YES&lt;/li&gt;
&lt;li&gt;Static pages for apps listing, about, contact etc. - YES&lt;/li&gt;
&lt;li&gt;Syntax highlighting - YES&lt;/li&gt;
&lt;li&gt;Social links - YES&lt;/li&gt;
&lt;li&gt;Google Analytics - YES&lt;/li&gt;
&lt;li&gt;Disqus commenting - YES&lt;/li&gt;
&lt;li&gt;Twitter cards - NO&lt;/li&gt;
&lt;li&gt;JSON Feed - NO&lt;/li&gt;
&lt;li&gt;Searching within the site - NO&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Hugo generated pages are all responsive and look great on my iPhone, but my
old static page for listing my apps does not look good, so I need to re-design
that.&lt;/p&gt;
&lt;p&gt;Twitter cards - I have found some articles on how to set that up, so it is
possible, but I haven&#39;t done it yet.&lt;/p&gt;
&lt;p&gt;JSON Feed is not working. There are themes that apparently have this, but I
haven&#39;t yet worked out how to implement it - hopefully later.&lt;/p&gt;
&lt;p&gt;In-site search - this was something I was able to get in my old WordPress site,
but not in Jekyll. There are Hugo themes listed that support search, but they
seem to just give a link to Google or DuckDuckGo. Maybe this can be edited to
restrict it to the site, but I dislike using such searches, so I will not add a
search unless it works internally. One theme I found uses &lt;a href=&quot;https://www.algolia.com&quot; title=&quot;Algolia&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Algolia&lt;/a&gt; to search
the site, so I will investigate that.&lt;/p&gt;
&lt;p&gt;Overall, I am impressed. The transition was relatively easy, especially
considering that I didn&#39;t take the time to learn anything about Hugo first, but
just blundered in and tried to work it out on the fly.&lt;/p&gt;
&lt;p&gt;The speed of generating pages and the live reload while developing make Hugo a
pleasure to work with. Now all I have to do is write some interesting posts...&lt;/p&gt;
&lt;p&gt;And I want to tidy up the URLs, headers and tags for the older pages as well as
investigating Twitter cards, search &amp;amp; JSON Feed.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>JSON Parsing in Swift 4</title>
    <link href="https://troz.net/post/2017/json-parsing-in-swift-4/" />
    <updated>2017-06-18T00:00:00Z</updated>
    <id>https://troz.net/post/2017/json-parsing-in-swift-4/</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; 3rd September 2017.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Checked syntax using Xcode 9.0 beta 6.&lt;/li&gt;
&lt;li&gt;Added section on &lt;a href=&quot;https://troz.net/post/2017/json-parsing-in-swift-4/#nulls&quot;&gt;allowing for nulls&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Since &lt;strong&gt;JSON&lt;/strong&gt; has become the de facto standard for data transfers around the
internet, there has always been a lot of interest in Swift libraries to parse
JSON into Swift classes or structs. Searching for &amp;quot;swift json library&amp;quot; on
&lt;a href=&quot;https://github.com/search?q=swift+json+library&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; discovers &lt;s&gt;77&lt;/s&gt; 86 repositories. So why are there so many? And
what has Swift 4 done to &lt;em&gt;sherlock&lt;/em&gt; them all?&lt;/p&gt;

&lt;p&gt;The problem has always been converting loosely typed JSON to strictly typed
Swift which involves a lot of type casting, checking, optionals etc. Swift has
always provided access to the Objective-C methods for converting JSON to and
from NSData, NSDictionary and NSArray. (These are now called Data, Dictionary
and Array, but those labels are so universal, that I sometimes feel a more
specific nomenclature would be useful. Have you tried doing a search for
&#39;Data&#39;?)&lt;/p&gt;
&lt;p&gt;For sample data, I am using https://jsonplaceholder.typicode.com/users which
returns 10 user objects in this format:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Leanne Graham&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Bret&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sincere@april.biz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;address&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Kulas Light&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;suite&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Apt. 556&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Gwenborough&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;zipcode&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;92998-3874&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;geo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;lat&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-37.3159&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;lng&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;81.1496&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;phone&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1-770-736-8031 x56442&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;website&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hildegard.org&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;company&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Romaguera-Crona&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;catchPhrase&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Multi-layered client-server neural-net&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;bs&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;harness real-time e-markets&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The goal will be to convert this to an instance of this matching Swift struct:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; phone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; website&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; address&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; company&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; street&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; suite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; city&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; zipcode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; geo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lat&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lng&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; catchPhrase&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing to note is that the Swift struct (and its embedded structs) use
multiple different types: Int, String, Double, Address, Coordinates, Company.
The JSON data only has strings and numbers and even then, some of the numbers
are really strings - look at the lat &amp;amp; lng entries. So converting from JSON to a
struct and back again has always been problematic, but let&#39;s give it a go using
built-in Swift 3 processing with no external libraries.&lt;/p&gt;
&lt;h2&gt;Decoding in Swift 3&lt;/h2&gt;
&lt;p&gt;You can insert this into a playground or &lt;a href=&quot;https://github.com/trozware/json&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;download my playground&lt;/a&gt; and check
out the Swift 3 JSON page:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sampleDataAddress &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://jsonplaceholder.typicode.com/users&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sampleDataAddress&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentsOf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; phone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; website&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; address&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; company&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; username &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; phone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;phone&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; website &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;website&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; addressDict &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;address&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; addressDict&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; companyDict &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;company&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; company &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; companyDict&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;username &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; username
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;phone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; phone
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;website &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; website
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; address
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;company &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; company
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; street&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; suite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; city&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; zipcode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; geo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; street &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; suite &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;suite&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; city &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; zipcode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;zipcode&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; geoDict &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;geo&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; geo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; geoDict&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;street &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; street
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;suite &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; suite
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;city &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; city
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zipcode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; zipcode
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; geo
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lat&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lng&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; latString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;latString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lngString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lng&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lng &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lngString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lat
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lng &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lng
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; catchPhrase&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; catchPhrase &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;catchPhrase&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dict&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bs&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;catchPhrase &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; catchPhrase
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bs
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONSerialization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;jsonObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jsonData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; json &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jsonArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flatMap &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token short-argument&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count
        &lt;span class=&quot;token function&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don&#39;t actually expect you to go through all this code in detail, but I
included it here to make it obvious how verbose this method is.&lt;/p&gt;
&lt;p&gt;The first 3 lines get the data, and I have force-unwrapped the URL and the Data
which I would not do in a production app but which is OK while testing in a
playground. Then there is a declaration of the Users struct with all its sub
structs. Each one has a failable init that tries to parse the JSON dictionary
and returns nil if the data doesn&#39;t match, by way of a lengthy set of guard
statements. The basic layout of each struct is the same but it is very verbose.
And as an extra step, the lat and lng properties need to be converted from
Strings to Doubles.&lt;/p&gt;
&lt;p&gt;I have to confess that it took quite come time to get this right ... lots of
guard statements to check that the data can be converted to the required types
which meant that any typos produced an empty array. The sub-structs have to be
extracted from the JSON as Dictionaries and then initialised by themselves.&lt;/p&gt;
&lt;h2&gt;Decoding in Swift 4&lt;/h2&gt;
&lt;p&gt;So this works, and I get an array of Users objects. But it isn&#39;t pretty and it
takes a lot of code to do the processing. So now I am going to move on to doing
this in Swift 4. I am using Xcode 9.0 beta 6 (9M214v) so if you have a later
version, you may need to adapt to any changes.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; phone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; website&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; address&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; company&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; street&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; suite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; city&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; zipcode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; geo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lat&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lng&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; catchPhrase&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonDecoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonDecoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                    from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jsonData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ignoring the complexities of converting latitude and longitude to Doubles, I get
vastly simpler code. I declare all the structs as conforming to the &lt;code&gt;Codable&lt;/code&gt;
protocol and then I can remove all the init methods and just let &lt;code&gt;JSONDecoder&lt;/code&gt;
do its magic. I just have to tell it what data type to expect - in this case an
Array of Users. I don&#39;t have to worry about the initial conversion of the JSON
data to a Dictionary or looping through the elements using flatMap.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In the playground, I used a do...catch structure to check the result of the
decode function, but I have used try? here to keep the code short.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Changing data types&lt;/h2&gt;
&lt;p&gt;The lat &amp;amp; lng coordinates are stored in the JSON as Strings, but need to be
converted to Doubles for the Swift struct.&lt;/p&gt;
&lt;p&gt;This requires a custom init method for the Coordinates struct to do the
conversion from String to Double.&lt;/p&gt;
&lt;p&gt;So here is the complete code for Swift 4 - again, if you don&#39;t want to create
your own playground, you can &lt;a href=&quot;https://github.com/trozware/json&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;use mine&lt;/a&gt;, this time looking at the Swift 4
JSON page:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sampleDataAddress &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://jsonplaceholder.typicode.com/users&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sampleDataAddress&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;contentsOf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; phone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; website&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; address&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; company&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; street&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; suite&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; city&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; zipcode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; geo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lat&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lng&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from decoder&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Decoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; values &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; decoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyedBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodingKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; latString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; forKey&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lngString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; forKey&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lng&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                lat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;latString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
                lng &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lngString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Company&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; catchPhrase&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; bs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonDecoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonDecoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jsonData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

users&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count
&lt;span class=&quot;token function&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For comparative purposes, I counted the lines of code in each, removing the
common boiler plate of the top of each and the two lines for displaying the
results in the playground. I also removed the blank lines which I always use a
lot in my code for readability but which are not relevant when comparing code
efficiency:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Lines&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Lines if lat &amp;amp; lng are Strings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Swift 3&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;95&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;93&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swift 4&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;35&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Encoding&lt;/h2&gt;
&lt;p&gt;Encoding back to JSON is very similar. In the Swift 3 version, I would have
written a method for each struct that created a Dictionary and then used
JSONSerialization to convert the Dictionary back to JSON. This is very verbose
and tedious to write so I am not going to bother to demonstrate it here.&lt;/p&gt;
&lt;p&gt;But in Swift 4, this is even easier than decoding. Add this code to the end of
your Swift 4 JSON playground or playground page:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; users &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; users &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonEncoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONEncoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    jsonEncoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;outputFormatting &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prettyPrinted

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; backToJson &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonEncoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; backToJson&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; encoding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jsonString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;JSONEncoder&lt;/code&gt; returns Data and I have converted that to a String for
checking. I set JSONEncoder&#39;s outputFormatting property to .prettyPrinted get a
more readable String.&lt;/p&gt;
&lt;p&gt;Notice how the encoded data is not quite the same as I received because the lat
&amp;amp; lng properties are now Doubles. In a production app, if I needed to be able to
convert back to JSON, I probably would have left them as Strings so as to enable
data transfer in both directions. I would add computed properties to the struct
to convert these Strings to Doubles as I needed them.&lt;/p&gt;
&lt;h2&gt;Advanced Decoding&lt;/h2&gt;
&lt;p&gt;Now that we have the basics, I want to look at three more features: changing
property names, date handling and allowing for nulls.&lt;/p&gt;
&lt;h2&gt;Changing Property Names&lt;/h2&gt;
&lt;p&gt;In these examples I used exactly the same names for the properties as were used
in the JSON. In the Swift 3 version, it would have been easy to change the
property names, since the data for each property was being extracted manually.
In Swift 4, if you want the decoder to change names, you have to tell it what
you want.&lt;/p&gt;
&lt;p&gt;Make a new playground or playground page and replace the contents with this - or
go to the Swift 4 Extras page on &lt;a href=&quot;https://github.com/trozware/json&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;my playground&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&quot;
{
&quot;name1&quot;: &quot;Jane&quot;,
&quot;name2&quot;: &quot;Smith&quot;
}
&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jsonString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonDecoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonDecoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jsonData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses the new multi-line literals in Swift 4 to assemble the JSON string
which is amazingly useful as it allows quotes to be embedded in the string
without having to escape them. As you can see, the JSON property names are not
very helpful, so I want to change them in my Person struct. At the moment,
&lt;code&gt;person&lt;/code&gt; is nil because I have not given the decoder any clues to help it
translate the names. To do that, I have to add a CodingKeys enum to the Person
struct and it tells the decoder what names in the JSON match up to what
properties in the struct.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodingKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodingKey&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; firstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name1&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; lastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name2&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&#39;s all I have to do. Now the JSON is correctly converted to a Person. As
an exercise, use JSONEncoder to get back to JSON from the Person. You will see
that the JSON correctly names the elements using &amp;quot;name1&amp;quot; and &amp;quot;name2&amp;quot;.&lt;/p&gt;
&lt;h2&gt;Date Handling&lt;/h2&gt;
&lt;p&gt;Next let&#39;s look at dates - add this code to the playground:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonString2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&quot;
{
&quot;name&quot;: &quot;My New Project&quot;,
&quot;created&quot;: &quot;2017-06-18T06:28:25Z&quot;
}
&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonData2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jsonString2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; created&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonDecoder2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; project &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonDecoder2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jsonData2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When this runs, &lt;code&gt;project&lt;/code&gt; is nil because the decoder has no idea how to get from
the String &amp;quot;2017-06-18T06:28:25Z&amp;quot; to a Date object. Add this line after creating
&lt;code&gt;jsonDecoder2&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;jsonDecoder2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dateDecodingStrategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iso8601&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the date can be converted and an instance of the Project struct will be
created.&lt;/p&gt;
&lt;p&gt;And to get back to the JSON, converting the Date back to ISO8601 format:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonEncoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONEncoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
jsonEncoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dateEncodingStrategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iso8601

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; backToJson &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonEncoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; backToJson&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; encoding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jsonString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;nulls&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Allowing For Nulls&lt;/h2&gt;
&lt;p&gt;JSON data will often include &lt;strong&gt;null&lt;/strong&gt; where there is no value for a particular
property name. When using JSONDecoder or JSONEncoder, this can be allowed for by
using Optionals. If you are not clear about Optionals, have a look at my
previous article: &lt;a href=&quot;https://troz.net/post/2016-02-12-learning-swift-optionals/&quot;&gt;Learning Swift - Optionals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The crucial step is to declare the properties of the Swift object as optionals
if you think they could get a null value.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Codable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; nickName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonStringWithNulls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&quot;
[
    {
        &quot;firstName&quot;: &quot;Sally&quot;,
        &quot;lastName&quot;: &quot;Sparrow&quot;,
        &quot;nickName&quot;: null
    },
    {
        &quot;firstName&quot;: &quot;Doctor&quot;,
        &quot;lastName&quot;: &quot;Who&quot;,
        &quot;nickName&quot;: &quot;The Doctor&quot;
    }
]
&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonDataWithNulls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jsonStringWithNulls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonDecoder3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; roles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonDecoder3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jsonDataWithNulls&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;roles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example (which you can find in the Swift 4 Extras page on &lt;a href=&quot;https://github.com/trozware/json&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;my
playground&lt;/a&gt;), I have declared a struct called Role with 3 String properties.
The 3rd String - nickName - is an Optional so it may be a String or it may be
nil.&lt;/p&gt;
&lt;p&gt;The JSON contains 2 elements - one has a nickName value and the other has it as
null. Because the matching property in the Swift struct is an optional, this
works as expected and the nickName property for Sally Sparrow is decoded as nil.
If you remove the question mark to make nickName non-optional, the decoding will
fail.&lt;/p&gt;
&lt;p&gt;Going back to JSON from a Swift object with optionals works much the same except
that it does not specifically mark items as null, it just leaves them out.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonEncoder2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSONEncoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
jsonEncoder2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;outputFormatting &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prettyPrinted

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; backToJsonWithNulls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; jsonEncoder2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;roles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jsonString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; backToJsonWithNulls&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; encoding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jsonString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Property Lists&lt;/h2&gt;
&lt;p&gt;As well as JSONDecoder and JSONEncoder, Swift 4 has introduced
PropertyListDecoder and PropertyListEncoder, so let&#39;s take a quick look at that.&lt;/p&gt;
&lt;p&gt;Add this to the bottom of the last playground as it uses the Project struct and
data:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; plistEncoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PropertyListEncoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
plistEncoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;outputFormat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xml
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; plist &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; plistEncoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; plistString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; plist&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; encoding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plistString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; plistDecoder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PropertyListDecoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; project2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; plistDecoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; plist&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don&#39;t see this is being quite as useful as the JSON Encoding &amp;amp; Decoding, but I
mention it here for completeness.&lt;/p&gt;
&lt;h2&gt;Codable&lt;/h2&gt;
&lt;p&gt;In all the Swift 4 examples above, I set the structs to conform to &lt;code&gt;Codable&lt;/code&gt;.
Reading Apple&#39;s docs, I see that &lt;code&gt;Codable&lt;/code&gt; is actually a typealias referring to
2 separate protocols:&lt;/p&gt;
&lt;pre class=&quot;language-Swift&quot;&gt;&lt;code class=&quot;language-Swift&quot;&gt;typealias Codable = Decodable &amp; Encodable&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you only need to convert data one way, it will be more efficient to set your
struct or class as confirming to only one of these protocols. For example, if
you download JSON data from an API but never need to send it back, just conform
to Decodable to transform the JSON data into your data structure.&lt;/p&gt;
&lt;p&gt;Much more information can be found in the &lt;a href=&quot;https://developer.apple.com/documentation/swift/encoding_decoding_and_serialization&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Apple documentation&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Posting from my new iPad</title>
    <link href="https://troz.net/post/2017/posting-from-my-new-ipad/" />
    <updated>2017-06-17T00:00:00Z</updated>
    <id>https://troz.net/post/2017/posting-from-my-new-ipad/</id>
    <content type="html">&lt;p&gt;Last week I got a 10.5” iPad Pro. I have had an iPad ever since the original
release, but I have to confess to mainly using it for content consumption rather
than creation. Since I am usually close to a Mac anyway, there didn’t seem a lot
of point. But now that iOS 11 is really addressing the issue of making the iPad
a &amp;quot;pro&amp;quot; device, I decided to give it a try.&lt;/p&gt;
&lt;p&gt;So here I am, sitting in an armchair with my iPad on my lap, writing this post
and working out the details as I go. Any typos are due to the cat trying to
compete with the iPad for space on my lap!&lt;/p&gt;
&lt;h2&gt;iPad&lt;/h2&gt;
&lt;p&gt;As already mentioned, I have a new iPad Pro. I also have an Apple Pencil which I
use for taking notes. I do not have a keyboard, so I am using the on-screen
keyboard right now.&lt;/p&gt;
&lt;p&gt;I don’t usually install beta versions of operating systems, but iOS 11 is a huge
part of making the iPad more professional, so I have installed it. As you would
expect, there are a few issues (apps quitting, layout issues especially when
rotating) but so far I am loving it.&lt;/p&gt;
&lt;h2&gt;Writing&lt;/h2&gt;
&lt;p&gt;My site is published on &lt;a href=&quot;https://pages.github.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub Pages&lt;/a&gt; using &lt;a href=&quot;http://jekyllrb.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jekyll&lt;/a&gt; with all the posts
and pages written in Markdown. One key feature is that every post or page
requires a chunk of &amp;quot;front matter&amp;quot; that must be formatted correctly for the
Jekyll parser to interpret it and create the static content. After a bit of
research, it looked like &lt;a href=&quot;http://omz-software.com/editorial/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Editorial&lt;/a&gt; was going to be the best option for an
editor app. I moved my site files to Dropbox, linked my Dropbox account to
Editorial and I was ready to start writing.&lt;/p&gt;
&lt;p&gt;I didn’t want to create the front matter manually for each post. On my Mac I
have a text expander snippet that handles that but copying from &lt;a href=&quot;http://mattgemmell.com/using-the-ipad-for-blogging-with-jekyll/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Matt Gemell’s
post&lt;/a&gt; on the topic, I created an Editorial workflow to create a new post. As
a side-note, Matt has a series of posts on using an iPad which I have found very
useful and inspirational.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2017/Workflow.jpeg&quot; alt=&quot;Editorial workflow&quot;&gt;&lt;/p&gt;
&lt;p&gt;Text entry and editing is a task that I have always felt was too difficult in
iOS, particularly when it came to selecting text and moving the cursor. However
there are now several improvements. As with iOS 10, a two-fingered press on the
keyboard transforms it into a trackpad for moving the cursor. If you already
have text selected, this moves the selection handles. The new iOS 11 keyboard
has a really nice feature for entering non-alphabetic characters. The keys now
show a small grey character above the main character. Drag down on the key to
move the small character into the main key and type it. For example, drag down
on the h key to type (.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2017/keyboard-drag.png&quot; alt=&quot;Keyboard drag&quot;&gt;&lt;/p&gt;
&lt;p&gt;Editorial has its own editing helpers. There is an additional toolbar above the
keyboard with a tab key as well as frequently used Markdown modifiers. And
swiping across this toolbar moves the cursor. This is a much slower move than
when using the keyboard as a trackpad so might be better for more precise
movements.&lt;/p&gt;
&lt;h3&gt;Adding Links:&lt;/h3&gt;
&lt;p&gt;I have referenced a few sites now, so I guess it is time to work out how to link
to them. I always use Markdown’s referenced link method, with the links
collected at the foot of the document, so that is what I plan to do here. To set
text as a link, I selected it and used the Editorial toolbar to type an opening
square bracket which surrounded the selection in square brackets. Then I moved
the cursor to after the closing bracket and typed another opening square
bracket. This auto-closed and I was able to type a number which will be the
reference.&lt;/p&gt;
&lt;p&gt;Next step is to find the relevant URLs - drag &amp;amp; drop with multi-tasking to the
rescue! Swipe up from the bottom of the screen to see the new Dock. This also
makes the keyboard disappear. Hold down on the Safari icon until you grab it,
then drag it up into the Editorial window. Now it is open as an overlay.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2017/safari-overlay.png&quot; alt=&quot;Safari as overlay&quot;&gt;&lt;/p&gt;
&lt;p&gt;At this point, I have several options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Swiping from the left or right of the overlay window lets me position it to
the right or left of the screen, still as an overlay.&lt;/li&gt;
&lt;li&gt;Dragging from the bar at the top of the overlap allows me to convert to split
screen mode, and I can then drag the divider to adjust the ratios.&lt;/li&gt;
&lt;li&gt;Swiping all the way to the right disappears the overlay, but swiping back from
the right edge returns it. Since I want to copy and paste some links, this
looks like the best option right now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This swiping conflicts slightly with Editorial&#39;s swiping from the side to see a
preview. Swipe from off the edge for the overlap and from well inside the edge
for preview.&lt;/p&gt;
&lt;p&gt;And here my plans come crashing down. I can drag from Safari&#39;s address bar into
the Editorial document, but the drop doesn’t take. I guess I need an update to
Editorial for this to work. But I can still use the old style copy and paste to
get the links.&lt;/p&gt;
&lt;h3&gt;Images&lt;/h3&gt;
&lt;p&gt;So now I have my links, but I want to insert a few images. I used iOS 11’s new
screen shot abilities. Press the usual home button &amp;amp; power button combination to
take a screen shot and it appears as a small picture in the bottom left of the
screen. Tap it to start editing: markup, crop etc. Then I used the sharing
button to save the picture to Dropbox, putting it inside the images folder in my
web site folder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2017/screenshot.jpeg&quot; alt=&quot;Editing a screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;I want to insert an image link but I have just worked out that I saved this
Markdown file in the root folder instead of the _posts folder. I can’t see a way
to move it from inside Editorial, so I will swap to the Dropbox app to move the
file - be right back... (I see now how I could have done this in Editorial.)&lt;/p&gt;
&lt;p&gt;Ok, this file is in the right place and the images are in the right folder, so
how to add an image link? Checking back at previous posts, I see that the format
I use for inserting an image is: &lt;code&gt;![Alt text][#]&lt;/code&gt; and the reference at the foot
of the page is like&lt;code&gt;[#]: /images/2017/image_name.jpg&lt;/code&gt; but I don’t want to have to
remember that every time, so I think it is time for some snippets. Tapping the
lightning icon on the Editorial keyboard lets me edit and create snippets, so I
have made one for the image link and one for the image reference. Time to scroll
back up the page and try them out.&lt;/p&gt;
&lt;p&gt;I opened the Dropbox app in an overlay so that I could find the file names for
the images I just saved, then I created the links. The images don’t appear in
the preview but that is expected because Jekyll has not parsed the addresses.
Looking at the information about the images in Dropbox, I see that they are far
too big. I need a way to resize them. The screen shot editor doesn’t have a way
to resize apart from cropping, at least not in this beta.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://workflow.is/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Workflow&lt;/a&gt; is another Pro level app that I have never really used, but it
seems that it might do the job. I tried a few workflows to edit the size of an
image from a Dropbox file and save it back, but they although they appeared to
complete, the image file remained the same size. So then I went to the Workflow
Gallery and found a workflow called Quick Resize Screenshot. When editing a
screen shot, I can use the sharing button to run the workflow and then I can
save the smaller images to Dropbox for use in the page. For the large images
that I have already saved, I will resize them on my Mac before publishing.&lt;/p&gt;
&lt;h2&gt;Publishing&lt;/h2&gt;
&lt;p&gt;This brings me to the final step - publishing the changes to GitHub Pages.
Jekyll is a system that creates static web pages from the Markdown and other
files. on my Mac, I run the &lt;code&gt;jekyll build&lt;/code&gt; command in Terminal and the pages are
created or re-generated as required. Then I use &lt;code&gt;git&lt;/code&gt; to commit my changes and
push them to GitHub which serves them.&lt;/p&gt;
&lt;p&gt;There appear to be Editorial workflows written in Python that use the GitHub
APIs to upload files, but this wouldn’t work well for me. Because of the
structure of my site, many different pages need to be re-generated when I
publish a new post, so I will keep the publishing phase on my Mac. However there
is no need to make it any more difficult than necessary, so I have written a
shell script to do the work.&lt;/p&gt;
&lt;p&gt;The script takes an optional commit message parameter, builds the site, commits
changes and pushes then to GitHub. I have only tested this when no files had
changed, so this post will be the first real test. Shell scripting is outside my
comfort zone, so we will have to see how that works.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;So now I have a workflow on my iPad that uses Editorial, Dropbox and Workflow.
This allows me to do 100% of the writing task. Then I have a single shell script
on my Mac that does the publishing part. I can edit and create posts anywhere. I
can easily use split screen or screen overlays to add links. iOS 11 makes
creating and annotating screen shots very easy.&lt;/p&gt;
&lt;p&gt;I am hoping for an update to Editorial that will allow it to accept drag &amp;amp; drop.
The ability to resize screen shots before saving them would also be really
useful.&lt;/p&gt;
&lt;p&gt;Sometimes my posts are more code-based and need access to Xcode and in those
cases, I will stick to my Mac, especially considering Xcode’s new ability to
connect directly to GitHub which will make it great for publishing Swift
playgrounds. But for more narrative-style posts like this one, I will use my
iPad and see how I can improve my workflow and expand my tool set.&lt;/p&gt;
&lt;p&gt;It feels good to be using the iPad as a content creation device at last.&lt;/p&gt;
&lt;h2&gt;Extra Notes from a Mac:&lt;/h2&gt;
&lt;p&gt;Since this was my first time writing a blog post using an iPad and this
workflow, I decided that I should test the results before publishing, so I
switched back to the Mac and in Terminal, ran &lt;code&gt;jekyll serve&lt;/code&gt; so that I could see
the results. There were a few issues mainly to do with the formatting of the
front matter. The three dashes at the top &amp;amp; bottom of the front matter had
become munged into an emdash plus a dash, so that didn’t work. Jekyll is also
fussy about the spaces in the tag &amp;amp; summary lines and it doesn&#39;t handle
non-alphanumerics in the summary text.&lt;/p&gt;
&lt;p&gt;I also used the Mac to shrink some of the images that I had saved before working
out how to reduce the size of screen shots.&lt;/p&gt;
&lt;p&gt;So I need to check the structure of the front matter workflow before next time,
but this post is ready to go with only minor editing on the Mac side. Now to
test my shell script...&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>JSON Feed for TrozWare</title>
    <link href="https://troz.net/post/2017/json-feed-for-trozware/" />
    <updated>2017-05-20T00:00:00Z</updated>
    <id>https://troz.net/post/2017/json-feed-for-trozware/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://jsonfeed.org/2017/05/17/announcing_json_feed&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;jsonfeed.org&lt;/a&gt; has announced a new format for web site feeds, designed as an
alternative to RSS. TrozWare has had an XML RSS feed for years, but I don&#39;t
think anyone ever uses it (I certainly don&#39;t), so today I have replaced it with
a JSON feed, which you can access through the
&lt;a class=&quot;fa fa-rss&quot; href=&quot;https://troz.net/feed.json&quot;&gt;&lt;/a&gt; button at the top of every page.&lt;/p&gt;

&lt;p&gt;I am sure many JSON Feed viewers will appear soon, but the only one I know about
so far is at &lt;a href=&quot;http://json-feed-viewer.herokuapp.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;http://json-feed-viewer.herokuapp.com&lt;/a&gt;. As soon as this update
to my site goes live, I will apply to have TrozWare added to the list of sites
on this page. Meanwhile, you can paste in the URL: &lt;code&gt;https://troz.net/feed.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This site is constructed using Jekyll, so I am very grateful to Niclas Darville
for his very easy to follow &lt;a href=&quot;https://ndarville.com/blog/2017/05/19/json-feed-for-jekyll/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;guide&lt;/a&gt;. However it is still well worth reading
through the &lt;a href=&quot;https://jsonfeed.org/version/1&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;official specs&lt;/a&gt; to see what else you want to add, or if you want
to remove anything. I had to tweak a few settings to make it work for my
configuration, and I added some more objects, but Niclas got me off to a flying
start.&lt;/p&gt;
&lt;p&gt;Two things to watch out for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make sure your feed is producing valid JSON (I had an extra comma that was
breaking it...).&lt;/li&gt;
&lt;li&gt;As third-party apps &amp;amp; sites may be displaying your content, you need to make
sure that you are not using relative URLs for images and internal links. I
was using relative image URLs like &lt;code&gt;/images/WorkHelp1.png&lt;/code&gt; but that showed a
broken link in the feed viewer, so I have changed all such links to
&lt;code&gt;/images/WorkHelp1.png&lt;/code&gt;. Hopefully that will work correctly on my local test
server as well as when published. &lt;br&gt;&lt;br&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://jsonfeed.org/graphics/icon.png&quot; alt=&quot;JSON Feed icon&quot;&gt;&lt;/p&gt;
&lt;p&gt;JSON Feed offers a nice icon which I would have liked to use, but I could not
work out a way to make it play nicely with the existing icons on my pages which
all use &lt;a href=&quot;http://fontawesome.io&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Font Awesome&lt;/a&gt; icons. So I ended up just using the existing Feed
icon. Hopefully Font Awesome will soon add a JSON Feed icon to their already
impressive list.&lt;/p&gt;
&lt;p&gt;If anyone still wants to use the &lt;a href=&quot;https://troz.net/index.xml&quot;&gt;RSS feed&lt;/a&gt;, it is no longer linked to the
buttons at the top of the pages, but you can access it manually.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Blackmail Marketing</title>
    <link href="https://troz.net/post/2017/blackmail-marketing/" />
    <updated>2017-03-08T00:00:00Z</updated>
    <id>https://troz.net/post/2017/blackmail-marketing/</id>
    <content type="html">&lt;p&gt;I use my iPad to play games as a form of relaxation. Nearly all the games I play
are puzzle apps with levels that can be played in a few minutes. Nearly all of
them are free to install. So how do they make money?&lt;/p&gt;
&lt;p&gt;Now I would be the last person you should consult about marketing but it has
always seemed to me that the best way to market your product is to convince
people that it offers something they want so that they are prepared to pay for
it.&lt;/p&gt;
&lt;p&gt;This is not the currently favored approach for mobile apps - at least certainly
not for games. No, the theory at the moment is to cause so much frustration and
annoyance that people give you money to make it stop. Blackmail marketing.&lt;/p&gt;
&lt;p&gt;A frequently used method is showing ads, sometimes just banner ads but more
often full screen video ads that run for up to 30 seconds. If you interrupt the
ad, you don&#39;t get the reward.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every goal reached triggers an ad.&lt;/li&gt;
&lt;li&gt;Quitting and resuming the app triggers an ad.&lt;/li&gt;
&lt;li&gt;Wanting to play another level triggers an ad.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The theory is that you get so annoyed with this that you pay money to disable
the ads.&lt;/p&gt;
&lt;p&gt;The other common tactic is the delay. A tower defence game needs you to build a
tower? OK, that will take 12 hours real time and you can&#39;t proceed until it&#39;s
finished. Or you can spend 12 gems which are sold as an in-app purchase. Maybe
you can only play 3 levels and then you need to recharge: again, pay up or wait.
You keep dying on that level? You&#39;re out of lives. Pay up or wait until
tomorrow. These delays are completely artificial. They have nothing to do with
game play but are solely designed to infuriate you enough to get you to pay.&lt;/p&gt;
&lt;p&gt;So when, and perhaps more importantly why, did mobile app marketing become so
negative? What happened to making your customers happy? Do unhappy customers
spend more?&lt;/p&gt;
&lt;p&gt;Financially, it appears that irritating your users is a successful strategy.
Nearly all the top grossing games in the App Store are &amp;quot;free to play&amp;quot; in that
the initial download costs nothing. But these big studios are raking in enormous
sums of money, so many people are persuaded to spend significant money by these
infuriating schemes.&lt;/p&gt;
&lt;p&gt;A large part of this must be due to the various App Stores driving prices to the
bottom. Gone are the days when the price of a mobile app was other than risible.
So app developers have four choices:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do it as a hobby or learning exercise.&lt;/li&gt;
&lt;li&gt;Distribute apps as a form of self–promotion for other work.&lt;/li&gt;
&lt;li&gt;Work for someone who pays you a real wage to develop apps.&lt;/li&gt;
&lt;li&gt;Join the blackmailers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So how does this relate to indy developers like me? I care about my users and
want them to be happy.&lt;/p&gt;
&lt;p&gt;I have tried several different approaches to app distribution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Totally free gets by far the most downloads but is economically ridiculous
unless the app adds value to your business in some other way.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A tip jar gets you nothing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In-app purchases (in my limited experience) make less than selling the app
up-front for a small price.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Banner ads with no method of forcing people to watch them generate almost no
revenue.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So that leaves me with where I am today - selling apps for a few dollars and
nothing more. Happily, I have found that Mac users are more prepared to pay a
few dollars for an app, so I have shut down many of my iOS apps and am
concentrating on the Mac app market.&lt;/p&gt;
&lt;p&gt;Am I letting my principles stand in the way of mobile app profits? Should I just
join the gang and start blackmailing my customers? If so, what form should this
take?&lt;/p&gt;
&lt;p&gt;At the moment, I am considering banner ads on the screen with an in-app purchase
to disable them entirely. I read a suggestion for a less invasive method of
doing this by creating your own ads for your other apps. This might be what I
end up doing.&lt;/p&gt;
&lt;p&gt;I would welcome any suggestions, advice or comments. Please join the discussion
below or contact me using one of the links at the top of the page.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>NCSS 2017</title>
    <link href="https://troz.net/post/2017/ncss-2017/" />
    <updated>2017-01-06T00:00:00Z</updated>
    <id>https://troz.net/post/2017/ncss-2017/</id>
    <content type="html">&lt;p&gt;I gave a Masterclass on Swift at the NCSS Summer School 2017 in Sydney on 8th
January 2017. This post contains useful links for use during and after my class.&lt;/p&gt;
  
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://swiftlang.ng.bluemix.net/#/repl&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;IBM Swift Sandbox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Sandbox Code Samples&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://swiftlang.ng.bluemix.net/#/repl/58670662da20a814737c3562&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Strings&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://swiftlang.ng.bluemix.net/#/repl/5868463fb5619f13e0a940af&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Optionals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://swiftlang.ng.bluemix.net/#/repl/5868954cb5619f13e0a940bd&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Functions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://swiftlang.ng.bluemix.net/#/repl/5866f31fda20a814737c353b&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Closures&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://swiftlang.ng.bluemix.net/#/repl/5866f48bda20a814737c353c&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Objects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://swiftlang.ng.bluemix.net/#/repl/5866ed80da20a814737c3538&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Protocols&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;http://swiftlang.ng.bluemix.net/#/repl/5866fc4dda20a814737c3557&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Generics&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Contact Details:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Email: &lt;a href=&quot;mailto:sarah@troz.net?subject=NCSS%202017&quot;&gt;sarah@troz.net&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Twitter: &lt;a href=&quot;https://twitter.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@trozware&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Slides:
&lt;a href=&quot;https://www.dropbox.com/s/09r19my3d6jk5jt/NCSS%202017%20Swift%20MC.pdf?dl=0&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;NCSS 2017 Swift MC.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Useful Links:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://itunes.apple.com/au/book/swift-programming-language/id881256329?mt=11&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;The Swift Programming Language&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://swift.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift.org&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.raywenderlich.com/132258/top-5-ios-swift-newsletters&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Newsletters&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.raywenderlich.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Tutorials&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Looking for beta testers</title>
    <link href="https://troz.net/post/2016/looking-for-beta-testers/" />
    <updated>2016-11-17T00:00:00Z</updated>
    <id>https://troz.net/post/2016/looking-for-beta-testers/</id>
    <content type="html">&lt;p&gt;I am looking for beta testers for my latest Mac app: &lt;strong&gt;&lt;a href=&quot;https://troz.net/work/&quot;&gt;Work&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Work&lt;/strong&gt; is a project organizer app for developers, designers, students or any
professional who works with multiple projects involving files &amp;amp; folders, web
pages, email addresses and lists of tasks. Click the link above for more
information and help for getting started.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Work&lt;/strong&gt; requires macOS 10.12 or OS X 10.11.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you would like to test this app, please &lt;a href=&quot;mailto:work@troz.net?subject=Work%20beta%20test&quot;&gt;contact me&lt;/a&gt;, giving details of
your Mac and operating system. The easiest way to do this is to select &lt;strong&gt;About
This Mac&lt;/strong&gt; in the Apple menu and send me a screen shot of the window that
appears.&lt;/p&gt;
&lt;p&gt;All beta testers will receive a free license to Work when it is released. If you
report a legitimate bug or provide valuable feed-back, you will also receive a
free license to your choice of one of my &lt;a href=&quot;https://troz.net/apps/&quot;&gt;other apps&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Another Man Reader Update...</title>
    <link href="https://troz.net/post/2016/another-man-reader-update/" />
    <updated>2016-11-05T00:00:00Z</updated>
    <id>https://troz.net/post/2016/another-man-reader-update/</id>
    <content type="html">&lt;p&gt;A quick update to &lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader&lt;/a&gt; to version 1.8 is now available through the
&lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main reason for this version was that Man Reader was not previously
detecting man pages installed by third-party IDEs in the Applications folder.
There was also an issue with Man Reader failing to display the text of the
previously selected man page on app launch.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More complete search for man pages, including in third-party IDEs.&lt;/li&gt;
&lt;li&gt;Fix for glitch when displaying previously selected page on launch.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Man Reader Update</title>
    <link href="https://troz.net/post/2016/man-reader-update/" />
    <updated>2016-10-12T00:00:00Z</updated>
    <id>https://troz.net/post/2016/man-reader-update/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader&lt;/a&gt; has just been updated to version 1.7 and is available through
the &lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main reason for this update was to make the app work well with macOS Sierra,
as I found that version 1.6 was sometimes crashing on launch. While doing this,
the update was rejected by the App Store reviewers because it crashed on OS X
10.10. Since I no longer have a Mac running 10.10, I decided to set the minimum
supported system version to OS X 10.11. If you need support for older versions,
you should still be able to download version 1.6 which will work back to 10.7.&lt;/p&gt;
&lt;p&gt;I also took the opportunity to fix some graphical issues, dealing with different
color schemes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The man page list now shows alternating colors even when not using the default
scheme.&lt;/li&gt;
&lt;li&gt;When scrolling the man pages past the top or the bottom with a non-white
background, you should no longer see white blocks top &amp;amp; bottom.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Podcasting</title>
    <link href="https://troz.net/post/2016/podcasting/" />
    <updated>2016-10-11T00:00:00Z</updated>
    <id>https://troz.net/post/2016/podcasting/</id>
    <content type="html">&lt;p&gt;I have recently started what I hope will be a weekly podcast with my friend and
colleague, &lt;a href=&quot;https://twitter.com/jerrydaniels&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jerry Daniels&lt;/a&gt;. Each podcast will be about 30 minutes in length.&lt;/p&gt;
&lt;p&gt;We will discuss Macs, iPhones, iPads, macOS, iOS, programming and anything
tech-related that we find interesting. I hope you will find it entertaining and
informative, so please join us.&lt;/p&gt;
&lt;p&gt;We are using &lt;a href=&quot;https://zcast.co/zcasts&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;ZCast&lt;/a&gt; which has an iPhone app that makes it super easy to get
together and record our chats. It doesn&#39;t allow any post-processing, but really
lowers the barrier to entry into the world of podcasting.&lt;/p&gt;
&lt;p&gt;If you get the &lt;a href=&quot;https://itunes.apple.com/app/zcast-podcast-from-your-phone!/id1041586670&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;ZCast app for iPhone&lt;/a&gt;, go to the ZCasters tab and search for
&amp;quot;Trozware&amp;quot; or &amp;quot;Jerry Daniels&amp;quot; to find all our podcasts. Or use these links to
listen in your browser.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://zcast.co/j/ZyB8yK4z86&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;The new macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zcast.co/j/ewQe6x6E4d&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hands-on with iPhone 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zcast.co/j/A9zvLAAEa3&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hands on iOS 10&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since the first one was recorded using my name instead of my &lt;a href=&quot;https://twitter.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@trozware&lt;/a&gt;
Twitter account, I will embed it here as it will not show up if you search for
&amp;quot;Trozware&amp;quot;.&lt;/p&gt;
&lt;IFRAME src=&quot;https://zcast.co/embedded/ZyB8yK4z86&quot; width=&quot;350&quot; height=&quot;500&quot; STYLE=&quot;width: 350; height: 500; border: none;&quot; scrolling=&quot;auto&quot;&gt;&lt;/IFRAME&gt;
</content>
  </entry>
  <entry>
    <title>Dice Pass Updated</title>
    <link href="https://troz.net/post/2016/dice-pass-updated/" />
    <updated>2016-09-17T00:00:00Z</updated>
    <id>https://troz.net/post/2016/dice-pass-updated/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://troz.net/dicepass/&quot;&gt;Dice Pass 1.1&lt;/a&gt; is now available from the &lt;a href=&quot;https://itunes.apple.com/app/dice-pass/id997688302&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What does Dice Pass do?&lt;/h2&gt;
&lt;p&gt;Dice Pass is a utility to app to generate random passphrases based on the
&lt;a href=&quot;http://world.std.com/~reinhold/diceware.html&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Diceware&lt;/a&gt; system. Diceware uses a table of pre-defined words, each assigned
a 5 digit code number containing the digits 1 to 6. The official way to generate
a phrase is to roll 5 dice for each word you want in your passphrase and then
consult the words table to find the matching word.&lt;/p&gt;
&lt;p&gt;The resulting phrase uses real words or common abbreviations so it is easier to
remember than a random collection of characters. At the same time, the random
selection of words makes the generated passphrase much more secure as it avoids
the human tendency to pick words with personal meaning.&lt;/p&gt;
&lt;p&gt;Dice Pass allows you to select the number of words in your passphrase,
re-generate the entire passphrase any time, re-roll a single word in your
passphrase or set the dice rolls manually for maximum security and randomness.&lt;/p&gt;
&lt;h2&gt;What&#39;s New in Version 1.1:&lt;/h2&gt;
&lt;p&gt;You can now select from three word lists in the Word Lists menu:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;original Diceware list&lt;/li&gt;
&lt;li&gt;Beale list (fewer Americanisms and obscure words)&lt;/li&gt;
&lt;li&gt;Diceware 8k list (a list of words that is a whole power of two in length)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also import your own word list if you prefer.&lt;/p&gt;
&lt;p&gt;Dice Pass 1.1 is compatible with macOS Sierra.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How much work does Icon Builder save you?</title>
    <link href="https://troz.net/post/2016/how-much-work-does-icon-builder-save-you/" />
    <updated>2016-09-17T00:00:00Z</updated>
    <id>https://troz.net/post/2016/how-much-work-does-icon-builder-save-you/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://troz.net/icon-builder/&quot;&gt;Icon Builder&lt;/a&gt; is a Mac app that takes a single image file and creates all
the different image sizes that you need to make a set of icons for your app:
Mac, iPhone, iPad, iOS Universal or Apple Watch.&lt;/p&gt;
&lt;p&gt;Version 4, released 16 September 2016 is available through the &lt;a href=&quot;https://itunes.apple.com/app/icon-builder/id552293482&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App
Store&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What&#39;s New in Version 4:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Added support for iMessage apps and Sticker Pack apps.&lt;/li&gt;
&lt;li&gt;Added support for creating Mac .icns files.&lt;/li&gt;
&lt;li&gt;Better removal of alpha channel for Apple Watch icons.&lt;/li&gt;
&lt;li&gt;Clearer usage instructions in ReadMe files.&lt;/li&gt;
&lt;li&gt;iTunes Artwork folders will no longer be over-written with the latest image
files.&lt;/li&gt;
&lt;li&gt;Supports macOS Sierra and Xcode 8&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/IconBuilder.png&quot; alt=&quot;Icon Builder&quot;&gt;&lt;/p&gt;
&lt;p&gt;While working on version 4 and accommodating all these new icon sets (and
wishing I had the time to re-write the app in Swift...), I counted up all the
icon files that Icon Builder makes for each app type:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:left&quot;&gt;App Type&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Number of Icons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;Mac&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iPhone&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iPhone supporting pre iOS 7&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iPad&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iPad supporting pre iOS 7&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iOS Universal&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iOS Universal supporting pre iOS 7&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;Apple Watch (also requires iOS app icons)&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;Sticker Pack app&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iMessages app&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;iMessages app Messages extension&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So as you can see, Icon Builder is doing a lot of work for you. It also names
all the icon files using the expected format, stores them in an concept folder,
creates the JSON file that identifies them all to Xcode and optionally installs
them in your Xcode project automatically. That’s a lot of value for dragging in
an icon and clicking a button!&lt;/p&gt;
&lt;p&gt;So next time your designer sends you the twentieth tweaked icon for the day,
don&#39;t get mad. Just drop it into Icon Builder and sit back while it does all the
work. (No need to tell the designer that...)&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Retiring old apps</title>
    <link href="https://troz.net/post/2016/retiring-old-apps/" />
    <updated>2016-09-15T00:00:00Z</updated>
    <id>https://troz.net/post/2016/retiring-old-apps/</id>
    <content type="html">&lt;p&gt;With the release of iOS 10 and the need to update iOS apps to suit, I have
decided it is time to retire some of my lesser-performing apps as it is not
worth the effort required to maintain them.&lt;/p&gt;
&lt;p&gt;At the moment, the following iOS apps are no longer available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Knight&#39;s Move&lt;/li&gt;
&lt;li&gt;Dice Pass&lt;/li&gt;
&lt;li&gt;Pic-a-POD&lt;/li&gt;
&lt;li&gt;Quick Score - Tennis&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the first three listed there, the Mac versions are still available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://itunes.apple.com/app/a-knights-move/id533321133&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;A Knight&#39;s Move for Mac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itunes.apple.com/app/dice-pass/id997688302&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Dice Pass for Mac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itunes.apple.com/app/pic-a-pod/id477909802&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Pic-a-POD for Mac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you bought any of these apps and would like a promo code for the Mac version,
send me a screen shot of the app running on your iPhone or iPad and I will send
you a code.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Man Reader updated to 1.6</title>
    <link href="https://troz.net/post/2016/man-reader-updated-to-1.6/" />
    <updated>2016-07-16T00:00:00Z</updated>
    <id>https://troz.net/post/2016/man-reader-updated-to-1.6/</id>
    <content type="html">&lt;p&gt;Man Reader has been updated to version 1.6 with the permissions needed for
finding all available man pages and a fix for search terms being over-written
after a background update.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader 1.6&lt;/a&gt; is now available through the &lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Change Log:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Permissions fix when searching for available man pages.&lt;/li&gt;
&lt;li&gt;Fixed error with new search term being over-written.&lt;/li&gt;
&lt;li&gt;Sand-boxed version now works just as well as the non-sand-boxed so please
switch back to this version.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This version fixes a problem with sand-boxing permissions potentially not
allowing access to all the installed man pages on your system. This should now
be fixed but if you discover any man pages that are available through Terminal
but not through Man Read, please &lt;a href=&quot;mailto:sarah@troz.net?subject=Man%20Reader%20not%20detecting%20some%20man%20pages&quot;&gt;email me the details&lt;/a&gt; and help me make Man
Reader better.&lt;/p&gt;
&lt;p&gt;There was also an issue with a previous search being restored after a background
update, even if a new search term had been entered. This has been fixed.&lt;/p&gt;
&lt;p&gt;If you previously had downloaded the Man Reader (no SB) version of the app from
this site, please revert to the App Store version which you would have needed to
have bought in order to use the downloaded app. If you bought the app from
Paddle, &lt;a href=&quot;mailto:sarah@troz.net?subject=Man%20Reader%20transfer%20to%20App%20Store&quot;&gt;contact me&lt;/a&gt; and I will transfer you to the App Store version.
Neither of these other versions will be supported or updated any more.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Dice Pass Mac Update</title>
    <link href="https://troz.net/post/2016/dice-pass-mac-update/" />
    <updated>2016-07-03T00:00:00Z</updated>
    <id>https://troz.net/post/2016/dice-pass-mac-update/</id>
    <content type="html">&lt;p&gt;I recently got an email from someone who had purchased &lt;a href=&quot;https://troz.net/dicepass/&quot;&gt;Dice Pass&lt;/a&gt; for Mac
from the &lt;a href=&quot;https://itunes.apple.com/app/dice-pass/id997688302&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt; but had been unable to get it to run. This person
was using OS X 10.9.3.&lt;/p&gt;
&lt;p&gt;I checked the App Store specifications and it showed that 10.9 was the minimum
system requirement, so it should have worked. But going back to my Xcode
project, I found that it was set to a minimum of OS X 10.10.&lt;/p&gt;
&lt;p&gt;I tried to re-compile for OS X 10.9 but this failed as the app uses several
features that are not available prior to 10.10.&lt;/p&gt;
&lt;p&gt;The minimum system version as displayed in the App Store is supposedly deduced
automatically by the App Store servers from the app&#39;s binary. Somehow this was
incorrect in the App Store, so some people may have bought that app and found
that it did not work despite them having what was listed as a compatible system.&lt;/p&gt;
&lt;p&gt;If you bought Dice Pass for OS X 10.9 and are unable to run it, I have two
possible solutions for you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Upgrade your operating system to at least 10.10.&lt;/li&gt;
&lt;li&gt;Ask Apple for a refund since it was their error that caused you to buy an app
that you cannot run.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I have updated the app in the App Store changing nothing but the version number
and it is now showing the correct minimum system requirement. My apologies if
you have been inconvenienced by this error.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Learning Swift - Sets</title>
    <link href="https://troz.net/post/2016/learning-swift-sets/" />
    <updated>2016-05-28T00:00:00Z</updated>
    <id>https://troz.net/post/2016/learning-swift-sets/</id>
    <content type="html">&lt;p&gt;Sets are the forgotten collection type in many languages, including Swift. I
think most developers use Arrays without really considering the advantages of
using a Set but they have some amazingly useful features that should make them a
part of any programmer&#39;s toolkit.&lt;/p&gt;
&lt;p&gt;If you want to follow along with a playground, you can download it &lt;a href=&quot;https://github.com/trozware/sets&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What is a Set?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;A Set is an un-ordered collection of unique items.&lt;/strong&gt; That&#39;s it - nothing more
than that. So it is very similar to an Array, but it is not indexed like an
Array and it cannot contain more than one of each entry.&lt;/p&gt;
&lt;h2&gt;Creating a Set&lt;/h2&gt;
&lt;p&gt;Creating a Set is as easy as creating an Array:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; myArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hamster&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; mySet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hamster&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are running these commands in a playground, notice that the differences
between the 2 results:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hamster&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// myArray&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hamster&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;           &lt;span class=&quot;token comment&quot;&gt;// mySet&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;The Array is shown wrapped in square brackets, the Set is shown wrapped in
curly braces. This is just a visual clue and doesn&#39;t really mean anything.
You cannot initialize a set using curly braces.&lt;/li&gt;
&lt;li&gt;All the supplied elements of the Array are listed, but the Set has removed
the duplicate &amp;quot;dog&amp;quot; element. This did not cause an error or warning, it just
happened quietly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When initializing a Set, you must add &lt;code&gt;: Set&lt;/code&gt; to distinguish it from an array
initialization. In the example above, I did not specify the data type of the
elements in the Set as the Swift compiler was able to infer this from the
contents. But if initializing an empty array, the data type must be specified.
To check how to do this, I option-clicked on &lt;code&gt;mySet&lt;/code&gt; to see what the Swift
compiler thought it was.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/set_types.png&quot; alt=&quot;Sets&quot;&gt;&lt;/p&gt;
&lt;p&gt;So mySet is actually &lt;code&gt;Set&amp;lt;String&amp;gt;&lt;/code&gt;. This means that to create an empty Set, you
need to use something like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; emptySetOfStrings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;
emptySetOfInts&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Adding and removing elements&lt;/h2&gt;
&lt;p&gt;If you have been using an Array to store unique values, then you have probably
written code like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Sets, you don&#39;t have to care. Just use &lt;code&gt;insert()&lt;/code&gt; and let the Set work out
whether to add the item or not.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;mySet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;goldfish&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// goldfish added: {&quot;hamster&quot;, &quot;cat&quot;, &quot;dog&quot;, &quot;goldfish&quot;}&lt;/span&gt;
mySet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// dog already there: {&quot;hamster&quot;, &quot;cat&quot;, &quot;dog&quot;, &quot;goldfish&quot;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Removing elements is also easier than in Arrays. For an Array, you first have to
find the index of the element and remove it by index:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// myArray.remove(&quot;hamster&quot;) // will not compile&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hamster&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  myArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;at&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But in a Set, you can remove any element easily and trying to remove an element
that doesn&#39;t exist will fail without an error.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;mySet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hamster&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// returns &quot;hamster&quot;&lt;/span&gt;
mySet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;canary&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// returns nil&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Converting between Sets and Arrays&lt;/h2&gt;
&lt;p&gt;Sometimes you need to be able to switch between the two. My most recent example
was when I wanted to store data from a Set in a plist. Sets are not property
list types but Arrays are, so I converted the Set to an Array before storing it
in the plist. When reading the data in from the plist, I converted it back to a
Set.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; myArrayAsSet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; mySetAsArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mySet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One useful side-effect of these easy conversions is the ability to &#39;unique&#39; an
Array in a single line. This may be inefficient for large arrays, but works very
well for small ones. Just be careful if the order of the elements is important
as you cannot guarantee the order of elements in a Set.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; myArrayUniqued &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// [&quot;cat&quot;, &quot;dog&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Iterating over elements in a Set&lt;/h2&gt;
&lt;p&gt;As with an Array, you can use a &lt;code&gt;for element in set&lt;/code&gt; structure, or you can use
&lt;code&gt;enumerated()&lt;/code&gt;. But you cannot subscript a Set.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; animal &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; mySet &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;animal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; animal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; mySet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enumerated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;animal&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// will not compile&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// for index in 0 ..&amp;lt; mySet.count {&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   print(&quot;&#92;(index) = &#92;(mySet[index])&quot;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Where Sets get really interesting&lt;/h2&gt;
&lt;p&gt;Remember in school when you learnt about Venn diagrams with pretty interlocking
circles? Sets can do the same things, although you will have to do your own
pretty drawings :-)&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;horse&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; intersect &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intersection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;set2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// {&quot;pig&quot;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; subtract &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subtracting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;set2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// {&quot;cat&quot;, &quot;dog&quot;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; union &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;set2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// {&quot;pig&quot;, &quot;cat&quot;, &quot;dog&quot;, &quot;cow&quot; &quot;horse&quot;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; xor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;symmetricDifference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;set2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// {&quot;cat&quot;, &quot;dog&quot;, &quot;cow&quot;, &quot;horse&quot;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code example above, we have two Sets of animals, with one animal in
common.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;intersection()&lt;/code&gt; lists the elements in common.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subtracting()&lt;/code&gt; lists the elements in one Set after removing all elements that
are in the other.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;union()&lt;/code&gt; joins all the elements without duplicates.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;symmetricDifference()&lt;/code&gt; lists the elements that are in one or other of the
Sets but not in both. (Swift 3 renamed this function from &lt;code&gt;exclusiveOr()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is my best attempt at a pretty drawing to show how these go together:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/sets_1.png&quot; alt=&quot;Sets&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The next fun trick is working out sub-sets, super-sets and disjoint sets.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;horse&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; smallSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

smallSet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isSubset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;
smallSet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isSubset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; set2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;smallSet&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; a subset of &lt;code&gt;set1&lt;/code&gt; because it contains an element that is
not in &lt;code&gt;set1&lt;/code&gt;. &lt;code&gt;smallSet&lt;/code&gt; &lt;strong&gt;is&lt;/strong&gt; a subset of &lt;code&gt;set2&lt;/code&gt; because every element in
&lt;code&gt;smallSet&lt;/code&gt; is also in &lt;code&gt;set2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/sets_2.png&quot; alt=&quot;Sets&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you want to get technical, a Set should not be considered a subset of an
identical Set. The default &lt;code&gt;isSubset(of:)&lt;/code&gt; allows this, but you can use
&lt;code&gt;isStrictSubset(of:)&lt;/code&gt; if you prefer.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isSubset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isStrictSubset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Superset works just the same but in reverse so the diagram above explains it
too:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;horse&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; smallSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isSuperset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; smallSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;
set2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isSuperset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; smallSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;

set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isSuperset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;           &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isStrictSuperset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;set1&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; a superset of &lt;code&gt;smallSet&lt;/code&gt; because it does not contain every
element in &lt;code&gt;smallSet&lt;/code&gt;. &lt;code&gt;set2&lt;/code&gt; &lt;strong&gt;is&lt;/strong&gt; a superset of &lt;code&gt;smallSet&lt;/code&gt; because every
element in &lt;code&gt;smallSet&lt;/code&gt; is also in &lt;code&gt;set2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;isSuperset(of:)&lt;/code&gt; and &lt;code&gt;isStrictSuperset(of:)&lt;/code&gt; functions allow or reject
identical sets.&lt;/p&gt;
&lt;p&gt;The final comparison tool that might be useful is &lt;code&gt;isDisjoint(with:)&lt;/code&gt; which
returns true only if the two Sets have no elements in common i.e. if there is no
overlap in the circles.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; set2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;horse&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; otherSet&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;duck&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;chicken&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isDisjoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; set2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;
set1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isDisjoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otherSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;quot;pig&amp;quot; occurs in both &lt;code&gt;set1&lt;/code&gt; and &lt;code&gt;set2&lt;/code&gt; so they are &lt;strong&gt;not&lt;/strong&gt; disjoint. &lt;code&gt;otherSet&lt;/code&gt;
and &lt;code&gt;set1&lt;/code&gt; have no matching entries so they &lt;strong&gt;are&lt;/strong&gt; disjoint.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;When should you use a Set?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;If you want the elements to be unique.&lt;/li&gt;
&lt;li&gt;If you want easy methods of comparing the contents of different collections.&lt;/li&gt;
&lt;li&gt;If you want to be able to remove elements easily.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;When should you not use a Set?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;If you need the collection to be able to hold multiples of an element.&lt;/li&gt;
&lt;li&gt;If the order of the collection is important.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;For more details on Sets, check out &lt;a href=&quot;http://swiftdoc.org/v2.2/type/Set/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftDoc.org&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>19th Hole Golf Scorer 3.1</title>
    <link href="https://troz.net/post/2016/19th-hole-golf-scorer-3.1/" />
    <updated>2016-05-07T00:00:00Z</updated>
    <id>https://troz.net/post/2016/19th-hole-golf-scorer-3.1/</id>
    <content type="html">&lt;p&gt;One of the problems with using 19th Hole on the Apple Watch is making the app
show every time you raise your wrist. My solution to this was to go into
Settings (either on my iPhone through the Watch app, or directly on the watch)
and change it to &amp;quot;Resume Last Activity&amp;quot; on wrist raise. This worked fine but as
I usually prefer the watch face to show on wrist raise, I had to keep setting
and re-setting this which was a pain.&lt;/p&gt;
&lt;p&gt;But last week I noticed the fine print when changing this setting. In this
screen shot from the Watch app on my iPhone, you can see that &amp;quot;session-based
apps&amp;quot; and &amp;quot;some third-party workout apps&amp;quot; will over-ride the &amp;quot;Show Watch Face&amp;quot;
setting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/WakeScreen.jpg&quot; alt=&quot;Wrist raise settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;I started to research this and found that if an app starts a &amp;quot;workout session&amp;quot;,
then it will become the active app until the session is ended or some other app
starts a session. So I got to work and added a workout session to the 19th
Hole&#39;s Apple Watch app.&lt;/p&gt;
&lt;p&gt;When you start scoring a round on the Apple Watch, you will see this dialog
asking if you would like to start a workout:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Watch-workout.png&quot; alt=&quot;Start workout dialog&quot;&gt;&lt;/p&gt;
&lt;p&gt;The workout will be stopped automatically when you have scored the last hole.
You can also use a force-press on the scoring screen to turn the workout off or
on at any time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Watch_end_workout.png&quot; alt=&quot;Stop workout&quot;&gt;&lt;/p&gt;
&lt;p&gt;This should make the app much more usable as an Apple Watch app, so happy
golfing :-)&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>My Year With Apple Watch</title>
    <link href="https://troz.net/post/2016/my-year-with-apple-watch/" />
    <updated>2016-04-28T00:00:00Z</updated>
    <id>https://troz.net/post/2016/my-year-with-apple-watch/</id>
    <content type="html">&lt;p&gt;As the Apple Watch hits its first birthday, there seems to be in increasing
number of bloggers complaining about it. One (which I refuse to link to) titled
&amp;quot;My Year of Hell With the Apple Watch&amp;quot;. I think the article has since been
re-published with a slightly less inflammatory title, but really! Did somebody
rivet the watch to his wrist? If it was so awful, why not just stop using it?&lt;/p&gt;
&lt;p&gt;I am a watch nerd and have always loved watches and having one on me. I liked
watches with features and even had a calculator watch at one point although as a
woman with skinny wrists, I found it constantly annoying that watch makers put
all the gadgets into man-sized watches so I was left with the choice of no
features or a watch that looked stupidly large on my wrist.&lt;/p&gt;
&lt;p&gt;A few years ago I stopped wearing a watch. I can&#39;t remember now why I did it -
maybe my watch strap broke and it wasn&#39;t important enough to get a new one. I
always had my iPhone to give me the time.&lt;/p&gt;
&lt;p&gt;But I ordered three Apple Watches as soon as they were available and myself,
husband and eldest son have been using them ever since.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Here are what I consider to be the best features of the Apple Watch:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Accuracy&lt;/strong&gt;: It is a fantastically accurate time-piece. Not enough people
give this credit, but we have always been used to clocks and watches that were
inherently inaccurate. Having a device on my wrist that I &lt;strong&gt;know&lt;/strong&gt; to be
perfectly accurate is an amazing thing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Notifications&lt;/strong&gt;: this takes a bit of work to get right and with every new
app, you need to assess whether to have its notifications appear on your
wrist. But the wonderful thing is knowing that you haven&#39;t missed anything and
being able to glance at your wrist when a notification arrives and tell -
unobtrusively - whether this is something that needs immediate attention. And
the ability to respond instantly to messages and emails with just a couple of
taps is amazingly useful.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Customisable watch faces&lt;/strong&gt;: depending on what I am doing, I need quick
access to different pieces of information. When travelling, I can set up a
watch face with multiple time zones. When working, I need a timer. When on
holiday, I prefer one of the more relaxing but less detailed watch faces.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Activity&lt;/strong&gt;: I am not the world&#39;s most active person so I find the three
rings to be a really good motivator. In fact I just had to stop typing and run
up &amp;amp; down the stairs to get another notch to the blue ring. The goals are not
out of my reach, so I like trying to fill in the rings every day.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Voice commands&lt;/strong&gt;: being able to send a text message completely by voice when
driving or when my hands are busy is fantastic. Starting a timer, doing unit
conversions and navigating are things I do frequently by voice.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Excellent battery life&lt;/strong&gt;: I started off having the battery life complication
always visible, but it very quickly became apparent that this was unnecessary.
Right now I have been wearing the watch for about 11 hours and the battery is
at 74%. And charging is so fast that if I did run it down, a 30 minute charge
would get me through most of a day.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Milanese Loop&lt;/strong&gt;: best watch band ever.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are other good features, but I think those are my favourites.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;So what don&#39;t I like?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Third-party apps are still a problem&lt;/strong&gt;. I know. I have published three
myself and sometimes they just doesn&#39;t happen. However this appears to vary a
lot. Some people say they can never get apps to run, even Apple&#39;s apps. Others
say they work but with a long delay. My watch seems to be better than the
average but it is still an issue.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sketches don&#39;t always get through or can take ages&lt;/strong&gt;. We thought it would be
a lot of fun to send each other little sketches and so it is, despite our
total lack of any artistic skills. But it loses a bit of impact when you have
to send a message 20 minutes later asking if the sketch has arrived.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The exercise tracking seems to be a bit erratic&lt;/strong&gt;. If my husband and I go
for a walk together, he comes back with 25 minutes of exercise and I only have
7 minutes. Am I not going fast enough? Are my arms not swinging enough? If it
is very hot and my wrist gets sweaty, I seem to get better numbers - is this
because my heart is beating faster or the capillaries closer to the surface
have expanded? Or is the watch picking up a better signal that way? Either
way, I still like the activity circles, but the green ring frustrates me a
bit.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;And what would I like to see in Watch 2 / watchOS 3?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Some sort of always-on display of the time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A speaker to allow Siri to talk back to me and to play audio.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FaceTime audio calls.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Better support for third-party apps - perhaps open up some private APIs?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allow developers to create watch faces or apps that told the time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allow developers to say that an app should stay as the frontmost app once
opened.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Will I buy Watch 2?&lt;/h2&gt;
&lt;p&gt;At this stage I would say no. We bought the bottom-of-the-line Sport models
because with version 1.0 of anything, you don&#39;t want to spend so much that
upgrading is ruled out. However these watches are still going great and a lot of
the improvements I am looking for will be in software not hardware.&lt;/p&gt;
&lt;p&gt;But 14 months ago, I was waiting for Apple to announce the iWatch as we all
thought it would be called, and saying that I didn&#39;t think I would get one. The
feature list convinced me and I have been delighted with it. So if Watch 2 comes
up with some killer feature that matters to me, then I might have to
re-consider...&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How To Confuse TestFlight</title>
    <link href="https://troz.net/post/2016/how-to-confuse-testflight/" />
    <updated>2016-04-24T00:00:00Z</updated>
    <id>https://troz.net/post/2016/how-to-confuse-testflight/</id>
    <content type="html">&lt;p&gt;I ran into an unusual problem when testing my latest app: &amp;quot;&lt;a href=&quot;https://troz.net/what-is-my-speed/&quot;&gt;What Is My
Speed?&lt;/a&gt;&amp;quot;. It is now available from the &lt;a href=&quot;https://itunes.apple.com/app/what-is-my-speed/id1091394524&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;iTunes App Store&lt;/a&gt;, but getting
there was a struggle.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2016/how-to-confuse-testflight/#tldr&quot;&gt;TL;DR&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Xcode is terrible at renaming projects, so it is a really good idea to have
worked out your app&#39;s name before starting. I would even recommend creating an
app ID in your Apple Developer account and registering the app name in iTunes
Connect. Only once you get to iTunes Connect will the name be checked for
uniqueness. Searching the App Stores is not enough as it does not cover
un-released apps or apps that are not available in your country.&lt;/p&gt;
&lt;p&gt;So I set up my app. I was hoping for &amp;quot;What&#39;s My Speed?&amp;quot; but was happy with &amp;quot;What
Is My Speed?&amp;quot;.&lt;/p&gt;
&lt;p&gt;Next step was to create the project in Xcode which I did using the app name and
manually entering the bundle identifier that I had registered at Apple
Developer. Xcode accepted the name without any warnings and created the default
targets using that name.&lt;/p&gt;
&lt;p&gt;In the default unit testing file, the module was defined like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@testable&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;What&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Is_My_Speed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which made me think that Xcode was quite happy to have parsed the app name as
required to replace any unwanted characters to suit.&lt;/p&gt;
&lt;p&gt;The app was built, passed all unit tests, profiled, analyzed, tested on real
devices as well as the simulators and uploaded to TestFlight.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BARRRPPPP!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/TestFlight_error.png&quot; alt=&quot;This app cannot be installed&quot;&gt;&lt;/p&gt;
&lt;p&gt;What?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(I forgot to take a screenshot but found a similar one online. Thanks &lt;a href=&quot;https://www.simpletechs.net/apps/easy-fix-for-testflight-problem-after-restoring-from-backup/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Simple
Techs&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I have had experience with TestFlight&#39;s oddities in the past, so I incremented
the build number and tried again, thinking that maybe there had been a
processing issue. Same result.&lt;/p&gt;
&lt;p&gt;This started a very frustrating 24 hours as I ran through all the possibilities
I could think of or find suggested on the net. Due to disastrous Australian
internet speeds, especially uploads, each attempt took from 40 - 120 minutes.
About 5 minutes to implement the next idea, 25 minutes upload time, then the
rest waiting for Apple to process the app so I could try again.&lt;/p&gt;
&lt;p&gt;Here is what I tried:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Uploaded a fresh build.&lt;/li&gt;
&lt;li&gt;Removed Bitcode.&lt;/li&gt;
&lt;li&gt;Fixed the code signing identity which was set to iOS Developer for some
reason.&lt;/li&gt;
&lt;li&gt;Manually specified the provisioning profiles for all three components (iPhone
app, WatchKit app, WatchKit Extension).&lt;/li&gt;
&lt;li&gt;Manually selected the code signing identity for the provisioning profiles.&lt;/li&gt;
&lt;li&gt;Uploaded using Application Loader instead of Xcode.&lt;/li&gt;
&lt;li&gt;Removed third-party framework.&lt;/li&gt;
&lt;li&gt;Removed WatchKit app.&lt;/li&gt;
&lt;li&gt;Revoked my distribution profile, re-created it and updated all provisioning
profiles.&lt;/li&gt;
&lt;li&gt;Created a completely blank project with same app name and bundle identifier.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As you can see from this list, I thought it was a code signing or profile error.
Using &lt;a href=&quot;https://itunes.apple.com/app/apple-configurator-2/id1037126344&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Apple Configurator 2&lt;/a&gt; (the modern version of iPhone Configuration
Utility), I was able to get the logs from my iPhone and entries like the
following seemed to confirm this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class=&quot;language-Error&quot;&gt;&lt;code class=&quot;language-Error&quot;&gt;installd.staging/temp.2LWJ2h/extracted/Payload/What Is My Speed?.app : 0xe8008017 (A signed resource has been added, modified, or deleted.)}```&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;But when I got to the stage of a completely blank app still failing, I suddenly
thought of the question mark. With the benefit of my preamble to this post, you
all probably got there long before I did, but I got there in the end.&lt;/p&gt;
&lt;p&gt;The solution was to remove the question mark from the names of the targets in
Xcode. I had to fix up the module names in my unit testing files, but apart from
that, there were no changes. After this I was able to upload the complete app to
TestFlight and install it on my iPhone.&lt;/p&gt;
&lt;p&gt;It is possible that it was the Product Names that were the issue, rather than
the target name as in Build Settings, Product Name is set to be &lt;code&gt;$(TARGET_NAME)&lt;/code&gt;
by default but I didn&#39;t test that. Maybe next time...&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;tldr&quot;&gt;TL;DR&lt;/a&gt; Do not put any unusual characters in your target names.
Alphanumerics and spaces are OK, but I am not sure about anything else.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Road Trip Apps</title>
    <link href="https://troz.net/post/2016/road-trip-apps/" />
    <updated>2016-04-07T00:00:00Z</updated>
    <id>https://troz.net/post/2016/road-trip-apps/</id>
    <content type="html">&lt;p&gt;Over the past few months, we have done two long road trips: one approximately
4,000 km and the other about 1,800 km. These gave us the chance to try out
various apps for navigation and travel information. We live in Queensland,
Australia and some of these comments may be country-specific.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Navigation&lt;/h2&gt;
&lt;p&gt;The basic navigation came down to three apps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.apple.com/ios/maps/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Apple Maps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itunes.apple.com/app/google-maps-real-time-navigation/id585027354&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Google Maps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itunes.apple.com/au/app/metroview-gps-navigation/id370753892&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Metroview&lt;/a&gt; (Australia and New Zealand only)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each has their strengths and weaknesses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Apple Maps is by far the most beautiful and easy to read. It integrates
perfectly with Contacts (as you would expect). Turn-by-turn directions were
mostly excellent. The biggest downside is that it does not allow you to
specify a route avoiding tolls.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Google Maps allows you to plot a route avoiding tolls... if you can find the
setting. Its traffic reporting is by far the best and it would offer suggested
changes of routes during the trip to get around traffic. The interface is
confusing at times. Turn-by-turn directions were good, although I would have
preferred an Australian voice option.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Metroview has all the maps downloaded which saves on data connection costs and
handles bad connectivity areas better. Its best feature is notification of
speed limits and alerts if you go over the limit, although it gets confused
when on over or under-passes. Another very useful feature is the display of
lanes when approaching highway exits, so you could tell which lane to be in.
Apparently the voice guidance is now more natural but we didn&#39;t try this. The
display is messy and cluttered compared to the others.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We ended up with the following sequence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Apple Maps for the bulk of the trip.&lt;/li&gt;
&lt;li&gt;Google Maps &amp;amp; Metroview (on 2 separate iPhones) in the metropolitan areas and
when about to take a highway exit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Even then it was amusing to see the apps panic when you took a new road and the
apps assumed you had left the road and were heading through the bush! And at one
point there was a large and official sign saying &amp;quot;Ignore GPS - do not take this
turn&amp;quot; and sure enough, both Apple Maps and Google Maps wanted us to turn down a
little country lane instead of keeping to the highway.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Speed Display:&lt;/h2&gt;
&lt;p&gt;Apart from the basic navigation apps, we tried a couple of speed display apps.
There are a lot of HUD speed apps out there that project the data on the the
windscreen but they had two problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They only worked when it was dark. As soon as the sun got up, they became
invisible.&lt;/li&gt;
&lt;li&gt;They required you to place your iPhone flat on the dashboard so even if they
had worked during the day, the iPhone would have got far too hot and
shut-down. (We have a mount that holds our iPhones in front of the air vents
to avoid this over-heating problem. Any mount that sticks to the windscreen
itself leaves the iPhone too exposed to the sun.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One we stuck with (in non-HUD mode) was &lt;a href=&quot;https://itunes.apple.com/app/speedometer-+-hud-digital/id375318117&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Speedometer + HUD&lt;/a&gt;. This had a clear
display with a few flaws:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When moving from 99 kph to 100 kph, the display was not re-arranged for 3
digits. Moving from 101 down to 100 fixed it, but as 100 kph is the standard
speed limit, this was a frequent annoyance.&lt;/li&gt;
&lt;li&gt;Adjusting the speed limit was too fiddly and difficult even for the passenger.
It would have been impossible for the driver.&lt;/li&gt;
&lt;li&gt;It only works in landscape mode which does not always suit the car mounts - we
ended up perching the 2nd iPhone on the unused ashtray.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also tried &lt;a href=&quot;https://itunes.apple.com/app/talking-hud-voice-prompt-speedometer/id873194479&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Talking HUD&lt;/a&gt; but didn&#39;t like it. It worked solely in HUD mode,
so was only useful at night or very early morning. It had voice prompts to tell
you when to change gear but in an automatic car, they were just a distraction
and it was not obvious how to turn them off.&lt;/p&gt;
&lt;p&gt;But overall, we found having a speed display app as well as a navigation app to
be the best combination. The audible alerts when you went over speed were very
useful when over-taking or getting up to speed as you could get the information
without having to take your eyes off the road.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Future App Plans:&lt;/h2&gt;
&lt;p&gt;Having determined that a speed display app is really useful, but not finding one
that suited us perfectly, the obvious next step was to design my own. I wanted
it to have the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clean display, visible in all light conditions&lt;/li&gt;
&lt;li&gt;easily settable speed limit&lt;/li&gt;
&lt;li&gt;audible and visual over-speed warnings&lt;/li&gt;
&lt;li&gt;no HUD mode&lt;/li&gt;
&lt;li&gt;Apple Watch component for when you only had a single iPhone and it was
navigating but you still wanted a speed display&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So watch this space for further news of my up-coming app...&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Update&lt;/h2&gt;
&lt;p&gt;&amp;quot;&lt;a href=&quot;https://troz.net/what-is-my-speed/&quot;&gt;What Is My Speed?&lt;/a&gt;&amp;quot; is available now from the &lt;a href=&quot;https://itunes.apple.com/app/what-is-my-speed/id1091394524&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;iTunes App Store&lt;/a&gt; for
iPhone and Apple Watch.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Structs vs Classes</title>
    <link href="https://troz.net/post/2016/structs-vs-classes/" />
    <updated>2016-03-30T00:00:00Z</updated>
    <id>https://troz.net/post/2016/structs-vs-classes/</id>
    <content type="html">&lt;p&gt;One of the big debates among Swift developers is when to use &lt;code&gt;structs&lt;/code&gt; and when
to use &lt;code&gt;classes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Classes are the building blocks of object-oriented programming
but structs as provided by Swift are newly powerful. Structs have been around in
C-based languages for a long time, but Swift has made them more powerful and
given them more features so that they are almost indistinguishable from classes.
So what are the differences and which one should you use?&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Where they are the same?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;both can define initializers&lt;/li&gt;
&lt;li&gt;both can define properties&lt;/li&gt;
&lt;li&gt;both can define methods&lt;/li&gt;
&lt;li&gt;both can conform to protocols&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Where they are different?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;classes can inherit from other classes&lt;/li&gt;
&lt;li&gt;structs cannot inherit from other structs&lt;/li&gt;
&lt;li&gt;classes are reference types&lt;/li&gt;
&lt;li&gt;structs are value types&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The reference type vs value type difference is where things really get
interesting. Have a look at this example of a class with a single property:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; personA &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
personA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;// Woody&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; personB &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; personA
personB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Buzz&quot;&lt;/span&gt;&lt;/span&gt;
personB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;// Buzz&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That looks like standard stuff, but what do you think &lt;code&gt;personA&lt;/code&gt;&#39;s name is now?
If you guessed &amp;quot;Buzz&amp;quot; then you win a prize! &lt;em&gt;(No, not a real prize - pat
yourself on the back.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is because when we created the &lt;code&gt;personB&lt;/code&gt; variable and assigned &lt;code&gt;personA&lt;/code&gt; to
it, we did not assign the &lt;strong&gt;VALUE&lt;/strong&gt; of &lt;code&gt;personA&lt;/code&gt;, we assigned a &lt;strong&gt;REFERENCE&lt;/strong&gt; to
&lt;code&gt;personA&lt;/code&gt; - actually the address in memory of &lt;code&gt;personA&lt;/code&gt; rather than the data
inside.&lt;/p&gt;
&lt;p&gt;So now we have two objects and they are both looking at the same spot in memory
for their data. This means that changing the name of &lt;code&gt;personB&lt;/code&gt; changed the name
of &lt;code&gt;personA&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;Let&#39;s try the same thing with a struct:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; personC &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
personC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;// Rex&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; personD &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; personC
personD&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hamm&quot;&lt;/span&gt;&lt;/span&gt;
personD&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;// Hamm&lt;/span&gt;

personC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;// Rex&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This time, because we are using a struct, when we assign &lt;code&gt;personC&lt;/code&gt; to the new
&lt;code&gt;personD&lt;/code&gt; variable, we are actually making a copy of &lt;code&gt;personC&lt;/code&gt; and setting the
values of &lt;code&gt;personD&lt;/code&gt; to this new copy. So now we can change &lt;code&gt;personD&lt;/code&gt; without
messing with &lt;code&gt;personC&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note that I did not have a define an &lt;code&gt;init&lt;/code&gt; for the struct because it creates
one automatically. You can still add one yourself if you want to do anything
different, but you do not have to.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;At first glance, you may think that you should now use structs all the time to
avoid these unintended consequences, but it isn&#39;t quite as simple as that.
Sometimes a class is still the best thing to use.&lt;/p&gt;
&lt;p&gt;The inheritance capabilities of classes can make your decision simple: if you
need to create a button and want to start by sub-classing UIButton or NSButton,
then your button must be a class, not a struct. This will apply to most user
interface objects.&lt;/p&gt;
&lt;p&gt;Apple really wants us to use structs and in the Swift standard libraries, a very
high percentage of the objects are structs. But structs are especially well
suited to a certain subset of objects.&lt;/p&gt;
&lt;p&gt;The best explanation that I have found of when to use a struct is the &lt;a href=&quot;http://faq.sealedabstract.com/structs_or_classes/#an-alternative-hypothesis&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jeff Trick&lt;/a&gt;. Reduced down, the rule is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you can overload == to compare two instances of your object, use a struct.
&lt;br&gt; If this doesn&#39;t make sense, use a class.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So use structs for your things: Person, Shape, Brick, Cat. &lt;br&gt; Use classes for
everything else.&lt;/p&gt;
&lt;p&gt;I would add one caveat: don&#39;t fight the compiler. If using a struct is giving
lots of errors and warnings, change to a class.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;A logical consequence of this is that all structs should conform to the
&lt;a href=&quot;http://swiftdoc.org/v2.2/protocol/Equatable/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Equatable&lt;/a&gt; protocol.&lt;/p&gt;
&lt;p&gt;Extending &lt;code&gt;PersonStruct&lt;/code&gt; to make it conform just requires a single function:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Equatable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lhs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rhs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; lhs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; rhs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this struct only has one property, we can say that two instances of this
struct are equal if the names are equal.&lt;/p&gt;
&lt;p&gt;Testing this, we can see:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; personC &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; personD &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; personC
personD&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hamm&quot;&lt;/span&gt;&lt;/span&gt;

personC &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; personD &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; personE &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
personC &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; personE &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
personC &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; personE &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conveniently, providing an &lt;code&gt;==&lt;/code&gt; function effectively gives us a &lt;code&gt;!=&lt;/code&gt; function
for free as you can see from the last example.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;There is one final point I would like to make about struct and that concerns
mutating functions. Look at what happens if we include a function that changes
the &lt;code&gt;name&lt;/code&gt; property in the struct:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/mutating.png&quot; alt=&quot;Mutating error&quot;&gt;&lt;/p&gt;
&lt;p&gt;Fix-it is very helpfully pointing out that the method needs to be marked as
&lt;code&gt;mutating&lt;/code&gt; for this to work and is showing where this should go. Accepting the
suggestion will get rid of the error and then the name can be changed using this
method.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Equatable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;mutating&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;changeName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to newName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;newName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newName
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; personC &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonStruct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
personC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;// Woody&lt;/span&gt;

personC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changeName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
personC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;// Sid&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is no problem about using &lt;code&gt;mutating&lt;/code&gt; and it will not have the unintended
consequences of using classes. Despite the scary name, a mutating function
actually returns a new copy of the struct.&lt;/p&gt;
&lt;p&gt;The problem arises if you have many nested structs and the mutating has to
spread through the list. So don&#39;t nest your structs - at least not more than two
deep!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Learning Swift - Optionals</title>
    <link href="https://troz.net/post/2016/learning-swift-optionals/" />
    <updated>2016-03-12T04:24:00Z</updated>
    <id>https://troz.net/post/2016/learning-swift-optionals/</id>
    <content type="html">
&lt;p&gt;Today I plan to discuss optionals since they were a feature of Swift that I
found difficult to grasp at first.&lt;/p&gt;
&lt;h2&gt;What is an optional in Swift?&lt;/h2&gt;
&lt;p&gt;An optional is a variable of a specified type that can also be nil.&lt;/p&gt;
&lt;h2&gt;Why does this matter?&lt;/h2&gt;
&lt;p&gt;In Objective-C, any object type could be nil. If you declared a variable like
this:&lt;/p&gt;
&lt;pre class=&quot;language-objectivec&quot;&gt;&lt;code class=&quot;language-objectivec&quot;&gt;NSString &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;myString&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then &lt;code&gt;myString&lt;/code&gt; was set to nil by default.&lt;/p&gt;
&lt;p&gt;But this could cause issues, especially as Objective-C does not complain if you
send a message to nil. This could lead to bugs that were very difficult to track
down.&lt;/p&gt;
&lt;p&gt;The other big use for nil is when returning from a function which has found no
appropriate data to return. The classic example is when looking for the index of
an element in an array. What should be returned if the element is not found in
the array?&lt;/p&gt;
&lt;p&gt;Some languages return -1, Objective-C uses &lt;code&gt;NSNotFound&lt;/code&gt;, but you have to know
what each language is going to do. The more logical answer is nil. However if
your function is expected to return an integer, then it cannot return nil
because nil is not an integer.&lt;/p&gt;
&lt;p&gt;This is where optionals come in: if the function is expected to return an
optional integer, it can return with an integer with the index of the matching
element, or it can return nil if the element was not found. This is much clearer
and less prone to error.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;How does Swift handle optionals?&lt;/h2&gt;
&lt;p&gt;One of the first things that struck me about Swift was how clean the code
looked, without so many non-alphanumeric characters scattered around. Gone were
all the:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But instead, Swift code sprouted:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What were these?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The key to understanding optionals is to realise that when you declare an
optional variable of a certain type, you are actually declaring a box that can
hold a variable of that type or can hold nil.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once you grasp that concept, it all becomes much more obvious.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; optionalInteger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ? indicates that this is an optional variable. It does not have to be
initialised as it is already set to nil which is valid for an optional variable.
Without the ? this would require initialisation as it would not be valid for it
to be nil.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Setting an optional&lt;/h2&gt;
&lt;p&gt;Setting the value of an optional variable is just the same as any other
variable:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;optionalInteger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
optionalInteger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;Getting an optional&lt;/h2&gt;
&lt;p&gt;The difference arises when you need to get the data out of the optional variable
in order to use it. This process is called un-wrapping and it means to get the
variable value out of the &#39;box&#39; it is stored it.&lt;/p&gt;
&lt;h3&gt;The most obvious way is to use !&lt;/h3&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newInteger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; optionalInteger&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;DO NOT DO THIS!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is called forced-unwrapping and assumes that the optional variable is not
nil. If the optional is nil, this will crash. In Xcode, when you connect
interface elements from your storyboard to a Swift file, Xcode will use ! like
this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@IBOutlet&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; startButton&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIButton&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have to assume Xcode knows what it is doing and the button will be available
when needed, but you should not use ! - it is un-safe. By using it, you are
vowing to the compiler that when it gets to that point, the optional value will
not be nil. There are much better and safer ways of doing that.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Use &#39;if let&#39;&lt;/h3&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doubleNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; optionalInteger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; integerValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; optionalInteger &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// integerValue is not an optional&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// and is guaranteed to contain an Int&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; integerValue &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// no integer found in the optional,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// so return nil to indicate failure&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;Use guard&lt;/h3&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doubleNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; optionalInteger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; integerValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; optionalInteger &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// get out quickly,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// returning nil to indicate failure&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// integerValue is not an optional&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// and is guaranteed to contain an Int&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; integerValue &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These two alternatives (&lt;code&gt;if let&lt;/code&gt; &amp;amp; &lt;code&gt;guard&lt;/code&gt;) do the same job but in opposite
ways. In both cases, they perform a conditional un-wrapping that may or may not
give a valid result. &lt;code&gt;if let&lt;/code&gt; checks if it is OK to proceed. &lt;code&gt;guard&lt;/code&gt; checks to
see if it is NOT OK to proceed. Which you use is really a matter of personal
preference and working out what is more logical in each case.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;guard&lt;/code&gt; statement is really good for checking data early in a process and
making a quick exit it something is wrong. The &lt;code&gt;if let&lt;/code&gt; construct encloses your
success code inside a block and can sometimes leave the failure code a long way
from the check which can make it less obvious. The other potential issue with
&lt;code&gt;if let&lt;/code&gt; is the &amp;quot;pyramid of doom&amp;quot; common in early Swift code as demonstrated in
this rather contrived example:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isValidAddressBookEntry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                             lastName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                             emailAddress&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                             phoneNumber&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validFirstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validLastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; emailAddress &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validPhone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; phoneNumber &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thankfully, Swift now allows us to chain both &lt;code&gt;if let&lt;/code&gt; and &lt;code&gt;guard&lt;/code&gt; statements.
Here is the previous example re-factored to use &lt;code&gt;if let&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isValidAddressBookEntrySwift2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   lastName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   emailAddress&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   phoneNumber&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validFirstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validLastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; emailAddress&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validPhone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; phoneNumber &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the same function but using &lt;code&gt;guard&lt;/code&gt; which allows the inputs to be checked immediately and the function exited if the u=inputs are not valid. For a short function like this, the change is not really significant, but if the function does a lot of processing of the input data, checking first and getting out as soon as possible is more efficient.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isValidAddressBookEntryUsingGuard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                       lastName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                       emailAddress&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                       phoneNumber&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validFirstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validLastName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; emailAddress&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; validPhone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; phoneNumber &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;Use optional chaining&lt;/h3&gt;
&lt;p&gt;The final way to deal with optionals safely is to use optional chaining:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SocialMediaAccounts&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; facebook&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; twitter&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; lastName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; handle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; socialMedia&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SocialMediaAccounts&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
socialMedia &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SocialMediaAccounts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; twitterAccount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

socialMedia&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;twitter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; twitterAccount
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; twitterHandle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socialMedia&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;twitter&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handle&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we have defined a &lt;code&gt;SocialMediaAccounts&lt;/code&gt; struct that holds
optional &lt;code&gt;Person&lt;/code&gt; structs for the various social media outlets. The
&lt;code&gt;socialMedia&lt;/code&gt; variable is defined as an optional and then created. A
&lt;code&gt;twitterAccount&lt;/code&gt; variable of type &lt;code&gt;Person&lt;/code&gt; is also created but contains no data
at the moment.&lt;/p&gt;
&lt;p&gt;When assigning the &lt;code&gt;twitterAccount&lt;/code&gt; to the &lt;code&gt;socialMedia.twitter&lt;/code&gt; property, a ?
is inserted which checks to see that &lt;code&gt;socialMedia&lt;/code&gt; is not nil. If it is nil,
then execution of that line stops at the ? and nothing bad will happen.&lt;/p&gt;
&lt;p&gt;In the same way, when trying to read back the twitter handle, we chained
together 2 optionals with ?&#39;s. If either &lt;code&gt;socialMedia&lt;/code&gt; or &lt;code&gt;socialMedia.twitter&lt;/code&gt;
is nil, that line will not complete. Again this is perfectly safe and the app
will not crash.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;All the examples in this article are available in a &lt;a href=&quot;https://github.com/trozware/optionals&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift playground&lt;/a&gt; which
has been updated to Swift 4 syntax.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Learning Swift - For-Loops</title>
    <link href="https://troz.net/post/2016/learning-swift-for-loops/" />
    <updated>2016-03-12T00:00:00Z</updated>
    <id>https://troz.net/post/2016/learning-swift-for-loops/</id>
    <content type="html">&lt;p&gt;Loops are a fundamental building block of any program. Doing repetitive tasks
fast and accurately is what computers are really good at and what we humans get
very bored doing. Swift offers several different ways to perform loops, but
today we are going to concentrate on for-loops.&lt;/p&gt;
&lt;p&gt;The most basic form of loop is the &lt;code&gt;for-in&lt;/code&gt; loop. There are two ways this can be
used: looping over the numbers in a range or looping over the elements in an
array or dictionary.&lt;/p&gt;
&lt;p&gt;Firstly, the range:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 0 1 2 3 4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;I am using a custom print function that allows me to print the results on a
single line for convenience.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This &lt;code&gt;for-in&lt;/code&gt; loop uses a half-open range and it is effectively saying:&lt;/p&gt;
&lt;p&gt;start with x at 0
print x with a space
add 1 to x
is x still less than 5?
yes - go back to the print stage
no - stop&lt;/p&gt;
&lt;p&gt;If we wanted to go all the way to 5 instead of stopping before 5, we would use a
closed range by replacing &lt;code&gt;..&amp;lt;&lt;/code&gt; with &lt;code&gt;...&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 0 1 2 3 4 5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;These methods work fine if we want to loop through a range of numbers one at a
time but what if we want a different increment? There are several ways to vary
the step.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stride(from:to:by:)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stride(from:through:by:)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;where&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stride&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; to&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; by&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 0 2 4 6 8&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stride&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; through&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; by&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 0 2 4 6 8 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The 2 variations of &lt;code&gt;stride&lt;/code&gt; are analogous to the 2 ways of expressing the range
in the the &lt;code&gt;for-in&lt;/code&gt; loop.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;stride(from:to:by:)&lt;/code&gt; starts with the number in the &lt;code&gt;from&lt;/code&gt; parameter and each
time through the loop, increments that number by the amount of the &lt;code&gt;by&lt;/code&gt;
parameter. If the incremented number is less than the &lt;code&gt;to&lt;/code&gt; parameter, the loop
continues.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;stride(from:through:by:)&lt;/code&gt; works the same way but continues until the
incremented number is greater than or equal to the &lt;code&gt;through&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;Using a negative value for &lt;code&gt;by&lt;/code&gt; allows counting backwards:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stride&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; through&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; by&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 10 8 6 4 2 0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stride can also be used for floating point loops although the result may contain
some unexpected changes in precision e.g 0.7000000000000001 instead of the
expected 0.7&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; startingNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stride&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; startingNumber&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; through&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; by&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 0.1 0.3 0.5 0.7000000000000001 0.9&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the result does not contain the &lt;code&gt;through&lt;/code&gt; parameter as the next
iteration would have produced 1.1 which is greater than it. And also
&lt;code&gt;startingNumber&lt;/code&gt; did not have to be a variable as it is never actually changed.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;where&lt;/code&gt; makes it possible to step through a loop while conforming to some
conditional:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 0 3 6 9&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;The other main use for &lt;code&gt;for-in&lt;/code&gt; loops is for iterating through the elements of
an array or dictionary:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arrayOfInts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; arrayOfInts &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: 2 4 6 8 10 12&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; myDict &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;three&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; myDict &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 2 = two&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 3 = three&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 1 = one&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a dictionary is un-sorted, the order of the items may vary.&lt;/p&gt;
&lt;p&gt;If you want to loop through an array while also monitoring the index of each
element, you can use &lt;code&gt;enumerated()&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arrayOfNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Andy&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Buzz&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; arrayOfNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enumerated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;The name at index &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; is &lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;&#92;(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// The name at index 0 is Andy&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// The name at index 1 is Buzz&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// The name at index 2 is Woody&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your array contains optionals, you can use &lt;code&gt;for case let&lt;/code&gt; to test each value:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arrayWithOptionals&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Buzz&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; arrayWithOptionals &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: Woody Buzz Rex&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; variable is un-wrapped each time through the loop so can be used
safely.&lt;/p&gt;
&lt;p&gt;Any of these loop styles can be combined with &lt;code&gt;where&lt;/code&gt; to perform checks on each
iteration:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arrayWithOptionals&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Buzz&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rex&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; arrayWithOptionals &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;printWithSpace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints: Buzz Rex&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;Some of you may be wondering what happened to the old C-style loops like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; x&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; terminator&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does not work in Swift 3+: the &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; operators and this style of
loop declaration have been removed from the language. For further details, check
out the relevant proposals at Swift Evolution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0007-remove-c-style-for-loops.md&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Remove C-style for-loops with conditions and incrementers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0004-remove-pre-post-inc-decrement.md&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Remove the ++ and -- operators&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;All the examples in this article are available in a &lt;a href=&quot;https://github.com/trozware/for-loops&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift playground&lt;/a&gt; now
updated to Swift 4 syntax.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Learning Swift - Generics</title>
    <link href="https://troz.net/post/2016/learning-swift-generics/" />
    <updated>2016-02-29T00:00:00Z</updated>
    <id>https://troz.net/post/2016/learning-swift-generics/</id>
    <content type="html">&lt;p&gt;One of the nice things about Swift is how clean your code looks. A lot of the
weird characters that pepper the code of other languages has been eliminated: No
more semi-colons, asterisks etc.&lt;/p&gt;
&lt;p&gt;But then you are reading somebody else&#39;s code and you find these angle brackets
all over the place and they don&#39;t seem to make sense.&lt;/p&gt;
&lt;p&gt;What does this mean?&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;mid&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like it is a function to find the middle element in an array, but what
is &lt;code&gt;&amp;lt;T: Comparable&amp;gt;&lt;/code&gt; or &lt;code&gt;[T]&lt;/code&gt; or even just &lt;code&gt;T&lt;/code&gt;? They are describing Generic data
types.&lt;/p&gt;
&lt;p&gt;Using the dictionary, I find the following definition for &lt;code&gt;generic&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;characteristic of or relating to a class or group of things; not specific.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And that really provides quite a good definition for Swift&#39;s generics too. They
allow you to assign a non-specific data type to a function so that function can
be used for many different data types.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;But enough of the theory: by far the best way to understand generics is to
encounter the problem they are meant to solve and then to solve it.&lt;/p&gt;
&lt;p&gt;The other day I was processing data to feed to a charting library. I ended up
with an array of CGFloats, but there were too many of them, so I wrote a
function to give me the last x elements of an array of CGFloats:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberOfElementsToInclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; numberOfElementsToInclude &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; array &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; startingElement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; numberOfElementsToInclude
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; endOfArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;startingElement &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; endOfArray
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; largeArray&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; smallArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;endOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; largeArray&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberOfElementsToInclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//smallArray now equals [8.0, 9.0, 10.0]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OK, so &lt;code&gt;largeArray&lt;/code&gt; isn&#39;t actually the largest array you have ever seen, but it
works for demo purposes. In my code, I had over 20,000 entries in the large
array and only wanted the last 5,000.&lt;/p&gt;
&lt;p&gt;I was completely happy with that until I found another data source where the
data was an array of Ints. Still perfectly chartable using my charting library,
but I could not get the end of the array using my nice function. First thought
was to replicate the function:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberOfElementsToInclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; numberOfElementsToInclude &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; array &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; startingElement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; numberOfElementsToInclude
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; endOfArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;startingElement &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; endOfArray
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; largeArrayInt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// type inferred to be Int&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; smallArrayInt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;endOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; largeArrayInt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberOfElementsToInclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// smallArrayInt now equals [4, 5, 6, 7, 8, 9, 10]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A quick glance will reveal that the declaration line was the only thing that
changed. I didn&#39;t have to give the function a new name because the compiler
worked out which of the two functions to use based on the types supplied. So all
I was doing was replacing &lt;code&gt;CGFloat&lt;/code&gt; with &lt;code&gt;Int&lt;/code&gt; in two places (the
&lt;code&gt;numberOfElementsToInclude&lt;/code&gt; parameter remains an &lt;code&gt;Int&lt;/code&gt; in both cases).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;As soon as you find yourself replicating code and only changing the types of
the data, that is a sign that you need to use generics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For my first attempt at creating a generic function, all I did was replace the
word &lt;code&gt;CGFloat&lt;/code&gt; with &lt;code&gt;T&lt;/code&gt; where &lt;code&gt;T&lt;/code&gt; indicates some type that will be specified
when the function is called. The convention is to use single letters for generic
types: &lt;code&gt;T&lt;/code&gt; for Type, &lt;code&gt;A&lt;/code&gt; for AnyObject, &lt;code&gt;Z&lt;/code&gt; because you feel like it. The choice
is up to you.&lt;/p&gt;
&lt;p&gt;That produced this error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Generics_error.png&quot; alt=&quot;Generics error&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Use of undeclared type &#39;T&#39;&lt;/code&gt;. Well that seems fair enough... how is the compiler
supposed to know what a &lt;code&gt;T&lt;/code&gt; is?&lt;/p&gt;
&lt;p&gt;It turns out that this is where the angle brackets come into play. You have to
warn the compiler that this function is going to get a generic type and that the
&lt;code&gt;T&lt;/code&gt; is only a placeholder, not a real type. So this version compiles and works -
notice the &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt; between the function name and the opening bracket:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endOf&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberOfElementsToInclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; numberOfElementsToInclude &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; array &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; startingElement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; numberOfElementsToInclude
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; endOfArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;startingElement &lt;span class=&quot;token operator&quot;&gt;..&amp;lt;&lt;/span&gt; array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; endOfArray
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I have no code duplication and if I ever get another data set that uses a
different object type, my function will still work.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Now we know to use generics instead of replicating code that operates in the
same way on different data types.&lt;/p&gt;
&lt;p&gt;And we know how to construct a generic function.&lt;/p&gt;
&lt;p&gt;Using it is identical to using the two functions I had before:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; largeArray&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; smallArray &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;endOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; largeArray&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberOfElementsToInclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; largeArrayInt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// type inferred to be Int&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; smallArrayInt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;endOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; largeArrayInt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; numberOfElementsToInclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You do not have to specify what &lt;code&gt;T&lt;/code&gt; is when calling the function, that will be
inferred from the supplied data.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Moving on into more advanced use cases, we have two things to consider:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Functions where the generic parameters must conform to a protocol.&lt;/li&gt;
&lt;li&gt;Functions that take generic parameters of different types.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The example function I started with (not mine but I forget where I got it... If
it is yours, thanks you very much) used:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;mid&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of specifying the type as completely generic, it specified that it must
conform to the Comparable protocol. This is because the function body used a
&lt;code&gt;sort()&lt;/code&gt; function so the generic function needed to be sure that the elements in
the array could be compared. This technique can also be used to restrict the
parameter types to sub-classes of a certain class.&lt;/p&gt;
&lt;p&gt;For a function with more than one generic data type, you need to use more than
one placeholder letter:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;genericWithTwoDifferentTypes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; param2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in exactly the same way, both &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;U&lt;/code&gt; can be set to conform to a
protocol or be members of a certain super-class.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Hopefully this has taken some of the mystery out of generics, but if you have
any questions, please get in touch using the &lt;a href=&quot;https://troz.net/contact/&quot;&gt;Contact page&lt;/a&gt;, comment below or
contact me via Twitter: &lt;a href=&quot;https://twitter.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@trozware&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Singleton to Protocol</title>
    <link href="https://troz.net/post/2016/singleton-to-protocol/" />
    <updated>2016-02-26T00:00:00Z</updated>
    <id>https://troz.net/post/2016/singleton-to-protocol/</id>
    <content type="html">&lt;p&gt;I was driving through the town of &lt;a href=&quot;https://en.wikipedia.org/wiki/Singleton,_New_South_Wales&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Singleton&lt;/a&gt; the other day and of course, it
got me thinking about using singletons in my apps. Singletons were a commonly
used pattern in Objective-C programming and appear in many of Apple&#39;s own APIs,
but seem to be increasingly frowned upon in the Swift world.&lt;/p&gt;
&lt;h2&gt;So what is a singleton?&lt;/h2&gt;
&lt;p&gt;A singleton is a class that only expects to have a single instance. Think of it
as a global instance of a class. In some cases this makes perfect sense if there
can only ever be one instance of a particular class or if there is a default
variant that suits most cases e.g.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared
&lt;span class=&quot;token class-name&quot;&gt;UserDefaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;standard
&lt;span class=&quot;token class-name&quot;&gt;NotificationCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;FileManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;URLSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are using an object with a property name of &amp;quot;shared&amp;quot;, &amp;quot;standard&amp;quot; or
&amp;quot;default&amp;quot; you can be pretty sure it is an singleton.&lt;/p&gt;
&lt;h2&gt;And what&#39;s the problem with singletons?&lt;/h2&gt;
&lt;p&gt;There are probably many different opinions here but I have two thoughts about
this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They are effectively global variables and global variables can make your code
messy and un-predictable.&lt;/li&gt;
&lt;li&gt;If they can be accessed by multiple other objects, possibly at the same time,
then you can get conflicts. These can be handled by clever use of background
queues, but it isn&#39;t easy.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;What to use instead?&lt;/h2&gt;
&lt;p&gt;As I drove, I mused on a singleton that I had implemented recently. It was a
logging utility that allowed any object in my app (mostly view controllers) to
save a new entry to a log file. The basic structure of the Logger class was
this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sharedLogger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;addToLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// use private functions to find log file URL&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// append event text with time stamp&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any object in my app could use the Logger like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sharedLogger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addToLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; newLogEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I got to think about how I was using this, I realised that instead of a
Logger &lt;em&gt;object&lt;/em&gt; that everything could use, what I really needed was a Loggable
&lt;em&gt;behaviour&lt;/em&gt; that I could apply &amp;amp; restrict to the few classes that actually
needed to log events. For me, this was the break-through:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Create a behaviour, not an object.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As soon as I started thinking about this as a behaviour, a protocol became the
obvious solution, so this is what I created:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Loggable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// not declaring functions here&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// as they should NOT be over-written&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Loggable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;addToLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// use private functions to find log file URL&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// append event text with time stamp&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We run immediately into one of the peculiarities of Swift protocol extensions
which has been very well explained by &lt;a href=&quot;https://cjwirth.com/tech/swift-protocol-extension-weirdness&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Caesar Wirth&lt;/a&gt;. If I had declared
&lt;code&gt;addToLog(_:)&lt;/code&gt; in the protocol, then any class or struct conforming to this
protocol would have been free to over-write this function and provide its own
version. This is not what I wanted here - I wanted every object to use the same
version. So I left the function declaration out of the protocol definition and
only included it in the protocol extension.&lt;/p&gt;
&lt;p&gt;To use this behaviour, a class or struct just has to be declared as conforming
to the Loggable protocol:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Loggable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;addToLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;I did something!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For my app, I knew that I would want all my NSViewControllers to be able to add
log events, so instead of setting them all individually as conforming to the
protocol, I used this shortcut which extends all NSViewControllers to conform to
the protocol.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSViewController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Loggable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added this line to the Loggable.swift file where I declared the protocol and
its extension, but outside both of them.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Protocol-oriented programming is a new technique to me, so it really helps when
I can find a practical example of where it solves a problem.&lt;/p&gt;
&lt;p&gt;If you are new to POP, I highly recommend the &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2015/408/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Crusty talk&lt;/a&gt; from WWDC 2015.
And this article by &lt;a href=&quot;http://matthijshollemans.com/2015/07/22/mixins-and-traits-in-swift-2/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Matthijs Hollemans&lt;/a&gt; was invaluable to me in
demonstrating the problems with object inheritance that are solved by protocols.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Three Rules for Writing Better Swift</title>
    <link href="https://troz.net/post/2016/three-rules-for-writing-better-swift/" />
    <updated>2016-02-24T00:00:00Z</updated>
    <id>https://troz.net/post/2016/three-rules-for-writing-better-swift/</id>
    <content type="html">&lt;ol&gt;
&lt;li&gt;Do not use !.&lt;/li&gt;
&lt;li&gt;Use let, not var.&lt;/li&gt;
&lt;li&gt;Allow the compiler to infer types.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Read on for more details...&lt;/p&gt;
  
&lt;h2&gt;1. Do not use !&lt;/h2&gt;
&lt;p&gt;Swift uses optionals for values that can be nil. To use an optional value, you
need to &amp;quot;un-wrap&amp;quot; it first, which means getting the actual value out, assuming
that the value exists and the optional is not actually nil.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/post/2016-02-12-learning-swift-optionals/&quot;&gt;Optionals&lt;/a&gt; are the subject of a complete post by themselves, but for now, I
will just say this: When you use ! you are &lt;strong&gt;promising&lt;/strong&gt; the compiler that you
will make sure there is a real value in that optional by the time it is needed.&lt;/p&gt;
&lt;p&gt;If this is not the case, your app will crash as shown in this playground:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Playground2.png&quot; alt=&quot;Playground crash&quot;&gt;&lt;/p&gt;
&lt;p&gt;Instead of using ! you should use optional chaining with ? or check the value
with &lt;code&gt;guard&lt;/code&gt; or &lt;code&gt;if let&lt;/code&gt; before use.&lt;/p&gt;
&lt;p&gt;One trap to beware of is with Xcode&#39;s attempts to be helpful and suggest fixes
to problems. If you use an optional where it cannot work, Xcode will warn you
that it is incorrect, but the suggested fix will be to insert !&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/optional_fix.png&quot; alt=&quot;Optional fix&quot;&gt;&lt;/p&gt;
&lt;p&gt;When you see this, do not accept the suggested fix, but use it as a warning to
show that you need to fix the problem properly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. Use let, not var&lt;/h2&gt;
&lt;p&gt;Using constants in C-based languages is a pain. I could never remember the
Objective-C syntax for declaring a static constant, so used to fall back to
using &lt;code&gt;#define&lt;/code&gt; for global constants. Inside my objects, all instance properties
were variables, not constants. Inside functions, it was the same.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t take much imagination to work out that your apps will perform better
and more securely if values that do not need to change are allocated as
constants instead of variables.&lt;/p&gt;
&lt;p&gt;With Swift, this is easy as you just declare a constant using the &lt;code&gt;let&lt;/code&gt; keyword
and declare variables using &lt;code&gt;var&lt;/code&gt;. The Swift compiler will now warn you if you
have used &lt;code&gt;var&lt;/code&gt; where &lt;code&gt;let&lt;/code&gt; would do, but I prefer to declare everything using
&lt;code&gt;let&lt;/code&gt; and only change to &lt;code&gt;var&lt;/code&gt; when this causes an error as shown in this
playground example:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Playground3.png&quot; alt=&quot;Playground let error&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Allow the compiler to infer types&lt;/h2&gt;
&lt;p&gt;With Objective-C and many other languages, you have to tell the compiler exactly
what each variable is going to be: a string, an integer, an object of a certain
type.&lt;/p&gt;
&lt;p&gt;With Swift, the compiler is clever enough to work this out for you, which can
make your code much cleaner looking and easy to read.&lt;/p&gt;
&lt;p&gt;There are a few exceptions to this rule which are shown in the playground below:&lt;/p&gt;
&lt;p&gt;Lines 3 - 6 are allowing the Swift compiler to decide what the type is and it is
choosing valid types: Int, Double, String and UIView.&lt;/p&gt;
&lt;p&gt;Line 8 is a case where we do not want the default type, which would be Double so
we need to tell the compiler that this one really should be a CGFloat.&lt;/p&gt;
&lt;p&gt;Lines 10 &amp;amp; 11 both deal with an array of Ints. In line 10, the values are
supplied so the type of the array can be inferred. In the second case, the array
is initially empty, so the type needs to be specified in the declaration.&lt;/p&gt;
&lt;p&gt;Lines 13 and 14 are the same but with dictionaries instead of arrays.&lt;/p&gt;
&lt;p&gt;In each case, it would have been valid Swift to specify the type but why bother?
The more text you enter , the more change of mistake and it leaves your code
looking cluttered.&lt;/p&gt;
&lt;p&gt;If you ever need to check what type has been assigned to a constant or variable,
Option-click on it in Xcode and you willl get a popup telling you what it is.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Playground4.png&quot; alt=&quot;Playground types&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This was going to be five rules, but then I got a bit too verbose so will add
more rules in a separate post.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Configuring Xcode</title>
    <link href="https://troz.net/post/2016/configuring-xcode/" />
    <updated>2016-02-17T00:00:00Z</updated>
    <id>https://troz.net/post/2016/configuring-xcode/</id>
    <content type="html">&lt;p&gt;While not strictly a part of my Learning Swift series, today I thought I would
discuss some of the ways to configure and use Xcode to be a more productive and
comfortable programmer.&lt;/p&gt;
&lt;h2&gt;1. Editor color themes and fonts:&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/XcodeFontPrefs.png&quot; alt=&quot;Editor&quot;&gt;&lt;/p&gt;
&lt;p&gt;Step through the supplied themes and find the best one for you. Then click the
&#39;+&#39; button at the bottom of the list and duplicate the selected theme. Now you
can tweak it to suit you. Each text category can have its own font and size, but
you can make multiple selections in order to do a bulk change. don&#39;t forget to
check out the Console section too - I dislike the default bold text for many of
the console reports, so always change that.&lt;/p&gt;
&lt;p&gt;An editing font should be mono-spaced i.e. every character should take up
exactly the same width on the screen. This makes your code look neater and
neater code is easier to read when you come back to it. I also much prefer a
sans-serif font, but that is a matter of preference.&lt;/p&gt;
&lt;p&gt;Menlo is the font used by Xcode as a default, but there are many other options
either in the default installation of OS X or as free downloads. I change every
now and then just to keep things interesting, but my current selection is &lt;a href=&quot;https://github.com/tonsky/FiraCode&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Fira
Code&lt;/a&gt;. I love the way it merges two character symbols into a single
character for faster comprehension.&lt;/p&gt;
&lt;p&gt;My other favourites are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/adobe-fonts/source-code-pro&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Source Code Pro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/chrissimpkins/hack&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Hack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mozilla/Fira&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Fira&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.fontsquirrel.com/fonts/Inconsolata&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Inconsolata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Look for a font that makes it easy to distinguish the digit &#39;1&#39; from a lowercase
&#39;l&#39; and where the digit &#39;0&#39; is different to the &#39;O&#39;.&lt;/p&gt;
&lt;p&gt;To install and use a new font, download the files and then double-click a TTF or
OTF font file to install it in FontBook. Once the font is installed, you may
need to restart Xcode in order to make it available.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. Add-ons:&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Xcode 8 has removed the ability to run any plugins or to use
Alcatraz, so this section is no longer valid. Instead Apple is offering official
support for Xcode Source Editor Extensions, so they are the way of the future.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Documentation:&lt;/h2&gt;
&lt;p&gt;Xcode has excellent documentation, but unless you specifically download it, this
will all be accessed online. If you have a bad or non-existent connection, this
can be annoying. Go to Xcode&#39;s Preferences -&amp;gt; Downloads and download all the
document sets you are interested in. They are quite large, but having them local
will be a big bonus.&lt;/p&gt;
&lt;p&gt;The other tool I use for documentation is &lt;a href=&quot;https://kapeli.com/dash&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Dash&lt;/a&gt;. It supports many languages
and is not just for use in Xcode. I found the Xcode integration plugin was not
very useful as it conflicted with other useful behaviours. So I set up a global
shortcut in System Preferences to search Dash for the selected text. This is
much faster than Xcode&#39;s built-in documentation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Dash.png&quot; alt=&quot;Dash&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. Other preferences:&lt;/h2&gt;
&lt;p&gt;Go through the preference panes in Xcode&#39;s preferences and tweak to suit your
style or your group&#39;s preferred style.&lt;/p&gt;
&lt;p&gt;General and Text Editing are important. Text Editing has a second tab for
Indentation which is easy to miss. Indentation styles seem to cause a lot of
heated debate, so make sure you check this out.&lt;/p&gt;
&lt;p&gt;Accounts is vital if you are publishing to the App Stores and also makes it easy
to transfer all your develop information from one computer to another.&lt;/p&gt;
&lt;p&gt;Fonts &amp;amp; Colors and Downloads are discussed above.&lt;/p&gt;
&lt;p&gt;I tend to leave the others set to the defaults.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How do I start to learn Swift?</title>
    <link href="https://troz.net/post/2016/how-do-i-start-to-learn-swift/" />
    <updated>2016-02-16T00:00:00Z</updated>
    <id>https://troz.net/post/2016/how-do-i-start-to-learn-swift/</id>
    <content type="html">&lt;p&gt;The first thing to do is to download the latest version of Apple&#39;s &lt;a href=&quot;https://itunes.apple.com/book/swift-programming-language/id881256329&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift
Programming Language book&lt;/a&gt; from the iBooks Store.&lt;/p&gt;
&lt;p&gt;You can just read this book, especially the first section: &amp;quot;A Swift Tour&amp;quot;, but
you will get more out of it if you follow along with the playground. There is a
link to download the playground from the book which you can use directly if you
are reading using iBooks on the Mac. If you are not, here is the &lt;a href=&quot;https://developer.apple.com/go/?id=swift-tour&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;download
link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You will also need to install &lt;a href=&quot;https://itunes.apple.com/app/xcode/id497799835&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Xcode&lt;/a&gt; from the Mac App Store. Xcode is
Apple&#39;s IDE and is used for programming Mac, iPhones, iPads, Apple Watches and
Apple TVs.&lt;/p&gt;
&lt;p&gt;Swift introduced the playground to Xcode. A playground is a fantastic way to
learn and experiment. You do not need to create a project, build, run a separate
app, install on a device. Code typed into a playground is run immediately and
you can see the results instantly, line by line.&lt;/p&gt;
&lt;p&gt;Here is an example of a playground showing the code on the left and the result
of each line appearing on the right.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2016/Playground1.png&quot; alt=&quot;Playground&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Once you have gone through the Swift Tour, Apple&#39;s book becomes more of a
reference guide to return to whenever you get stuck. The other way I use it is
to check up on changes made when a new version of Swift is released. As the
language is still rapidly evolving, these can be quite extensive. Once I have
the latest version of the book, I go to the Revision History at the back and
click the links to read about the features that have been added or changed.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Apart from Apple&#39;s documentation, there are a lot of great web sites, blogs and
Twitter accounts out there. There are also useful newsletters that give a weekly
summary of exciting developments.&lt;/p&gt;
&lt;p&gt;Here are some of my favourites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.natashatherobot.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Natasha the Robot&lt;/a&gt; - my favourite Swift blogger - sign up for her
newsletter and follow her on Twitter.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/swift/blog/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Apple&#39;s Swift Blog&lt;/a&gt; - not many posts, but good for learning about new
features.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ericasadun.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Erica Sadun&lt;/a&gt; - sometimes a bit too technical for me, but often contains
some really useful snippets. Erica has the distinction of being the first
person outside Apple to have had a Swift language proposal accepted as part of
the open source development process.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://andybargh.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Andy Bargh&lt;/a&gt; - detailed articles on various aspects of Swift, plus a
weekly newsletter.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.raywenderlich.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ray Wenderlich tutorials&lt;/a&gt; - search for Swift and find heaps of tutorials
covering many different aspects.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://swiftsandbox.io&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift Sandbox&lt;/a&gt; - weekly newsletter filled with interesting articles.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://swiftmonthly.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftMonthly&lt;/a&gt; - useful newsletter with links to tutorials, videos and
projects.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://swiftdoc.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;SwiftDoc.org&lt;/a&gt; - the documentation in Xcode is still heavily skewed
towards Objective-C programmers and it can be difficult to find Swift-specific
entries. This site makes it easy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And I hope you will keep coming back to this site to see what new Swift-related
articles are available.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Icon Builder 3.2 Released</title>
    <link href="https://troz.net/post/2016/icon-builder-32-released/" />
    <updated>2016-02-10T00:00:00Z</updated>
    <id>https://troz.net/post/2016/icon-builder-32-released/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://itunes.apple.com/app/icon-builder/id552293482&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Icon Builder 3.2&lt;/a&gt; is now available from the Mac App Store.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Icons for Apple Watch apps now have their alpha channel removed.&lt;/li&gt;
&lt;li&gt;Tidier folder structure for related files (iTunes Artwork files and Read Me).&lt;/li&gt;
&lt;li&gt;Updated to work with Xcode 7&#39;s Assets.xcassets folders.&lt;/li&gt;
&lt;li&gt;Creates iPad Pro icons for Xcode 7.2 or later.&lt;/li&gt;
&lt;li&gt;Fix for Retina Macs creating double-sized images.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Why is Swift so great?</title>
    <link href="https://troz.net/post/2016/why-is-swift-so-great/" />
    <updated>2016-02-10T00:00:00Z</updated>
    <id>https://troz.net/post/2016/why-is-swift-so-great/</id>
    <content type="html">&lt;p&gt;Firstly, I can give you the technical answer, as published by Apple:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Swift is a new programming language for iOS, OS X, watchOS, and tvOS apps that
builds on the best of C and Objective-C, without the constraints of C
compatibility. Swift adopts safe programming patterns and adds modern features
to make programming easier, more flexible, and more fun. Swift’s clean slate,
backed by the mature and much-loved Cocoa and Cocoa Touch frameworks, is an
opportunity to reimagine how software development works.&lt;br&gt;&lt;br&gt; Swift has
been years in the making. Apple laid the foundation for Swift by advancing our
existing compiler, debugger, and framework infrastructure. We simplified
memory management with Automatic Reference Counting (ARC). Our framework
stack, built on the solid base of Foundation and Cocoa, has been modernized
and standardized throughout. Objective-C itself has evolved to support blocks,
collection literals, and modules, enabling framework adoption of modern
language technologies without disruption. Thanks to this groundwork, we can
now introduce a new language for the future of Apple software development.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Excerpt From: Apple Inc. “&lt;a href=&quot;https://itunes.apple.com/book/swift-programming-language/id881256329&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;The Swift Programming Language (Swift 2.1)&lt;/a&gt;.”&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Now I can give you my opinion:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Swift makes for code that is easier &amp;amp; faster to write.&lt;/li&gt;
&lt;li&gt;Swift makes for code that is easier &amp;amp; cleaner to read (much more important
than ease of writing).&lt;/li&gt;
&lt;li&gt;Swift code is safer code.&lt;/li&gt;
&lt;li&gt;Swift breaks free from the out-dated conventions of C-based languages.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;When Apple announced Swift at WWDC 2105, I was astounded. That such a major
event could have been kept completely secret was amazing. Then I watched every
video from WWDC 2015 that discussed Swift. Some were beyond me and others I
absorbed. Looking back, I can see that the development of Objective-C over the
past few years was all aimed at getting to this point, especially with the
introduction of ARC and blocks.&lt;/p&gt;
&lt;p&gt;At that time, I was deeply involved in an existing Objective-C project and not
able to do much in Swift. But I went through the introduction at the start of
the &lt;a href=&quot;https://itunes.apple.com/book/swift-programming-language/id881256329&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Swift Programming Language&lt;/a&gt; book in iBooks and was able to get some
ideas about how the language worked.&lt;/p&gt;
&lt;p&gt;Some months later, I was able to spend time on Swift. As a learning exercise, I
converted an existing Objective-C iOS app to Swift. As might be expected, I
really just wrote Objective-C code using Swift syntax. It took a lot more
learning before I was able to write Swift-y code instead of just translated
Objective-C. In future posts of this series, I hope to enable others to cross
that divide faster than I did.&lt;/p&gt;
&lt;p&gt;Now when I go back to an Objective-C app, I feel like I am walking a tight-rope.
When I use an NSArray, NSSet or NSDictionary, I think &amp;quot;How can I tell what sort
of objects are in that array?&amp;quot;. When I use an object, I think &amp;quot;What if that
object is nil?&amp;quot;. All of a sudden, code that appeared stable now feels random.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;One of the big things that people have latched onto with Swift is that it allows
functional programming. I plan several detailed posts on functional programming
in this series. But for now I would just recommend that you not worry about the
distinction between procedural &amp;amp; functional programming. Both have their
advocates and their detractors. Both have their advantages and disadvantages.
Just do what ever works for you and we can worry about this later.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Learning Swift - An Introduction</title>
    <link href="https://troz.net/post/2016/learning-swift-an-introduction/" />
    <updated>2016-02-09T00:00:00Z</updated>
    <id>https://troz.net/post/2016/learning-swift-an-introduction/</id>
    <content type="html">&lt;h2&gt;Welcome to Learning Swift.&lt;/h2&gt;
&lt;p&gt;This is the first post in what I hope will be an extensive series dealing with
the Swift language. Here are some of the questions I hope to answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Should I learn Swift? Why?&lt;/li&gt;
&lt;li&gt;Why is Swift so great?&lt;/li&gt;
&lt;li&gt;How should I start learning Swift?&lt;/li&gt;
&lt;li&gt;I already know Objective-C - how do I convert?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After that, I am planning a series of posts discussing the major differences of
Swift from the view point of an Objective-C programmer and the aspects of Swift
that I found difficult to grasp. Topics covered will include such things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;optionals&lt;/li&gt;
&lt;li&gt;closures&lt;/li&gt;
&lt;li&gt;functional programming&lt;/li&gt;
&lt;li&gt;protocols&lt;/li&gt;
&lt;li&gt;generics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am coming from a background of Objective-C, so many of my posts will be
dealing with aspects of Swift that are not familiar to Objective-C programmers.
I will not assume any Objective-C programming experience, but some features of
Swift that are new to me, will be familiar to people accustomed to other
languages.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So let&#39;s start by answering the first question:&lt;/p&gt;
&lt;h2&gt;Should I learn Swift?&lt;/h2&gt;
&lt;p&gt;If you are writing or planning apps for any Apple platform - YES.&lt;br&gt; If not,
wait until Swift is available for the platforms you need to support.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Why?&lt;/h2&gt;
&lt;h3&gt;If you are already an iOS or Mac developer: YES&lt;/h3&gt;
&lt;p&gt;This is the new language and Apple is very committed to it. Objective-C is not
going away any time soon, but Swift is where it is going to end up, so why not
make the jump now while the language is still in development? You have to accept
that fact that every release of Xcode is going to break your code, but the
automatic re-factoring works very well. And by moving to Swift now, you get the
chance to influence the direction of a new language. How often does that happen?&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;If you are starting to learn iOS or Mac development: YES&lt;/h3&gt;
&lt;p&gt;If you have no history with Objective-C, then do not waste your time learning
it. There is still a lot of sample code out there in Objective-C, but increasing
numbers of tutorial sites are using Swift and converting their existing
tutorials to Swift. It will help if you know just enough Objective-C to read it,
but you do not need to know enough to write it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;If you do NOT program for Mac or iOS: NOT YET&lt;/h3&gt;
&lt;p&gt;Swift went open source in late 2015. This is a big deal and Apple&#39;s plans for
Swift are quite clear. They want Swift to be such a main-stream language that it
is used instead of Java for basic computer science courses in universities. This
was never going to happen unless the language was open-source and
cross-platform. It is now open-source and available for &lt;a href=&quot;https://swift.org/download/#latest-development-snapshots&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Linux as well as
Mac/iOS&lt;/a&gt;. While Apple has not yet released any further ports, I think there
are more on the way for Raspberry Pi, Arduino and other embedded systems as well
as server-side Swift libraries like &lt;a href=&quot;http://perfect.org&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Perfect&lt;/a&gt;. I expect that a version for
Windows will not be far behind.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>New Twitter account</title>
    <link href="https://troz.net/post/2016/new-twitter-account/" />
    <updated>2016-02-09T00:00:00Z</updated>
    <id>https://troz.net/post/2016/new-twitter-account/</id>
    <content type="html">&lt;p&gt;I have started a new Twitter account so that I can provide rapid support to
users of my apps. Please contact me &lt;a href=&quot;https://twitter.com/trozware&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;@trozware&lt;/a&gt; if you have any questions,
suggestions, bug reports or would just like to chat.&lt;/p&gt;
&lt;p&gt;I will also be using this account to promote my new &lt;a href=&quot;https://troz.net/post/2016-02-09-learning-swift-an-introduction/&quot;&gt;Learning Swift&lt;/a&gt; series.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Possible issues with Icon Builder &amp; Watch icons</title>
    <link href="https://troz.net/post/2016/possible-issues-with-iconbuilder/" />
    <updated>2016-02-06T00:00:00Z</updated>
    <id>https://troz.net/post/2016/possible-issues-with-iconbuilder/</id>
    <content type="html">&lt;p&gt;It has come to my attention that there are two possible issues with Icon Builder
3.0.1 when using icons created for an Apple Watch app. Once of them is something
I can fix and the other appears to be a bug in Xcode 7.2&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Version 3.2 of Icon Builder is now available in the &lt;a href=&quot;http://itunes.apple.com/app/icon-builder/id552293482&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App
Store&lt;/a&gt;. This removes the alpha channel from icons for a watch app.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The first problem is that iTines Connect now requires that the icons for an
Apple Watch app include no alpha channel. This is becoming an increasing trend
with it first having applied to the large icon file you upload directly to
iTunes Connect, then to screen shots. I expect it to extend to iOS app icons
soon, but hopefully Mac app icons can continue to include transparency.&lt;/p&gt;
&lt;p&gt;With Apple Watch icons, you are supposed to create a set of square icons and
watchOS or iTunesConnect applies the rounding mask. Presumably this is more
complicated if the icon contains an alpha channel even if there are no
transparent pixels. If your Watch app icons contain an alpha channel, you will
see errors like this after uploading your app to iTunes Connect:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Invalid Icon - The watch application &#39;AppName.app/Watch/AppName WatchKit
App.app&#39; contains an icon file &#39;AppName.app/Watch/AppName WatchKit
App.app/AppIcon-Watch24x24@2x.png&#39; with an alpha channel. Icons should not
have an alpha channel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You will probably get this error message for every icon size in the Watch app (8
in total). Until I release a new version of Icon Builder that allows for this
this, I suggest you use this &lt;a href=&quot;https://www.cocoacontrols.com/controls/alpha-channel-remover&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Alpha Channel Remover app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In your Xcode project, go to WatchKit App Assets.xcassets, select the AppIcon
set, right-click and choose &amp;quot;Show in Finder&amp;quot;. A folder will open containing
eight .png files and one .json file. Drag &amp;amp; drop the .png files into the Alpha
Channel Remover window and click the &amp;quot;Remove Alpha Channel&amp;quot; button. This will
replace all those image files with new versions without alpha channels. To
confirm this, select any of the images and press Command-I to get info. In the
More Info section, you will see &amp;quot;Alpha channel: No&amp;quot;.&lt;/p&gt;
&lt;p&gt;You can now submit your app again, but that only solves one of the issues.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The other problem is that you will get a warning about the 44x44@2x.png icon
file.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Invalid Icon Name - The watch application &#39;AppName.app/Watch/AppName WatchKit
App.app&#39; contains an invalid icon file name &#39;AppName.app/Watch/AppName
WatchKit App.app/AppIcon-Watch44x44@2x.png&#39;. Make sure that icon file names
follow the pattern&lt;br&gt; &amp;quot;&lt;code&gt;*&amp;lt;dimension&amp;gt;@&amp;lt;scale&amp;gt;x.png&lt;/code&gt;&amp;quot; and they match the
required dimensions. Please visit https://developer.apple.com/watchkit/ for
more information.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As you can see in the error message, the icon name (which is set by Xcode) does
conform to the required pattern, and the image is the correct size. This appears
to be a &lt;a href=&quot;http://www.openradar.me/23801324&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;bug in Xcode 7.2&lt;/a&gt; but does not stop you from submitting your app as
it is only a warning and not an error. Presumably this will be fixed in later
version of Xcode. I am running Xcode 7.2.1 and still see this warning.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Man Reader 1.5</title>
    <link href="https://troz.net/post/2016/man-reader-1.5/" />
    <updated>2016-01-31T00:00:00Z</updated>
    <id>https://troz.net/post/2016/man-reader-1.5/</id>
    <content type="html">&lt;p&gt;Man Reader has been updated to version 1.5 with a major improvement in the way
it looks for available man pages.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader 1.5&lt;/a&gt; is now available through the &lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Change Log:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Much improved searching for available man pages.&lt;/li&gt;
&lt;li&gt;Sand-boxed version now works just as well as the non-sand-boxed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main feature of this version is that it solves the previous problems with
the App Store version and sand-boxing. This was restricting the number of man
pages listed in Man Reader so I had to release a non-sand-boxed version of the
app to allow for full functionality.&lt;/p&gt;
&lt;p&gt;However I discovered that although this gave better results, it was still
missing a lot of man pages. Version 1.5 now queries the man page files in a
completely different way which is listing many more man pages.&lt;/p&gt;
&lt;p&gt;If you previously had downloaded the Man Reader (no SB) version of the app from
this site, please revert to the App Store version which you would have needed to
have bought in order to use the downloaded app. If you bought the app from
Paddle, &lt;a href=&quot;mailto:sarah@troz.net?subject=Man%20Reader%20transfer%20to%20App%20Store&quot;&gt;contact me&lt;/a&gt; and I will transfer you to the App Store version.
Neither of these other versions will be supported or updated any more.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Using 19th Hole on the Apple Watch</title>
    <link href="https://troz.net/post/2015/using-19th-hole-on-the-apple-watch/" />
    <updated>2015-12-12T00:00:00Z</updated>
    <id>https://troz.net/post/2015/using-19th-hole-on-the-apple-watch/</id>
    <content type="html">&lt;p&gt;19th Hole for iPhone and Apple Watch version 3 is now available through the
&lt;a href=&quot;https://itunes.apple.com/us/app/the-19th-hole/id871686159?mt=8&amp;amp;uo=4&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;iTunes App Store&lt;/a&gt;. This version is a complete re-write to accommodate the
changes in iOS 9 and watchOS2.&lt;/p&gt;
&lt;p&gt;The major changes are to do with the way the iPhone and the Watch communicate.
In watchOS1, all the brains of the app had to live in the iPhone, meaning that
every single interaction on the Watch had to be referred back to the iPhone for
processing and the result sent back to the Watch for display. This lead to
communications delays and breakdowns which caused third-party apps to work
sluggishly or not at all. In watchOS2, the brains of the watch part of the app
can now operate inside the watch, greatly reducing the number of communications
needed. As well as that, Apple has now provided us with multiple ways of getting
data to and from the Watch which again improves performance and reliability.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;With the 19th Hole, I made the decision to change the method of inputting
scores. The previous version used a slider with plus and minus buttons to adjust
scores. The problems with this were the small target area of the plus &amp;amp; minus
buttons as well as the fact that a slider with more than about 3 divisions was
not useful as a visual indicator.&lt;/p&gt;
&lt;p&gt;When watchOS2 allowed developers access to the digital crown, I decided to
switch to using that instead. Now you tap the score box of the player you wish
to adjust and use the digital crown to select a number. I think this will be
easier to use while wearing a glove and hope that it is still easy to read out
doors.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The other suggestion is not to do with watchOS2 but is a general tip for using
apps of this kind. By default, the Apple Watch will always show the watch face
when you raise your wrist. Normally this is the most useful thing, but when
scoring a round of golf, it would be easier if The 19th Hole app stayed at the
front. To make this happen, you need to set to watch to show the last used app
instead of the watch face. This can be set using the Apple Watch app on the
phone or on the watch itself.&lt;/p&gt;
&lt;p&gt;On the phone:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open the Apple Watch app.&lt;/li&gt;
&lt;li&gt;Select My Watch from the tabs at the bottom.&lt;/li&gt;
&lt;li&gt;Tap &amp;quot;General&amp;quot; and then scroll down to tap &amp;quot;Wake Screen&amp;quot;.&lt;/li&gt;
&lt;li&gt;Make sure &amp;quot;Resume Last Activity&amp;quot; is checked.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the watch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Press the digital crown to see the app icons.&lt;/li&gt;
&lt;li&gt;Find and tap the Settings icon.&lt;/li&gt;
&lt;li&gt;Scroll down and tap &amp;quot;General&amp;quot;.&lt;/li&gt;
&lt;li&gt;Tap &amp;quot;Wake Screen&amp;quot; and scroll down to make sure that &amp;quot;Previous Activity&amp;quot; is
checked in the &amp;quot;Resume To&amp;quot; section.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Dice Pass for Mac</title>
    <link href="https://troz.net/post/2015/dice-pass-for-mac/" />
    <updated>2015-05-30T00:00:00Z</updated>
    <id>https://troz.net/post/2015/dice-pass-for-mac/</id>
    <content type="html">&lt;p&gt;We live in a world of passwords and passphrases and there is a constant struggle
between having a secure and a memorable password. As a result, many of us re-use
passwords even though we know this is a bad idea.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xkcd.com/936/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;XKCD&lt;/a&gt; had a wonderful comic that illustrated the theory of using a
passphrase composed of words that are easy to remember but with a combination
that made the passphrase very hard to guess. However even when deciding to use a
passphrase like this, it is not easy for us humans to avoid using words that
have meaning for us, particularly when it is something we really need to
remember.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://world.std.com/~reinhold/diceware.html&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Diceware&lt;/a&gt; was developed to solve this problem. You have a set of 5 dice and
a list of words assigned to each possible dice roll for 11111 to 66666. You roll
the five dice and get the matching word from the list. Repeat this for the
number of words you want to use in your passphrase and you have a random phrase
that is hopefully still memorable.&lt;/p&gt;
&lt;p&gt;But this sounds like a lot of effort and not something you are likely to do when
faced by a password entry field on a web page. So &lt;a href=&quot;https://troz.net/dicepass/&quot;&gt;Dice Pass&lt;/a&gt; was written to
do the work for you quickly and easily.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2015/DicePass_Mac.png&quot; alt=&quot;Dice Pass for Mac&quot;&gt;&lt;/p&gt;
&lt;p&gt;Select the number of words you want in your passphrase and see the phrase
immediately. Keep clicking the &amp;quot;Generate New Phrase&amp;quot; button until you get a
phrase you like. If you feel that the computer is not to be trusted when
generating a random number for the dice rolls, use your real dice and click the
dice to change them to the roll you got. If you get a passphrase that is good
except for one or two words, use the Re-Roll button beside those words to get a
new roll for just that word.&lt;/p&gt;
&lt;p&gt;Dice Pass is available from the &lt;a href=&quot;https://itunes.apple.com/app/dice-pass/id997688302?mt=12&amp;amp;uo=4&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt; now.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Apple Watch First Impressions</title>
    <link href="https://troz.net/post/2015/apple-watch-first-impressions/" />
    <updated>2015-04-29T00:00:00Z</updated>
    <id>https://troz.net/post/2015/apple-watch-first-impressions/</id>
    <content type="html">&lt;p&gt;I realise that every one has written one of these posts, but given that everyone
looks for and notices different things, I think it is still valid.&lt;/p&gt;
&lt;p&gt;My first impression was &amp;quot;Why does it come in such a large box?&amp;quot;. The courier
delivered two boxes - my 38mm Apple Watch Sport and Tim&#39;s 42mm Apple Watch
Sport. They both came in long boxes, with the 42mm box significantly longer. And
both boxes were heavy!&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Un-boxing:&lt;/h3&gt;
&lt;p&gt;Opening them was a typical Apple experience: pull the tag to open the external
cardboard box, lift out the white internal box. This opens to reveal the watch
with the default strap attached (short for 38mm, long for 42mm). Lift the watch
out and pull the tags to remove the plastic protective coating. (On a side note,
it drives me crazy when people leave this coating on devices, but I can&#39;t see
this happening on the watch.) Underneath there is a sleeve containing the other
half watch strap so you can swap from long to short. My family tells me that
this also includes a user guide, but I didn’t read that - typical Apple user...
Under the sleeve is the power block and charging cable. The charging puck is
larger than I expected and the cable is very long. Having removed all the
components, the packaging is still quite heavy, so I guess Apple just wanted to
make sure that no watches were damaged in transit and that it felt like a
quality product.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Pairing&lt;/h3&gt;
&lt;p&gt;Pairing my watch to my iPhone was amazingly slick. After selecting my language,
the watch displayed a sort of nebula pattern. On the iPhone, I launched the
Apple Watch app, selected the pairing option and pointed the phone camera at the
watch display. This did the pairing. Then I waited while all the relevant apps
transferred their Apple Watch components to the watch and I was ready to start
using it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;The Watch:&lt;/h3&gt;
&lt;p&gt;The watch comes with the modular watch face showing by default. This is a
digital time read-out with various &#39;complications&#39;. Force-touch the watch face
to change it - this takes a surprisingly strong touch. Then you can swipe
sideways to select a watch style. Once you have a style selected, some of them
offer further customisations: select how much detail is shown, choose what extra
information to display, adjust colors.&lt;/p&gt;
&lt;p&gt;This is a lot of fun and I am still changing mine very frequently. The good
thing is that your customisations are stored, so if you set up the utility watch
face in a particular way, then swap to the modular and configure it, when you go
back to utility, your previous settings are all still there.&lt;/p&gt;
&lt;p&gt;The screen is gorgeous - very crisp and easy to read.&lt;/p&gt;
&lt;p&gt;Twisting your wrist to show the time works well most of the time. There is a
delay that I would estimate at about half a second, presumably to avoid false
positives triggering unnecessary displays. If you are holding your wrist at an
unusual angle e.g. showing the watch to someone, this twist will not always
work, but tapping the screen will display the watch face instantly.&lt;/p&gt;
&lt;p&gt;Tapping on a complication will take you to the relevant parent app e.g. tapping
the current temperature will open the Weather app.&lt;/p&gt;
&lt;p&gt;A minor quibble is that the phases of the moon are displayed from a northern
hemisphere point of view and do not match what we actually see here in
Australia.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Battery Life:&lt;/h3&gt;
&lt;p&gt;This is a complete non-issue. On my first day with the watch, the battery was at
70% when I first turned it on. As you can imagine, usage was very intense that
first day and I got to 8%. Since then, the battery level has only got down to 40
-50% over a day and it recharges to 100% within 2 hours. Tim&#39;s 42mm model gets
slightly better battery life as expected.&lt;/p&gt;
&lt;p&gt;I started off having the battery indicator as one of the &#39;complications&#39; on my
watch face, but now I just ignore it.&lt;/p&gt;
&lt;p&gt;And the battery life of my iPhone has improved remarkably due to so many quick
transactions being done through the watch instead.&lt;/p&gt;
&lt;p&gt;I did have a charging problem one night. I woke up and checked the time and
realised that the watch was quite warm to the touch. I checked the battery level
which was actually lower than when I had put it on the charger. I took it off
the charger for a few hours, then put it back and all was well.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Notifications&lt;/h3&gt;
&lt;p&gt;Here is where you need to be careful. I had turned off most notifications on my
phone before getting the watch, so I have not been inconvenienced, in fact I
have been able to re-enable many notifications as they are no longer so
intrusive.&lt;/p&gt;
&lt;p&gt;At first we thought some notifications were not getting through to the watch,
then we found out that if your iPhone is awake and un-locked, notifications are
not passed on to the watch. So drop the time until sleep on your phone to the
minimum which saves your phone battery and makes sure you receive most
notifications on the watch.&lt;/p&gt;
&lt;p&gt;If you want to stop any apps sending notifications through to the watch, this is
easily set in the iPhone&#39;s Apple Watch app.&lt;/p&gt;
&lt;p&gt;I have noticed there is a bit of a delay. I hear an email or message arriving on
my phone and it can be half a minute before it gets to my watch.&lt;/p&gt;
&lt;p&gt;Once you get a notification, you can tap it to go to the parent app, swipe up to
process it in some way depending on the type of notification, or swipe to the
left to see a button that will clear the notification. If you want to get rid of
all your notifications at once, do a force-touch on the notifications screen to
get the option to clear them all.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Glances&lt;/h3&gt;
&lt;p&gt;Glances are designed to be one screen indicators giving you important
information as quickly as possible. From there, you can decide whether to go to
the parent app or not. These are accessed by swiping up from the bottom of the
screen when looking at the watch face.&lt;/p&gt;
&lt;p&gt;I found that the most important thing here was to reduce the number of glances
shown, since they are displayed sequentially. If you have 20 glances installed,
you have to swipe at least 20 times to see them all. So go to the Apple Watch
app on your iPhone and turn off the glances you do not find essential.&lt;/p&gt;
&lt;p&gt;The first glance in the sequence is essential though as it allows you to toggle
airplane mode, do not disturb and silent mode. It also has a button that can
make your iPhone make a sound to help you locate it. Hold this button down to
make the iPhone flash come on as well.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Activity&lt;/h3&gt;
&lt;p&gt;Here I have to confess to my first disappointment. The activity app tracks three
things: Move, Exercise and Stand. When you first start the watch, you tell it
your age, sex, weight and height. It computes a suggested daily move goal for
you which you can adjust at any time. Then it tracks your daily activities and
shows how well you are doing in relation to your goal. For exercise and standing
the goals are 30 minutes exercise and standing for at least one minute in an
hour 12 times though the day.&lt;/p&gt;
&lt;p&gt;The move tracking seems to be fine. I am not sure what metrics it uses, but on
more active days, the numbers go up.&lt;/p&gt;
&lt;p&gt;The exercise one doesn&#39;t work for me. We went for a walk and when we returned,
Tim&#39;s watch showed 25 minutes exercise and mine showed 1 minute. We had walked
the same distance at the same speed, as confirmed by the pedometer. Subsequent
tests revealed that I don&#39;t swing my arms enough when walking to trigger what
ever sensor this is using. I can&#39;t quite see why it ignores the pedometer
completely, but hopefully this can be fixed by a software update. For now, I
just ignore this circle.&lt;/p&gt;
&lt;p&gt;Standing is another issue since I use a standing desk, so I stand for many hours
each day. Even when standing, I get my hourly reminders to stand. However I use
this as a trigger to make me to move about for a minute, so I suppose that is
still useful.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Communications&lt;/h3&gt;
&lt;p&gt;The various forms of communication are a real strength of the watch. With Tim &amp;amp;
I both having watches, we can phone, message (audio or text) and use the new
Digital Touch to send sketches, taps or heart beats. While it feels a bit weird
to be talking to your wrist, this works really well and the sound quality is
amazing for such a small device. If you need more privacy, it is very easy to
transfer a phone call back to your phone.&lt;/p&gt;
&lt;p&gt;Oddly, the watch does not appear to offer FaceTime but always uses standard
phone calls.&lt;/p&gt;
&lt;p&gt;Messages are where the watch excels. If you get a message with a question, the
watch can suggest possible answers so you just have to tap a button. Even
without this, there is a set of pre-defined answers which you can edit in the
Apple Watch app on your iPhone. And you can also use dictation or the new
animated emojis. Dictation seems a lot better on the watch than it ever was
before on the phone. And even if dictation is not transcribed perfectly, you can
send the message as an audio recording instead of text.&lt;/p&gt;
&lt;p&gt;When recording, tap the Done button as soon as you have finished speaking. The
inclination is to wait for the text to be processed, but the recording carries
on, so stop the recording and let the text processing finish in its own time.&lt;/p&gt;
&lt;p&gt;The animated emojis work when you send them to other devices as they appear as
GIFs. You can choose from 3 sets: faces, hands and hearts. Once you have swiped
to select a set, use the crown to scroll between the options. You can also swipe
further to get to the standard emojis.&lt;/p&gt;
&lt;p&gt;Digital Touch works between watches only. It is a fun gimmick and I can imagine
us developing a set of key codes or sketches, but neither of us can draw very
well, so the sketches are very rough. The heart beat is amusing, but the tapping
is surprisingly effective as an attention-getter.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;I think this is the start of a new way of interacting with our technology in a
way that is both closer to us and less intrusive. I am loving the watch so far
and can&#39;t wait to see where it goes from here.&lt;/p&gt;
&lt;hr&gt;
</content>
  </entry>
  <entry>
    <title>Time In Words 4.0</title>
    <link href="https://troz.net/post/2015/time-in-words-4.0/" />
    <updated>2015-04-15T00:00:00Z</updated>
    <id>https://troz.net/post/2015/time-in-words-4.0/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://troz.net/time-in-words/&quot;&gt;Time In Words&lt;/a&gt; for iOS started off as a fun gimmick, emulating the
&lt;a href=&quot;http://www.qlocktwo.com/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Qlocktwo&lt;/a&gt; type of time display where the relevant words are highlighted in
some way to spell out the time as a sentence. This very quickly evolved into
what I hoped would be more useful app, still showing the original display, but
also providing the current time &amp;amp; date as complete sentences. Then I added time
conversions and discovered the real power and utility of writing out times as
words.&lt;/p&gt;
&lt;p&gt;When it comes to converting times, I doubt there are many people in the world
who haven&#39;t had problems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is that morning or afternoon for them?&lt;/li&gt;
&lt;li&gt;Are they in tomorrow, today or yesterday?&lt;/li&gt;
&lt;li&gt;What if they are using daylight-savings?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having the time written out removes all ambiguity. As an example, Time In Words
currently reports very clearly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In Los Angeles, it is nine minutes to ten on Tuesday evening (daylight-savings
time).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When writing &lt;a href=&quot;https://troz.net/time-in-words-for-mac/&quot;&gt;Time In Words for Mac&lt;/a&gt;, I focussed immediately on the time zone
conversion facilities, and provided a menu bar app that can display as many time
zones as you like (although more than will show on a single screen would
probably get annoying).&lt;/p&gt;
&lt;p&gt;So when I decided to do a complete re-write of Time In Words for iOS, the
obvious move was to emulate the Mac version and allow for as many time zones as
the user wants, instead of the current 2 or 4 (iPhone or iPad). So I discarded
the Qlocktwo display and the separate date display which used a similar
mechanic. I kept the display that shows the current date and time, but every
display after that shows the time in a different time zone. The time zones can
be changed and re-ordered, so the ones you need most are the fastest to get to
once the app opens.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;One new feature (again taken from the Mac version), is the ability to calculate
&amp;quot;What time will it be...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2015/TiW_what_time.png&quot; alt=&quot;What time&quot;&gt;&lt;/p&gt;
&lt;p&gt;You dial in your local date and time to see what time that will be in your
selected zone. And if you want to calculate the other way around, tap the &amp;quot;Swap
time zones&amp;quot; button.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The main impetus for a re-write was the Apple Watch. I thought this would be a
perfect app for the watch as it could use the Apple Watch&#39;s superb time-keeping
in conjunction with my text generation, to add a significant level of usability
to the Apple Watch as regards time zone conversions.&lt;/p&gt;
&lt;p&gt;Sadly, Apple disagrees…&lt;/p&gt;
&lt;p&gt;I completed the app and submitted it to the App Store for review. The review
process seems to be very fast at the moment and the app moved into review only
one day later. And there it stuck for 3 days. This was very odd. My experience
is that once an app gets into review, it only takes a few hours, if that. For
&lt;a href=&quot;https://troz.net/19th-hole/&quot;&gt;19th Hole&lt;/a&gt; - my other app with an Apple Watch component, the review process
took 30 hours. But 3 days!&lt;/p&gt;
&lt;p&gt;Then I got a phone call from Apple where a very polite person explained to me
that although this was not mentioned in any of the documentation, the marketing
people had decided that no apps were to be allowed on the watch if they told the
time.&lt;/p&gt;
&lt;p&gt;I offered to remove the initial screen telling the local time, so the app would
not actually supply the current time but only the time conversions, but that was
not acceptable either.&lt;/p&gt;
&lt;p&gt;Then I tried appealing the rejection. My hopes were not high, but I didn&#39;t see
any harm in asking. I again offered to remove the local time display and
emphasised the accessibility advantages of displaying times in this manner.
Another day, another phone call, another polite Apple person saying no.&lt;/p&gt;
&lt;p&gt;So now I have re-submitted the app for review without the Apple Watch component,
which is a shame. I am hoping that after some months they will review this
policy and allow me to try again, but who knows.&lt;/p&gt;
&lt;p&gt;So you can see what you are missing, here are a couple of screen shots from the
watch app showing my local time, plus one time zone:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2015/Watch_1.png&quot; alt=&quot;Local time&quot;&gt;
&lt;img src=&quot;https://troz.net/images/2015/Watch_2.png&quot; alt=&quot;Converted time&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;One more minor point. People who actually look at version numbers may wonder why
I have gone from version 2.5 to version 4.0. When I was working on version 2.5,
I considered making it version 3.0 and then changed my mind. However by that
time I had already created a version 3.0 in iTunesConnect, so I was not able to
use 3.0 again this time. This version was such a big change that I wanted it to
be a .0 release, so that meant going to version 4.0&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Apple Watch App - Rejected, then Accepted</title>
    <link href="https://troz.net/post/2015/apple-watch-rejected-then-accepted/" />
    <updated>2015-04-11T00:00:00Z</updated>
    <id>https://troz.net/post/2015/apple-watch-rejected-then-accepted/</id>
    <content type="html">&lt;p&gt;As described in a &lt;a href=&quot;https://troz.net/post/2015-04-02-my-first-apple-watch-app/&quot;&gt;previous post&lt;/a&gt;, about 9 days ago I submitted my first
Apple Watch app for review. The app was an extension of my golf scoring app:
&lt;a href=&quot;https://troz.net/19th-hole/&quot;&gt;The 19th Hole&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I expected that App Store review times would go up dramatically once Apple had
allowed developers to submit watch apps, but this proved to be incorrect. Three
days after submission, the app was marked as &amp;quot;In Review&amp;quot;. This seemed to happen
last thing on a Saturday, so there was no further action for two days, at which
point the app was rejected, with the following details (sic):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We noticed an issue in your app that contributes to a lower quality user
experience than Apple users expect. Specifically,&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;while using the Apple Watch app with the phone app is in the background mode
(display off), the new data entry on the Watch App does not refresh
accordingly until the phone app is turn back on.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;So it appeared that the communication between the watch and the phone, which
worked perfectly on the simulators, did not work at all on the actual devices.
This is the problem with developing for a device that you do not have.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;After some research, it appears that the problem is with the phone not operating
correctly in background mode. Apple provides for communications from the watch
to the phone. The phone can respond, but only the watch can initiate a
connection. I guess this makes sense since every watch will be paired with a
phone but the reverse will not always be true.&lt;/p&gt;
&lt;p&gt;To talk to the phone, the watch uses this function:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;openParentApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; userInfo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NSObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  reply reply&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NSObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSError&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The phone listens for messages in this event handler:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; application&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  handleWatchKitExtensionRequest userInfo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NSObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  reply reply&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NSObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The name of the WatchKit function is slightly misleading as it does open the
parent application, but only in the background. Then the parent app is supposed
to do whatever tasks are called for in the handleWatchKitExtensionRequest
function, and return any required data using the supplied reply() function.&lt;/p&gt;
&lt;p&gt;So far, so good, and it all worked perfectly in the simulator. But it appears
that although this call wakes the iPhone app, it does not stay awake long enough
to do anything. Many thanks to Brian at &lt;a href=&quot;http://www.fiveminutewatchkit.com/blog/2015/3/11/one-weird-trick-to-fix-openparentapplicationreply&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Five Minute WatchKit&lt;/a&gt; who pointed
out the way to make this work. Firstly, you create a dummy background task
guaranteed to keep the iPhone app awake for 2 seconds. Then you perform your own
work, using another background task. When that is finished, the phone can send
the response and end the real background task. The dummy task will time out by
itself after 2 seconds.&lt;/p&gt;
&lt;p&gt;For my needs, I could probably reduce the 2 seconds considerably, but since this
is a problem I am unable to test, I decided just to go with Brian&#39;s suggestion
as it is.&lt;/p&gt;
&lt;p&gt;I re-submitted the app the same day it was rejected, and two days later it was
back in review. The review process took longer than usual, but about 30 hours
later, the app was marked &amp;quot;Pending an Apple Release&amp;quot;. I assumed this meant that
Apple would release all WatchKit apps on 24th April when the watches shipped,
but the following day the app was reported as &amp;quot;Pending Developer Release&amp;quot; (I had
set it to release manually). Now I have told iTunesConnect to release the app
and it is &amp;quot;Processing for App Store&amp;quot;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;On a related note, the start of pre-ordering for the Apple Watch was scheduled
for a very civilised 5:01 pm here in eastern Australia. I had three watches all
lined up in my Favorites in the Apple Store app, ready for ordering. When the
store became live (at about 5:03 pm), I could not work out how to order all
three in a single operation, so had to place these orders one at a time. I got
the first two ordered for the soonest possible delivery, but by the time I got
to the third (about 3 minutes later), I had missed out and it will ship in 4 to
6 weeks!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>My First Apple Watch App</title>
    <link href="https://troz.net/post/2015/my-first-apple-watch-app/" />
    <updated>2015-04-02T00:00:00Z</updated>
    <id>https://troz.net/post/2015/my-first-apple-watch-app/</id>
    <content type="html">&lt;p&gt;I have just submitted my first Apple Watch app to the iTunes App Store. This is
a scary thing to do, since I have never seen, touched or used an Apple Watch and
all I had to go on was the Simulator that Apple supplies with Xcode.&lt;/p&gt;
&lt;p&gt;At the moment, Apple has only made a limited set of Apple Watch features
available to developers, and all Apple Watch apps come tethered to an iOS app -
presumably this will mainly be iPhone apps, but I wonder will we be able to
connect Apple Watches to iPads? Anyway, it made sense to extend one of my
existing apps to use the Apple Watch instead of starting from scratch.&lt;/p&gt;
&lt;p&gt;Of my &lt;a href=&quot;https://troz.net/apps-ios/&quot;&gt;iOS apps&lt;/a&gt;, I decided that &lt;a href=&quot;https://troz.net/19th-hole/&quot;&gt;The 19th Hole&lt;/a&gt;, my golf scoring app,
would be well suited to the Apple Watch. The problem with using the iPhone
version to score is that you have to keep pulling your iPhone in and out of your
golf bag, sometimes in the rain, sometimes in bright sunlight. Being able to
enter the scores on your wrist sounds like a big improvement. We know that the
Apple Watch is at least somewhat water-resistant, but we are yet to see how well
it performs in bright sunlight, however I would expect that Apple has put
considerable effort into this.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Once I decided to write an Apple Watch app, the first step was to learn how. I
read all Apple&#39;s published documents to get a feel for what was possible and
what user-interface objects would be available. I find online courses very
helpful and by far the best Apple Watch course that I found was &lt;a href=&quot;http://www.raywenderlich.com/video-tutorials#watchkit&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Ray
Wenderlich&#39;s WatchKit video tutorial series&lt;/a&gt;. The instructors were very clear
and explained how the layout and sizing of objects works on the Apple Watch, as
well as how to set up and move between views and how to communicate from the
Watch back to the iPhone. The layout section of the course was particularly
valuable as no other course I found even mentioned this, although it is crucial
to the interface design and allowing for the two sizes of watch.&lt;/p&gt;
&lt;p&gt;I had released an update to &lt;a href=&quot;https://troz.net/19th-hole/&quot;&gt;The 19th Hole&lt;/a&gt; recently in preparation for this.
I wanted to have squashed the bugs and updated the interface, so the only
changes needed would be the WatchKit ones.&lt;/p&gt;
&lt;p&gt;The first step in adding a WatchKit app to your existing app is to create a new
target in the Xcode project. Choosing WatchKit actually adds two new targets:
the WatchKit Extension which lives on the iPhone and supplies the code for the
Watch, and the WatchKit App which lives on the Watch and provides the interface.&lt;/p&gt;
&lt;p&gt;I decided to use a paged interface with one page per hole. Each of these pages
has a table, with one row per player. A slider with plus &amp;amp; minus buttons lets
you enter the score for each player and the total score for each player is also
displayed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2015/19thHole-Watch2.png&quot; alt=&quot;Data entry&quot;&gt;&lt;/p&gt;
&lt;p&gt;Apart from this data entry view, I have a splash screen which shows you the
current round as set up on the iPhone and a final summary screen showing
everyone&#39;s totals.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://troz.net/images/2015/19thHole-Watch1.png&quot; alt=&quot;Splash screen&quot;&gt;
&lt;img src=&quot;https://troz.net/images/2015/19thHole-Watch3.png&quot; alt=&quot;Summary&quot;&gt;&lt;/p&gt;
&lt;p&gt;These images are taken from the Apple Watch simulator running in 42mm mode.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The biggest programming problem was getting the communications working beween
the two devices. The 19th Hole uses Core Data for all data storage. My first
attempt shared this data between the two apps, allowing both to read and update
the database. This almost worked, but if both apps were open, then changes in
one device did not get across to the other. It seemed that the data was cached
and I was unable to find a way to force either app to update every time.&lt;/p&gt;
&lt;p&gt;My next attempt kept all the data in the iPhone app and had the WatchKit
extension ask for whatever data it needed and send changes to the iPhone app.
This worked much better and I was able to get live updates between the two
devices.&lt;/p&gt;
&lt;p&gt;The other issue is the fact that I have never seen, touched or used an Apple
Watch. This means that I had to guess a lot of things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how well will it perform?&lt;/li&gt;
&lt;li&gt;will the icon look OK?&lt;/li&gt;
&lt;li&gt;are the controls large enough to use?&lt;/li&gt;
&lt;li&gt;will the text be large enough to read?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My main philosophy was to keep the Watch app as simple as possible and use the
standard interface elements, font sizes etc. Hopefully that will work.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The final step was submitting the app to iTunes Connect for review. It was not
clear to me how to do this, so I logged into iTunes Connect, created a new
version and entered the meta-data. I couldn&#39;t see anywhere to say anything about
the Watch. I archived the main iOS app as usual and validated it with the Store.
I had a few things to fix up, but I got the build submitted.&lt;/p&gt;
&lt;p&gt;Back to iTunes Connect and once the uploaded build had been processed, I was
able to select it and click &amp;quot;Submit for Review&amp;quot;. At this point, the page
reported an error and showed me a new section where I could upload the Apple
Watch icon and screen shots.&lt;/p&gt;
&lt;p&gt;I had been unsure how the icon would look when made round, but it looks OK on
the iTunes Connect page. How it will look on a tiny watch screen remains to be
seen. Anyway, once I had my icon and screen shots uploaded, I was able to
proceed to the next step. But then I got a message saying that the app could not
be processed - no explanation. Thankfully, I had seen some developers on Twitter
reporting this, and somebody said that the solution was to remove the alpha
channel from your screen shots. Sorry for not giving credit here - I forget who
said this, but it worked, so thank you whoever you are.&lt;/p&gt;
&lt;p&gt;So now my app is waiting for review and who knows how long that will take.
According to &lt;a href=&quot;http://appreviewtimes.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;App Review Times&lt;/a&gt;, the current estimate is 8 days, but the
trend is heading upwards. I would expect that to blow out in the next few weeks
as developers try to get their apps ready for the Apple Watch release date of
April 24th.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>LiveCode stacks</title>
    <link href="https://troz.net/post/2015/livecode-stacks/" />
    <updated>2015-03-18T00:00:00Z</updated>
    <id>https://troz.net/post/2015/livecode-stacks/</id>
    <content type="html">&lt;p&gt;When I converted my site to Jekyll &amp;amp; GitHub, one of the things I left behind was
a collection of LiveCode (Revolution) stacks. For many years I was active in the
Revolution community before going in other directions and over that time, I
built up a small library of stacks that I released into the public domain.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://livecode.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;LiveCode&lt;/a&gt; is a cross-platform rapid development environment. There is an
open source community edition available for anyone wishing to get started.&lt;/p&gt;
&lt;p&gt;I had assumed that since I had not been an active LiveCode user for many years,
these stacks would have become worthless. But in the last couple of days, I have
received a couple of queries from people looking for them.&lt;/p&gt;
&lt;p&gt;So I created a new GitHub repository and uploaded them all to here: &lt;br&gt;
&lt;a href=&quot;https://github.com/trozware/rev_stacks&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;LiveCode stacks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;None of these stacks have been updated recently, but feel free to use or adapt
as you see fit. They have not been tested using recent versions of LiveCode, so
may not work as expected. As stacks are provided as is. Use at your own risk.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Icon Builder 3.0.1</title>
    <link href="https://troz.net/post/2015/icon-builder-3.0.1/" />
    <updated>2015-03-17T00:00:00Z</updated>
    <id>https://troz.net/post/2015/icon-builder-3.0.1/</id>
    <content type="html">&lt;p&gt;So why an announcement for such a minor upgrade?&lt;/p&gt;
&lt;p&gt;Version 3.0 got no announcement because it was not the release I wanted.&lt;/p&gt;
&lt;p&gt;Having downloaded the Xcode 6.2 &amp;amp; 6.3 betas, I had worked out what icons were
needed to a WatchKit app. This, combined with several other changes, made me
bump the version number to 3.0.&lt;/p&gt;
&lt;p&gt;Sadly, Apple rejected the update because it referred to pre-release products. So
I pulled all mentions of WatchKit out of the interface and meta data and got 3.0
released.&lt;/p&gt;
&lt;p&gt;After the Apple keynote on March 9th, I re-enabled the WatchKit portions of the
app and re-submitted to the App Store, hoping that with the WatchKit SDK now
part of the official Xcode release, Apple&#39;s reviewers would allow it to get
through this time.&lt;/p&gt;
&lt;p&gt;This worked - in fact the app was reviewed and accepted within 6 days - so now
Icon Builder adds WatchKit to its list of supported platforms.&lt;/p&gt;
&lt;p&gt;Actually, Apple may have done me a favour in rejecting the first one as they
changed the specifications for the icons slightly between Xcode 6.2 beta and
Xcode 6.2 release.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://troz.net/icon-builder/&quot;&gt;Icon Builder page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itunes.apple.com/us/app/icon-builder/id552293482?mt=12&amp;amp;uo=4&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;What’s New in version 3?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Support for WatchKit app icons (version 3.0.1)&lt;/li&gt;
&lt;li&gt;Simplified interface: removed display of smaller icons.&lt;/li&gt;
&lt;li&gt;Fix for @3x images not being saved when you selected iOS 6 support.&lt;/li&gt;
&lt;li&gt;iTunesArtwork@2x file now saved as a JPG to avoid iTunes Connect errors.&lt;/li&gt;
&lt;li&gt;Removed CarPlay icon - Xcode sometimes gave errors when this was included.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>App Store Preview Videos</title>
    <link href="https://troz.net/post/2015/app-store-preview-videos/" />
    <updated>2015-03-16T00:00:00Z</updated>
    <id>https://troz.net/post/2015/app-store-preview-videos/</id>
    <content type="html">&lt;p&gt;Apple now allows app developers to add a video preview of their app when
uploading to iTunes Connect. This video is shown to potential purchasers when
they are looking at an app in the iTunes App Store, just like the screen shots.&lt;/p&gt;
&lt;p&gt;I have been working on an update to &lt;a href=&quot;https://troz.net/19th-hole/&quot;&gt;19th Hole&lt;/a&gt; and since it uses a custom
method for data input, I decided that a video would be really useful in this
case.&lt;/p&gt;
&lt;p&gt;The first step was to work out how to record video from my iPhone. In OS X
Yosemite (10.10), this can be done using QuickTime Player. Connect your device
to your Mac using a Lightning cable (I think it has to be a Lightning cable -
the old cables will not work). Open up QuickTime Player and choose &amp;quot;New Movie
Recording&amp;quot; from the File menu. By default this shows input from the Mac&#39;s
FaceTime camera, but just to the right of the red Record button, there is a down
arrow that shows a popup menu. Click this and you can select your iPhone as the
Camera. Since I wanted the sound effects included, I also selected my iPhone as
the Microphone, but if you want to provide a voice-over or sound-track, you
might want to change this.&lt;/p&gt;
&lt;p&gt;That handles the technical side of recording, but I needed a way to show where I
was tapping and pressing on the iPhone screen. After some searching, I found
&lt;a href=&quot;https://github.com/toddreed/Touchpose&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Touchpose&lt;/a&gt; which was exactly what I wanted. Since it requires changing the
main.m file, as well as changes to the app delegate, I created a branch in my
app&#39;s git repository so that I could apply these changes temporarily, but save
them for next time. The only alternation I made to the default settings was to
change the color of the cursor indicator to suit the color scheme of my app. And
since this was a temporary change, I set it to show the cursor indicator all the
time, not just when the screen was mirrored. All the details are shown in the
sample project.&lt;/p&gt;
&lt;p&gt;Next step was to actually record a session. After my first attempt (which lasted
about 67 seconds), I checked the Apple specs and found that the video could not
be longer than 30 seconds. So I re-thought what I wanted to include and tried
again. It took a few tries to get what I wanted, but finally I ended up with a
.mov file that was 26 seconds long.&lt;/p&gt;
&lt;p&gt;On to iTunes Connect where I had the new version of the app set up already with
its screen shots. Since I have an iPhone 6, the video was only suited for the
4.7 inch section. So I dragged it into the drop zone and waited. No good - the
video frame rate was too high. It has to be 30 fps or less. QuickTime Player had
made a video that was 50 fps.&lt;/p&gt;
&lt;p&gt;The app I usually turn to for any video conversion needs is &lt;a href=&quot;https://handbrake.fr&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Handbrake&lt;/a&gt;. So I
ran the app through Handbrake, specifying a frame rate of 30. This converted the
video to .mp4 instead of .mov, but it was 30 fps. It wasn&#39;t until I tried to
upload it to iTunes Connect that I realised Handbrake had cropped the video from
750 x 1334 to 750 x 1330. After searching through the settings, I found where I
needed to turn on custom clipping and set it to 0 so that the original
dimensions were preserved. But iTunes Connect still didn&#39;t like it, although
this time the error message was un-informative.&lt;/p&gt;
&lt;p&gt;Eventually, the brains trust on the other side of the room suggested iMovie. Not
only that, but he found that once the original had been dragged into iMovie, the
share options included an &amp;quot;App Preview&amp;quot; setting. This gave us a .mov file of the
correct dimensions and the correct frame rate which iTunes Connect accepted.&lt;/p&gt;
&lt;p&gt;iTunes Connect that allowed me to select the poster frame for the video and save
the changes to the app meta data. At that point, it showed a notice saying that
the app preview was being processed which could take up to 24 hours. It appears
that the processing has been finished, as that notice has disappeared.&lt;/p&gt;
&lt;p&gt;For the next stage, I ned to wait until the app gets reviewed and hopefully
approved. Then it will be interesting to see how the video looks in the iTunes
App Store.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As a reward for reading this post, here is a pre-release of the video, showing
how to enter the score data for a hole of golf using &lt;a href=&quot;https://troz.net/19th-hole/&quot;&gt;19th Hole&lt;/a&gt;:&lt;/p&gt;
&lt;video controls=&quot;&quot; poster=&quot;https://troz.net/images/19thHole-scoring-web.png&quot;&gt;
  &lt;source src=&quot;https://troz.net/images/19thHole-scoring-web.mp4&quot; type=&quot;video/mp4&quot;&gt;
	Your browser does not support the video tag.
&lt;/video&gt;
</content>
  </entry>
  <entry>
    <title>Testing automation</title>
    <link href="https://troz.net/post/2015/testing-automation/" />
    <updated>2015-03-12T00:00:00Z</updated>
    <id>https://troz.net/post/2015/testing-automation/</id>
    <content type="html">&lt;p&gt;Today I am experimenting with automating post generation.&lt;/p&gt;
&lt;p&gt;Firstly, there is the structure of the post Markdown document, which really
boils down to the file name and the header.&lt;/p&gt;
&lt;p&gt;I have set up a macro in &lt;a href=&quot;http://www.ergonis.com/products/typinator/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Typinator&lt;/a&gt; so that I just type the title of the
post in a text document. I copy that title to the clipboard and type the
shortcut. The header is then created with the title and the formatted permalink.&lt;/p&gt;
&lt;p&gt;To save the document, I use another Typinator snippet to convert the title
(still in the clipboard) to lower case and to replace the spaces in the title
with dashes.&lt;/p&gt;
&lt;p&gt;For starting the local Jekyll server, I have turned to an &lt;a href=&quot;http://www.alfredapp.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Alfred&lt;/a&gt; workflow.
I have two actions in the workflow. One opens Terminal, cd&#39;s to my site folder
and runs the command to start the local Jekyll server. The second opens my
default browser to the local Jekyll test address.&lt;/p&gt;
&lt;p&gt;Both these actions happen simultaneously so the browser usually fails until
refreshed. The server is left running in Terminal so I can shut it down when
finished testing.&lt;/p&gt;
&lt;p&gt;After writing the post, the final stage is to publish, so I need to work out the
commands for doing a git commit and push. Then I should be able to automate
those tasks.&lt;/p&gt;
&lt;p&gt;Testing the git commands using Terminal, I have come up with the following
sequence:&lt;/p&gt;
&lt;p&gt;git add .
git commit -a -m &amp;quot;Commit message here&amp;quot;
git push&lt;/p&gt;
&lt;p&gt;The commit message could be the title of the post, so I need a way to construct
the command and then execute it. Typinator is good at text manipulation, so I
constructed another macro that takes the clipboard, uses it as the commit
message and performs the git commands. This gave a good commit message, but the
actual commit failed, so that was no use.&lt;/p&gt;
&lt;p&gt;So then I went back to Alfred and created a workflow there with a series of
Terminal commands to perform the actions above. This only allows a generic
commit message, but it works as expected.&lt;/p&gt;
&lt;p&gt;So there is still room for improvement, but with a set of macros, I can automate
a lot of the standard text entry, which is always good for reducing errors.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The sequence now is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the text editor and type the title of the post.&lt;/li&gt;
&lt;li&gt;Cut this into the clipboard.&lt;/li&gt;
&lt;li&gt;Run the Typinator macro to fill in the post header.&lt;/li&gt;
&lt;li&gt;Save the post file, using the Typinator macro to construct the file name.&lt;/li&gt;
&lt;li&gt;Write the post, then use an Alfred workflow to test it locally.&lt;/li&gt;
&lt;li&gt;When ready, use an Alfred workflow to publish.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
</content>
  </entry>
  <entry>
    <title>Workflow for writing a post</title>
    <link href="https://troz.net/post/2015/workflow-for-writing-a-post/" />
    <updated>2015-03-10T00:00:00Z</updated>
    <id>https://troz.net/post/2015/workflow-for-writing-a-post/</id>
    <content type="html">&lt;p&gt;So here is where I need to get used to Jekyll and how things are done. I will
end up automating this when I am sure of the process, but for now, I am just
going to work my way through it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Start up the local Jekyll server:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I opened Terminal and used &lt;code&gt;cd&lt;/code&gt; to get to the folder containing my site.&lt;/li&gt;
&lt;li&gt;Next I started the Jekyll server locally using &lt;code&gt;jekyll serve&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This gave me the address of the local server - in my case
&lt;code&gt;http://127.0.0.1:4000/&lt;/code&gt; - so I went there in my browser to see the default
page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;My goal is to write a new blog post (this one in fact).&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For advice on creating a new post, I went to the &lt;a href=&quot;http://jekyllrb.com/docs/posts/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jekyll docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Jekyll needs the file name to be a specific format which includes the date
and title.&lt;/li&gt;
&lt;li&gt;Looking in the &lt;code&gt;_posts&lt;/code&gt; folder of the site folder, I see the previous post
file is titled &lt;code&gt;2015-03-08-new-site-for-trozware.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;As it is now 10th March 2015, I am saving this file as:
&lt;code&gt;2015-03-10-workflow-for-writing-a-post.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Saving at this point shows a Jekyll error in the Terminal, but the next
stage will fix that.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Front matter:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jekyll specifies that each file must have a &lt;a href=&quot;http://jekyllrb.com/docs/frontmatter/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;front matter block&lt;/a&gt; in a
certain format.&lt;/li&gt;
&lt;li&gt;The header has certain essential parameters and then more optional ones.&lt;/li&gt;
&lt;li&gt;My site was imported from WordPress, so the older posts contain several
header parameters that may not be necessary any more.&lt;/li&gt;
&lt;li&gt;Look at the screen shot below to see what I have in this post file:&lt;/li&gt;
&lt;li&gt;After saving, I see this entry in my Terminal:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Regenerating: 1 file(s) changed at 2015-03-10 13:50:40 ...done in 0.421789 seconds.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Now I can see the title and the start of the post on my first page and when
clicking on it, I can see all the text.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Adding an image:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is not really necessary for this post, but I wanted to document it as
part of the workflow.&lt;/li&gt;
&lt;li&gt;I wanted to show the front matter and start of this post, so I took a
screen shot of the top of my editor window.&lt;/li&gt;
&lt;li&gt;Then I renamed the image file and moved it into the &lt;code&gt;images&lt;/code&gt; folder of my
site.&lt;/li&gt;
&lt;li&gt;Now to embed the image using this Markdown: &lt;code&gt;![Post header][2]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;As you can see, I prefer to group all links (images &amp;amp; URLs) at the end of
the document and keep the body of the text as clean as possible.&lt;/li&gt;
&lt;li&gt;&lt;img src=&quot;https://troz.net/images/2015/post_header.png&quot; alt=&quot;Post header&quot;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Proofing:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Now that I have the text of my post, I need to proof-read it from the local
Jekyll server.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;jekyll serve&lt;/code&gt; command running in Terminal keeps the pages up-to-date
as you edit, but you will need to refresh your page in the browser manually
to check your changes.&lt;/li&gt;
&lt;li&gt;And I have found that if you make a major change to the site e.g. a CSS
file or a config file, then it is best to stop the Jekyll server and start
it again to make it re-build all the files.&lt;/li&gt;
&lt;li&gt;To be completely sure, stop the server and run &lt;code&gt;jekyll build&lt;/code&gt; in Terminal
although this may only do what &lt;code&gt;jekyll serve&lt;/code&gt; does on startup anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Publishing:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Now it is time to publish.&lt;/li&gt;
&lt;li&gt;As I am using GitHub Pages to host, the site files are all part of a single
Git repository.&lt;/li&gt;
&lt;li&gt;And as I understand it, the data &lt;strong&gt;must&lt;/strong&gt; be in the &lt;code&gt;master&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;Opening up the GitHub client, I see that my repository has a bunch of
changes: - the file I am working on - the image I inserted - feed.xml -
index.html files for each page&lt;/li&gt;
&lt;li&gt;This is as expected, since all pages are pre-generated when using Jekyll,
so pages of posts must be re-rendered when a new post is added to the top.&lt;/li&gt;
&lt;li&gt;Note that several of the files appear twice: once from where they are
edited and once from the _site folder which serves them.&lt;/li&gt;
&lt;li&gt;Just two simple steps now, using the GitHub client: 1. Commit 2. Sync&lt;/li&gt;
&lt;li&gt;And that&#39;s it. My new post is online and ready for reading.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This was my first post after the one I wrote when setting up, so the first
real post.&lt;/li&gt;
&lt;li&gt;While there appear to be a lot of steps, none of them are difficult.&lt;/li&gt;
&lt;li&gt;But I do have 4 apps running to do it: 1. TextWrangler for editing 1.
Terminal to run the local Jekyll server 1. Safari to check the finished
post 1. GitHub to commit and upload&lt;/li&gt;
&lt;li&gt;Automating this is an obvious next step, so stay tuned...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <title>New Site for TrozWare</title>
    <link href="https://troz.net/post/2015/new-site-for-trozware/" />
    <updated>2015-03-08T00:00:00Z</updated>
    <id>https://troz.net/post/2015/new-site-for-trozware/</id>
    <content type="html">&lt;h2&gt;Welcome to TrozWare&#39;s new site.&lt;/h2&gt;
&lt;p&gt;It appears that I am not alone in deciding to move away from WordPress.
WordPress is great - easy to set up and with lots of cool themes to allow
customisation of your site. But it is slow, especially if (like me) you are
running on a low-cost shared server.&lt;/p&gt;
&lt;p&gt;I had been reading about &lt;a href=&quot;http://jekyllrb.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Jekyll&lt;/a&gt;, so I decide to give it a go and also host
my site on &lt;a href=&quot;https://pages.github.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;, taking advantage of their faster servers.&lt;/p&gt;
&lt;p&gt;I followed the advice given by &lt;a href=&quot;http://www.girliemac.com/blog/2013/12/27/wordpress-to-jekyll/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Girlie Mac&lt;/a&gt; and performed the migration from
my self-hosted WordPress site. I am using the &lt;a href=&quot;http://pixyll.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Pixyll&lt;/a&gt; theme by &lt;a href=&quot;http://johnotander.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;John
Otander&lt;/a&gt; with a few tweaks of my own. The site is much faster to load, and I
am hoping that the easier editing will inspire me to write more posts.&lt;/p&gt;
&lt;p&gt;Of course it wasn&#39;t all positive. WordPress did give me some features I liked,
but I think overall the result is better.&lt;/p&gt;
&lt;h3&gt;WordPress&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;WordPress gave me a full LAMP stack, so I could run my own PHP scripts.&lt;/li&gt;
&lt;li&gt;WordPress gave me a search function.&lt;/li&gt;
&lt;li&gt;Commenting was built-in.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Jekyll&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Jekyll builds the pages out as static HTML, so all pages are pre-generated for
fast loading.&lt;/li&gt;
&lt;li&gt;Writing for Jekyll is pure Markdown so it is easier to write and preview
locally.&lt;/li&gt;
&lt;li&gt;Some of the things I needed a plugin for in WordPress can be done natively in
Jekyll: - tables - syntax-highlighting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For comments, I have switched to &lt;a href=&quot;https://disqus.com&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Disqus&lt;/a&gt;, but I am not sure about it yet. I
will evaluate over the next few weeks. In WordPress, I used a plugin to handle
the spam that inevitably arrived. I am hoping that Disqus will protect me from
that, but I have to see how good a job it does and whether the few real comments
are worth it. A better option might be to turn off comments completely and just
have a contact form (which I have added using &lt;a href=&quot;http://formspree.io&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Formspree&lt;/a&gt;). Working in
Markdown is great. I love how I can have a really clean look in my editor and
still end up with well formatted HTML. I use Coda 2 on my Mac as my standard web
editor, so that is what I am writing in now. I will probably experiment with
other Markdown editors as I go. And for any Mac users out there working in
Markdown, I highly recommend &lt;a href=&quot;http://brettterpstra.com/projects/markdown-service-tools/&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Brett Terpstra&#39;s Markdown Service Tools&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Man Reader 1.4 Now Available</title>
    <link href="https://troz.net/post/2014/man-reader-1-4-now-available/" />
    <updated>2014-04-03T00:00:00Z</updated>
    <id>https://troz.net/post/2014/man-reader-1-4-now-available/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://troz.net/manreader/&quot;&gt;Man Reader 1.4&lt;/a&gt; is now available through the &lt;a href=&quot;http://itunes.apple.com/app/man-reader/id522583774?mt=12&quot; rel=&quot;noreferrer nofollow noopener external&quot; target=&quot;_blank&quot;&gt;Mac App Store&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;The new features and fixes are:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Fix for plain text view not wrapping correctly for odd-sized fonts.&lt;/li&gt;
&lt;li&gt;Fix for HTML text view over-riding font colors that may be invisible.&lt;/li&gt;
&lt;li&gt;Search for missing entry (e.g. g++) no longer requires Enter or Return.&lt;/li&gt;
&lt;li&gt;Better automatic selection of matching entries while searching.&lt;/li&gt;
&lt;li&gt;Fix for animation warning message appearing in Console.&lt;/li&gt;
&lt;li&gt;Status display shows when man page list is being updated.&lt;/li&gt;
&lt;li&gt;Searching inside a page now allows a minimum of 2 characters (was 3), so
flags&lt;br&gt;
e.g. ‘-b’ do not need to be escaped.&lt;/li&gt;
&lt;li&gt;Fixed path for Network Utility in tool bar (moved in OS X 10.9).&lt;/li&gt;
&lt;li&gt;More information available about the effects of sand-boxing for this app.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Email clients for Mac and iOS</title>
    <link href="https://troz.net/post/2014/email-clients-for-mac-ios/" />
    <updated>2014-03-25T00:00:00Z</updated>
    <id>https://troz.net/post/2014/email-clients-for-mac-ios/</id>
    <content type="html">&lt;p&gt;Some people are moving away from email, but for me, it remains the best way to
communicate online. I get to keep a record of conversations and the attached
files are with the text so I can always see the context.&lt;/p&gt;
&lt;p&gt;But I think everyone would agree that Apple have dropped the ball with Mail in
OS X Mavericks, and to a lesser extent, in iOS 7.&lt;/p&gt;
&lt;p&gt;So I have been on a campaign lately to find an email client that suits my needs.
I don&#39;t feel that I have found the perfect answer yet, but I am getting there.&lt;/p&gt;
&lt;p&gt;The first step was to work out what I really needed in an email client:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;support for IMAP accounts and Gmail accounts&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;line-height: 1.5;&quot;&gt;unified inbox - all accounts shown
together&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;conversation threading&lt;/li&gt;
&lt;li&gt;support for POP accounts (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;On the Mac, I have narrowed it down to two clients that I am swapping between
every few days: &lt;a href=&quot;https://www.uniboxapp.com&quot; target=&quot;_blank&quot; rel=&quot;noreferrer nofollow noopener external&quot;&gt;Unibox&lt;/a&gt;
and &lt;a href=&quot;http://airmailapp.com&quot; target=&quot;_blank&quot; rel=&quot;noreferrer nofollow noopener external&quot;&gt;Airmail&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unibox is different in the way it displays your emails. It is much more focussed
on people and conversations, so the side bar shows all the people who you have
sent emails to or received emails from, grouped by date. I really like the
merging of incoming &amp;amp; outgoing emails, but it can be a bit weird if you delete
the latest email from someone who then disappears from sight.&lt;/p&gt;
&lt;p&gt;Emails are grouped by a single person, not by conversation. There is a button on
appropriate emails to toggle conversation view for that particular thread, but
it doesn&#39;t stick.&lt;/p&gt;
&lt;p&gt;In order to show a conversation, they truncate emails in the main part of the
window and show a &amp;quot;More&amp;quot; button at the bottom of the email to expand it. This
was annoying, but in the latest beta, it performs much better, so you are not
continually tapping this to see a long email.&lt;/p&gt;
&lt;p&gt;Airmail has a more conventional display. Threading and the layout of threaded
emails are both very nice. There are multiple view options, which I find can get
a bit messy looking, so I prefer the &amp;quot;Minimized View&amp;quot; setting. Airmail also does
POP accounts which is a plus.&lt;/p&gt;
&lt;p&gt;The main reason I keep going back to Unibox is the unification. I have grown to
like having sent and received messages all in the same window without having to
go and find them somewhere else.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;On the iPad, I am currently using
&lt;a href=&quot;http://www.getboxer.com&quot; target=&quot;_blank&quot; rel=&quot;noreferrer nofollow noopener external&quot;&gt;Boxer&lt;/a&gt; and have tried
numerous others. &lt;a href=&quot;http://www.moltoapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer nofollow noopener external&quot;&gt;Molto&lt;/a&gt;
was quite good and the interface was fun, but it would only use landscape mode
and like to be able to swap. Boxer is not great in portrait mode, but it does
work.&lt;/p&gt;
&lt;p&gt;So the jury is still out for iPad email clients, but on the Mac, I am leaning
towards Unibox.&lt;/p&gt;
</content>
  </entry>
</feed>