<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>coofdy.com</title>
  <subtitle>Martin Kenny&#39;s website</subtitle>
  <link href="https://coofdy.com/feed/feed.xml" rel="self" />
  <link href="https://coofdy.com/" />
  <updated>2024-12-13T00:00:00Z</updated>
  <id>https://coofdy.com/</id>
  <author>
    <name>Martin Kenny</name>
  </author>
  <entry>
    <title>Recovering data from multi-disk ZIP archives</title>
    <link href="https://coofdy.com/blog/2024-12-13-recovering-data-from-multi-disk-zip-archives/" />
    <updated>2024-12-13T00:00:00Z</updated>
    <id>https://coofdy.com/blog/2024-12-13-recovering-data-from-multi-disk-zip-archives/</id>
    <content type="html">&lt;p&gt;Last year, I wrote about how I had recently been &lt;a href=&quot;https://coofdy.com/blog/2023-04-28-restoring-dos-backups-in-2023/&quot;&gt;imaging and archiving my old 3.5-inch floppy disks&lt;/a&gt; from the late &#39;80s to mid-&#39;90s. Quite a number of those disks contain multi-disk ZIP archives, made by the original &lt;a href=&quot;https://en.wikipedia.org/wiki/PKZIP&quot;&gt;PKZIP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is a short one about the steps I figured out to extract them using &lt;code&gt;zip&lt;/code&gt; and &lt;code&gt;unzip&lt;/code&gt; from the &lt;a href=&quot;http://www.info-zip.org/&quot;&gt;Info-ZIP&lt;/a&gt; package. (The &lt;code&gt;unzip&lt;/code&gt; and &lt;code&gt;zip&lt;/code&gt; that are installed by default on macOS.)&lt;/p&gt;
&lt;p&gt;To extract files from multi-part ZIP archives (the ones with volume labels like &lt;code&gt;PKBACK# 001&lt;/code&gt;, etc), perform the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rename the original zip files (from the individual disk volumes) to &lt;code&gt;&amp;lt;name&amp;gt;.z01&lt;/code&gt;, &lt;code&gt;&amp;lt;name&amp;gt;.z02&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Run: &lt;code&gt;zip -FF &amp;lt;name&amp;gt;.z01 --out &amp;lt;name&amp;gt;.zip&lt;/code&gt;
&lt;ol&gt;
&lt;li&gt;Answer &lt;code&gt;n&lt;/code&gt; when it asks: &lt;code&gt;Is this a single-disk archive?  (y/n):&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Answer &lt;code&gt;e&lt;/code&gt; (end) when it complains that it &lt;code&gt;could not find &amp;lt;name&amp;gt;.z0n&lt;/code&gt; (where n is one higher than the last file number).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;unzip &amp;lt;name&amp;gt;.zip&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should now have all the files in the combined archive.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Fixing stuck macOS Local Network permissions</title>
    <link href="https://coofdy.com/blog/2024-12-04-fixing-stuck-macos-local-network-permissions/" />
    <updated>2024-12-04T00:00:00Z</updated>
    <id>https://coofdy.com/blog/2024-12-04-fixing-stuck-macos-local-network-permissions/</id>
    <content type="html">&lt;h3 id=&quot;tl-dr&quot;&gt;TL;DR&lt;/h3&gt;
&lt;p&gt;macOS permissions can become stuck in a state where the UI shows them turned on, but the system behaves as though they are turned off. The workaround is to reset the permission with &lt;code&gt;tccutil&lt;/code&gt;, then find and delete every copy of the app with the same Bundle ID, and maybe restart.&lt;/p&gt;
&lt;h3 id=&quot;the-scene&quot;&gt;The Scene&lt;/h3&gt;
&lt;p&gt;Apple added a yet another new permission prompt in macOS 15 Sequoia; they brought Local Network permission over from iOS. Now when an app needs to access another device (like a server) on the same local network (over Ethernet or Wi-Fi), the system will throw up a permission dialog on first access, then fail local network access until the user gives the app permission.&lt;/p&gt;
&lt;p&gt;That&#39;s all well-and-good; until it&#39;s not. Shortly after the first beta release of Sequoia, I was working on one of my personal apps that accesses a service on a local server. At this point I&#39;d already given the app Local Network permission, and things had been working smoothly.&lt;/p&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;As part of the changes I was making to the app, I was renaming it from All-the-Things to Omni. Suddenly Omni was unable to access my server, as though I&#39;d denied Local Network access. Checking System Settings &amp;gt; Privacy &amp;amp; Security &amp;gt; Local Network showed Omni in there, under the new name, and with access turned on. There may even have been two entries in there (it was 6 months ago, after all).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://michaelmkenny.com/&quot;&gt;Michael&lt;/a&gt; and I ended up spending half the day trying to get the Local Network permission dialog to appear. It seems that once you’ve approved access for a given Bundle ID, if you change something about the built app, such as the target name, you will be silently denied Local Network access.&lt;/p&gt;
&lt;h3 id=&quot;the-workaround&quot;&gt;The Workaround&lt;/h3&gt;
&lt;p&gt;In the end, the workaround is to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;tccutil&lt;/code&gt; to remove all permissions for your app&#39;s bundle id.&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;tccutil reset All your.bundle.id&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Then, remove all copies of the app that the system can see. Don&#39;t forget to find all the builds of your app that are inside your Developer folder. I&#39;m not sure if you need to remove builds that are inside your Xcode Archives (&lt;code&gt;.xcarchive&lt;/code&gt;). Also, you probably need to empty the Trash.&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt; /Applications &lt;span class=&quot;token parameter variable&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;YourApp.app&#39;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-type&lt;/span&gt; d
&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt; ~ &lt;span class=&quot;token parameter variable&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;YourApp.app&#39;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-type&lt;/span&gt; d&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re lucky, the app will be gone from the System Settings page. If it’s not gone, just missing its icon, a reboot might fix that. Once it’s gone from Settings, the next launch of the app (after re-building it) should show the permission dialog again.&lt;/p&gt;
&lt;h3 id=&quot;open-questions&quot;&gt;Open Questions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Exactly how extensively to you need to remove copies of your app? Maybe the system is only looking in a couple of these places. For example is it looking in the &lt;code&gt;.xcarchive&lt;/code&gt; folders?&lt;/li&gt;
&lt;li&gt;Is this bug limited to just the Local Network permission, or does it affect all the &lt;a href=&quot;https://book.hacktricks.xyz/macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-tcc&quot;&gt;TCC&lt;/a&gt;-controlled permissions?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;epilogue&quot;&gt;Epilogue&lt;/h3&gt;
&lt;p&gt;A couple of days ago Michael called me, asking me what the steps were to get out of this situation. The app he&#39;s writing had suddenly fallen into the same state. I dug up my old notes, read them out to him, then he used &lt;code&gt;find&lt;/code&gt; to locate and delete all copies of his app. After a restart, the permission dialog appeared again. At that point we decided I should write this up for other people to find.&lt;/p&gt;
&lt;p&gt;Also, I just looked at my Local Network permissions page, and saw the same problem waiting to bite me 😳:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/ZhuwZZfjs0-368.avif 368w, https://coofdy.com/images/scaled/ZhuwZZfjs0-736.avif 736w, https://coofdy.com/images/scaled/ZhuwZZfjs0-900.avif 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/ZhuwZZfjs0-368.webp 368w, https://coofdy.com/images/scaled/ZhuwZZfjs0-736.webp 736w, https://coofdy.com/images/scaled/ZhuwZZfjs0-900.webp 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/ZhuwZZfjs0-368.png&quot; alt=&quot;Part of a macOS Settings dialog, showing three instances of the Omni app, all with checkboxes on.&quot; width=&quot;900&quot; height=&quot;248&quot; srcset=&quot;https://coofdy.com/images/scaled/ZhuwZZfjs0-368.png 368w, https://coofdy.com/images/scaled/ZhuwZZfjs0-736.png 736w, https://coofdy.com/images/scaled/ZhuwZZfjs0-900.png 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Restoring old DOS backups in 2023</title>
    <link href="https://coofdy.com/blog/2023-04-28-restoring-dos-backups-in-2023/" />
    <updated>2023-04-28T00:00:00Z</updated>
    <id>https://coofdy.com/blog/2023-04-28-restoring-dos-backups-in-2023/</id>
    <content type="html">&lt;p&gt;Recently, as part of an ongoing effort to preserve old work, and to free up physical storage space, I went through the process of imaging all my 3.5-inch floppy disks. I’ve been using my &lt;a href=&quot;https://www.kryoflux.com/&quot;&gt;Kryoflux board&lt;/a&gt; to save off a &lt;code&gt;.img&lt;/code&gt; file for each floppy.&lt;/p&gt;
&lt;p&gt;While most of the disks contained &lt;code&gt;.zip&lt;/code&gt; files, or multi-disk &lt;code&gt;.zip&lt;/code&gt; archives (something for &lt;a href=&quot;https://coofdy.com/blog/2024-12-13-recovering-data-from-multi-disk-zip-archives/&quot;&gt;another post&lt;/a&gt;), there were several multi-disk backup sets created by some version of DOS Backup in the mid-nineties.&lt;/p&gt;
&lt;p&gt;Each of these backup disks contained a single file with names like &lt;code&gt;KK50306D.001&lt;/code&gt;. Looking at these files in a hex editor, we can see that they contain the string &lt;code&gt;NORTON Ver 1E&lt;/code&gt; in their header. Apparently Microsoft licensed Norton Backup for inclusion in versions of DOS at around that time.
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/XeF9ZiN5fh-368.avif 368w, https://coofdy.com/images/scaled/XeF9ZiN5fh-736.avif 736w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/XeF9ZiN5fh-368.webp 368w, https://coofdy.com/images/scaled/XeF9ZiN5fh-736.webp 736w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/XeF9ZiN5fh-368.png&quot; alt=&quot;The first bytes of a backup file, including the string NORTON Ver 1E&quot; width=&quot;736&quot; height=&quot;276&quot; srcset=&quot;https://coofdy.com/images/scaled/XeF9ZiN5fh-368.png 368w, https://coofdy.com/images/scaled/XeF9ZiN5fh-736.png 736w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;I fondly imagined that someone had written an open source tool for restoring these; I didn’t find one. I briefly thought about doing that myself.&lt;/p&gt;
&lt;p&gt;Here are the steps I ended up taking to restore my backup sets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It looked as though the only option was using the original DOS restore functionality. But which version of DOS?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Research led to DOS 6, 6.2, or 6.22 (see &lt;a href=&quot;https://en.wikipedia.org/wiki/DriveSpace&quot;&gt;DoubleSpace vs DriveSpace and the Stac Electronics patent&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I installed DOS 6.22 in a VirtualBox VM and tried its version of &lt;code&gt;msbackup&lt;/code&gt;. It couldn’t reconstruct the catalog from my disk images.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Some mucking around and it reconstructed the catalog from specially-named directories on disk, but when I tried to restore, it skipped all the files without explaining why.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;So I installed Windows 3.11 and tried the Windows version of backup, &lt;code&gt;mwbackup&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;mwbackup&lt;/code&gt; told me that this backup was made by DOS 6 or 6.20, and pointed me to the DOS readme file, which explained that I could install the DOS 6 or 6.20 versions of &lt;code&gt;msbackup&lt;/code&gt; over the 6.22 ones.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After accidentally trashing my VM, I rebuilt DOS 6.22 and installed the DOS 6 &lt;code&gt;msbackup&lt;/code&gt; over the top.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This version was happy to reconstruct the catalog from floppy images, and I was able to restore all my backup sets.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The backups include the drive letter that the original files were backed up from. You can choose to restore to a different drive letter, however &lt;code&gt;msbackup&lt;/code&gt; will prompt with an &amp;quot;Are you sure?&amp;quot; message for each new directory it needs to create. To avoid that, I used the DOS &lt;code&gt;subst&lt;/code&gt; command to map my drive as the drive letter from the original backups, then picked the &lt;code&gt;msbackup&lt;/code&gt; option to restore to the original drive letter.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some links which helped me through this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tek-tips.com/viewthread.cfm?qid=1586851&quot;&gt;Restoring a Norton Version 1E floppy backup&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tek-tips.com/viewthread.cfm?qid=18860&quot;&gt;Norton Backup for Win3.X&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20031218174059/https://www.fpns.net/willy/msbackup.htm&quot;&gt;Win9x &amp;amp; XP MSBackUp File Format and Data Recovery&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>SwiftUI errors with any View vs some View</title>
    <link href="https://coofdy.com/blog/2022-08-25-any-vs-some/" />
    <updated>2022-08-25T00:00:00Z</updated>
    <id>https://coofdy.com/blog/2022-08-25-any-vs-some/</id>
    <content type="html">&lt;p&gt;When writing SwiftUI code, Xcode sometimes complains with an error much like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Generic struct &#39;List&#39; requires that &#39;some AccessibilityRotorContent&#39; conform to &#39;View&#39;
Initializer &#39;init(content:)&#39; requires that &#39;some AccessibilityRotorContent&#39; conform to &#39;View&#39;
Struct &#39;ViewBuilder&#39; requires that &#39;some AccessibilityRotorContent&#39; conform to &#39;View&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When this happens, it turns out that I&#39;ve just written something like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;highlight-line&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&gt;
&lt;span class=&quot;highlight-line&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&gt;
&lt;span class=&quot;highlight-line&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&gt;
&lt;span class=&quot;highlight-line&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;&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; &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&gt;
&lt;span class=&quot;highlight-line&quot;&gt;                &lt;span class=&quot;token class-name&quot;&gt;MyView&lt;/span&gt;&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;highlight-line&quot;&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;myNewModifier&lt;/span&gt;&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;highlight-line&quot;&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyView&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&gt;
&lt;span class=&quot;highlight-line&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&gt;
&lt;span class=&quot;highlight-line&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;MyView&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;    &lt;/span&gt;
&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;myNewModifier&lt;/span&gt;&lt;span class=&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; any &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;/mark&gt;
&lt;span class=&quot;highlight-line&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;&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&gt;
&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The subtle error is that the signature of &lt;code&gt;myNewModifier&lt;/code&gt; should have looked like this:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&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;myNewModifier&lt;/span&gt;&lt;span class=&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 keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the &lt;code&gt;some&lt;/code&gt; vs the &lt;code&gt;any&lt;/code&gt; in my non-compiling code.&lt;/p&gt;
&lt;h3 id=&quot;postscript&quot;&gt;Postscript&lt;/h3&gt;
&lt;p&gt;I was going to put this down to inattention on my part, but as I was writing this post, I noticed what the sequence of events is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I write a function signature like&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&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;myNewModifier&lt;/span&gt;&lt;span class=&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;View&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Xcode responds with the following error and Fix-it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Use of protocol &#39;View&#39; as a type must be written &#39;any View&#39;
Replace &#39;View&#39; with &#39;any View&#39;  [Fix]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I click &lt;code&gt;Fix&lt;/code&gt;, and the scene is set for me to run around like a headless chook, looking for the mystery error.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <title>SceneKit: first steps — Part 1: Just a box</title>
    <link href="https://coofdy.com/blog/2016-05-11-scenekit-first-steps-1/" />
    <updated>2016-05-11T00:00:00Z</updated>
    <id>https://coofdy.com/blog/2016-05-11-scenekit-first-steps-1/</id>
    <content type="html">&lt;h3 id=&quot;background&quot;&gt;Background&lt;/h3&gt;
&lt;p&gt;In the spirit of &lt;a href=&quot;http://austinkleon.com/show-your-work/&quot;&gt;#showyourwork&lt;/a&gt;, and learning in the open, I&#39;ve decided to document my journey as I teach myself about &lt;a href=&quot;https://developer.apple.com/scenekit/&quot;&gt;SceneKit&lt;/a&gt; on iOS and OS X, and modern 3D APIs.&lt;/p&gt;
&lt;h4 id=&quot;early-1980s&quot;&gt;Early 1980s&lt;/h4&gt;
&lt;p&gt;My first foray into 3D was sometime in the early 1980s, when I played with drawing a 3D model of the earth. My inspiration was most likely Robert Tinney&#39;s cover for the May 1979 issue of &lt;a href=&quot;https://en.wikipedia.org/wiki/Byte_%28magazine%29&quot;&gt;&lt;em&gt;BYTE&lt;/em&gt; magazine&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.vintagefreeware.com/bytecvrs.htm&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/ZNu2r4E1rL-368.avif 368w, https://coofdy.com/images/scaled/ZNu2r4E1rL-600.avif 600w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/ZNu2r4E1rL-368.webp 368w, https://coofdy.com/images/scaled/ZNu2r4E1rL-600.webp 600w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/ZNu2r4E1rL-368.jpeg&quot; alt=&quot;May 1979 BYTE cover&quot; width=&quot;600&quot; height=&quot;782&quot; srcset=&quot;https://coofdy.com/images/scaled/ZNu2r4E1rL-368.jpeg 368w, https://coofdy.com/images/scaled/ZNu2r4E1rL-600.jpeg 600w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately inspiration didn&#39;t equal reality on my home-brewed &lt;a href=&quot;https://en.wikipedia.org/wiki/TRS-80&quot;&gt;TRS-80&lt;/a&gt; clone. Its 128×48 graphics mode was a bit limiting, and state-of-the-art 3D on that platform looked like &lt;a href=&quot;http://fshistory.simflight.com/fsvault/fs1-trs80.htm&quot;&gt;subLogic &lt;em&gt;Flight Simulator&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://fshistory.simflight.com/fsvault/fs1-trs80.htm&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/GwzR9u82-q-368.avif 368w, https://coofdy.com/images/scaled/GwzR9u82-q-520.avif 520w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/GwzR9u82-q-368.webp 368w, https://coofdy.com/images/scaled/GwzR9u82-q-520.webp 520w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/GwzR9u82-q-368.gif&quot; alt=&quot;A screenshot of subLogic Flight Simulator on the TRS-80&quot; width=&quot;520&quot; height=&quot;400&quot; srcset=&quot;https://coofdy.com/images/scaled/GwzR9u82-q-368.gif 368w, https://coofdy.com/images/scaled/GwzR9u82-q-520.gif 520w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;mid-1990s&quot;&gt;Mid 1990s&lt;/h4&gt;
&lt;p&gt;In the mid 1990s I was experimenting with projecting from 3D to 2D, then drawing directly to Windows&#39; GDI API. Later, I played around with Microsoft&#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/WinG&quot;&gt;WinG API&lt;/a&gt;, then &lt;a href=&quot;https://en.wikipedia.org/wiki/Direct3D&quot;&gt;Direct3D&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;early-2000s&quot;&gt;Early 2000s&lt;/h4&gt;
&lt;p&gt;By about 2000, my inspiration was the movie &lt;em&gt;Toy Story&lt;/em&gt;, and the game &lt;a href=&quot;https://en.wikipedia.org/wiki/Train_Simulator_(Dovetail_Games)&quot;&gt;&lt;em&gt;Train Simulator&lt;/em&gt;&lt;/a&gt; (fuelled by my then two-year-old son&#39;s train obsession). I still have a bunch of books with titles like &lt;em&gt;OpenGL Programming Guide&lt;/em&gt; (the &lt;a href=&quot;http://www.glprogramming.com/red/&quot;&gt;&amp;quot;red book&amp;quot;&lt;/a&gt;), &lt;em&gt;Advanced RenderMan&lt;/em&gt;, &lt;em&gt;Real-Time Rendering&lt;/em&gt;, and &lt;em&gt;Advanced Animation and Rendering Techniques&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;At the time I played around with modelling the front of a locomotive, in code, and rendering it with OpenGL.&lt;/p&gt;
&lt;h4 id=&quot;now&quot;&gt;Now&lt;/h4&gt;
&lt;p&gt;Today&#39;s inspiration are games like &lt;em&gt;The Witness&lt;/em&gt; and &lt;em&gt;Monument Valley&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://store.steampowered.com/app/210970/&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/eAMDalc3Uj-368.avif 368w, https://coofdy.com/images/scaled/eAMDalc3Uj-736.avif 736w, https://coofdy.com/images/scaled/eAMDalc3Uj-900.avif 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/eAMDalc3Uj-368.webp 368w, https://coofdy.com/images/scaled/eAMDalc3Uj-736.webp 736w, https://coofdy.com/images/scaled/eAMDalc3Uj-900.webp 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/eAMDalc3Uj-368.jpeg&quot; alt=&quot;A screenshot of The Witness&quot; width=&quot;900&quot; height=&quot;506&quot; srcset=&quot;https://coofdy.com/images/scaled/eAMDalc3Uj-368.jpeg 368w, https://coofdy.com/images/scaled/eAMDalc3Uj-736.jpeg 736w, https://coofdy.com/images/scaled/eAMDalc3Uj-900.jpeg 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/a&gt; &lt;a href=&quot;https://itunes.apple.com/us/app/monument-valley/id728293409?mt=8&amp;amp;at=1000lmTc&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/la0xmW__CL-368.avif 368w, https://coofdy.com/images/scaled/la0xmW__CL-736.avif 736w, https://coofdy.com/images/scaled/la0xmW__CL-900.avif 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/la0xmW__CL-368.webp 368w, https://coofdy.com/images/scaled/la0xmW__CL-736.webp 736w, https://coofdy.com/images/scaled/la0xmW__CL-900.webp 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/la0xmW__CL-368.jpeg&quot; alt=&quot;A screenshot of Monument Valley&quot; width=&quot;900&quot; height=&quot;1343&quot; srcset=&quot;https://coofdy.com/images/scaled/la0xmW__CL-368.jpeg 368w, https://coofdy.com/images/scaled/la0xmW__CL-736.jpeg 736w, https://coofdy.com/images/scaled/la0xmW__CL-900.jpeg 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;
&lt;p&gt;Enough history. Let&#39;s get started!&lt;/p&gt;
&lt;p&gt;Through this series, I&#39;m being guided by Apple&#39;s &lt;a href=&quot;https://developer.apple.com/library/ios/documentation/SceneKit/Reference/SceneKit_Framework/&quot;&gt;SceneKit documentation&lt;/a&gt;, David Rönnqvist&#39;s &lt;a href=&quot;https://www.objc.io/issues/18-games/scenekit/&quot;&gt;SceneKit article from objc.io&lt;/a&gt;, and his excellent iBooks book &lt;a href=&quot;https://geo.itunes.apple.com/us/book/3d-graphics-with-scene-kit/id936235049?mt=11&amp;amp;at=1000lmTc&quot;&gt;&lt;em&gt;3D Graphics with Scene Kit&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;ll be doing all of these steps in a simple single-view application. The scene is presented in an &lt;code&gt;SCNView&lt;/code&gt; that completly covers the app&#39;s main view. The &lt;code&gt;SCNView&lt;/code&gt; from the storyboard is accessed through an &lt;code&gt;@IBOutlet&lt;/code&gt; called &lt;code&gt;sceneView&lt;/code&gt;. The complete listing for the view controller can be found at the bottom of this post.&lt;/p&gt;
&lt;h3 id=&quot;an-empty-scene&quot;&gt;An empty scene&lt;/h3&gt;
&lt;p&gt;The first step is to create an empty scene. Note that I set the scence view&#39;s background color to black, for that super-spooky look (and to make sure that the simple lighting looks realistic). I also get a reference to the scene&#39;s root node, as a convenience for later:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; scene &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNScene&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
sceneView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scene &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; scene
sceneView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;debugOptions &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 comment&quot;&gt;// [.ShowLightExtents, .ShowBoundingBoxes]&lt;/span&gt;
sceneView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundColor &lt;span class=&quot;token operator&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;&lt;span class=&quot;token function&quot;&gt;blackColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
sceneView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allowsCameraControl &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;// allow dragging the camera around&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; rootNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; scene&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rootNode&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;an-actor&quot;&gt;An actor&lt;/h3&gt;
&lt;p&gt;Next up we need something to look at. In this first test, it&#39;s going to be a 1 × 1 × 1 cube with rounded edges. SceneKit provides the &lt;code&gt;SCNBox&lt;/code&gt; class which creates some geometry in the shape of a rounded box. Add one of those to a node, and add that node to the root node of the scene, and we have our boxy actor:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&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;createBoxNode&lt;/span&gt;&lt;span class=&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;SCNNode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// make a 1×1×1 box&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNBox&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;1&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;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; length&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; chamferRadius&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&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; boxNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;geometry&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; boxNode
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

…

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; boxNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createBoxNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
rootNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addChildNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;boxNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;camera&quot;&gt;Camera&lt;/h3&gt;
&lt;p&gt;At this point the scene will render, but with a default light and camera. The position of the default camera means that the cube will fill the entire viewport. To fix that we need our own camera.&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&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;createCameraNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lookingAt target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&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;SCNNode&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; lookAt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNLookAtConstraint&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; target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    lookAt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gimbalLockEnabled &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;let&lt;/span&gt; camera &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNCamera&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    camera&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xFov &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60.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; cameraNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    cameraNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;camera &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; camera
    cameraNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constraints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;lookAt&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cameraNode
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

…

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cameraNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createCameraNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lookingAt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; boxNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
cameraNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNVector3&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;2&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.5&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;2.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
rootNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addChildNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cameraNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I&#39;ve setup a camera with a field-of-view of 60º in the x-axis (&lt;code&gt;xFov&lt;/code&gt;), a constraint that keeps the camera looking at the box, and with gimbal-lock enabled to keep the horizon level if the camera moves.&lt;/p&gt;
&lt;p&gt;With the camera moved into place, here&#39;s what we can see (remember that we&#39;re still using the default light source):&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/A__PIofsHV-368.avif 368w, https://coofdy.com/images/scaled/A__PIofsHV-736.avif 736w, https://coofdy.com/images/scaled/A__PIofsHV-900.avif 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/A__PIofsHV-368.webp 368w, https://coofdy.com/images/scaled/A__PIofsHV-736.webp 736w, https://coofdy.com/images/scaled/A__PIofsHV-900.webp 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/A__PIofsHV-368.png&quot; alt=&quot;A cube rendered with default lighting&quot; width=&quot;900&quot; height=&quot;505&quot; srcset=&quot;https://coofdy.com/images/scaled/A__PIofsHV-368.png 368w, https://coofdy.com/images/scaled/A__PIofsHV-736.png 736w, https://coofdy.com/images/scaled/A__PIofsHV-900.png 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3 id=&quot;lights&quot;&gt;Lights&lt;/h3&gt;
&lt;p&gt;So, things don&#39;t look great with the default lighting. Let&#39;s replace the defaults with our own light:&lt;/p&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&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;createOmniDirectionalLightNode&lt;/span&gt;&lt;span class=&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;SCNNode&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; omniLight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNLight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNLightTypeOmni&lt;/span&gt;
    omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&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;&lt;span class=&quot;token keyword&quot;&gt;init&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&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.75&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.75&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; alpha&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;
    omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attenuationStartDistance &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;
    omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attenuationEndDistance &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;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; omniLightNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    omniLightNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;light &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; omniLight
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; omniLightNode
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

…

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; omniLightNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createOmniDirectionalLightNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
omniLightNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNVector3&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.5&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;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
rootNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addChildNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;omniLightNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;ve placed a light pink (rose gold!) omnidirectional light to the right, above, and in-front of the box. I&#39;ve also used &lt;code&gt;attenuationStartDistance&lt;/code&gt; and &lt;code&gt;attenuationEndDistance&lt;/code&gt; to make sure that the brightness falls off with distance from the light. Here&#39;s what it looks like now:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/g90pgzHLsb-368.avif 368w, https://coofdy.com/images/scaled/g90pgzHLsb-736.avif 736w, https://coofdy.com/images/scaled/g90pgzHLsb-900.avif 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/g90pgzHLsb-368.webp 368w, https://coofdy.com/images/scaled/g90pgzHLsb-736.webp 736w, https://coofdy.com/images/scaled/g90pgzHLsb-900.webp 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/g90pgzHLsb-368.png&quot; alt=&quot;A cube rendered with one omnidirectional light&quot; width=&quot;900&quot; height=&quot;505&quot; srcset=&quot;https://coofdy.com/images/scaled/g90pgzHLsb-368.png 368w, https://coofdy.com/images/scaled/g90pgzHLsb-736.png 736w, https://coofdy.com/images/scaled/g90pgzHLsb-900.png 900w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The cube is starting to look better, but some better materials and texture could definitely spruce things up. Next time I&#39;ll look at adding some of those.&lt;/p&gt;
&lt;h3 id=&quot;viewcontroller-swift&quot;&gt;ViewController.swift&lt;/h3&gt;
&lt;pre class=&quot;language-swift&quot; tabindex=&quot;0&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;ViewController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&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 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; sceneView&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNView&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&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;viewDidLoad&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&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 function&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&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;// create a brand-new scene&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; scene &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNScene&lt;/span&gt;&lt;span class=&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; rootNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; scene&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rootNode

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; boxNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createBoxNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        rootNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addChildNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;boxNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; omniLightNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createOmniDirectionalLightNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        omniLightNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNVector3&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.5&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;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        rootNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addChildNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;omniLightNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cameraNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createCameraNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lookingAt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; boxNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        cameraNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNVector3&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;2&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.5&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;2.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        rootNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addChildNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cameraNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        sceneView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pointOfView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cameraNode
        sceneView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scene &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; scene
    &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;createBoxNode&lt;/span&gt;&lt;span class=&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;SCNNode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// make a 1x1x1 box&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNBox&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;1&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;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; length&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; chamferRadius&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&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; boxNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;geometry&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; boxNode
    &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;createOmniDirectionalLightNode&lt;/span&gt;&lt;span class=&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;SCNNode&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; omniLight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNLight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNLightTypeOmni&lt;/span&gt;
        omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&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;&lt;span class=&quot;token keyword&quot;&gt;init&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&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.75&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.75&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; alpha&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;
        omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attenuationStartDistance &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;
        omniLight&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attenuationEndDistance &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;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; omniLightNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        omniLightNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;light &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; omniLight
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; omniLightNode
    &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;createCameraNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lookingAt target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&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;SCNNode&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; lookAt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNLookAtConstraint&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; target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; camera &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNCamera&lt;/span&gt;&lt;span class=&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; cameraNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SCNNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        cameraNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;camera &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; camera
        cameraNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constraints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;lookAt&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cameraNode
    &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;
</content>
  </entry>
  <entry>
    <title>A fix for cwRsync ignoring --delete</title>
    <link href="https://coofdy.com/blog/2016-05-02-cwrsync-ignoring-delete/" />
    <updated>2016-05-03T00:00:00Z</updated>
    <id>https://coofdy.com/blog/2016-05-02-cwrsync-ignoring-delete/</id>
    <content type="html">&lt;p&gt;I was just helping Michael set up a Windows rsync-based solution to sync/backup his symbolic-linked game saves to a central folder (for subsequent syncing with BitTorrent Sync). We installed &lt;a href=&quot;https://www.itefix.net/cwrsync&quot;&gt;cwRsync&lt;/a&gt;, then used a command-line like this:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-aL&lt;/span&gt; /cygdrive/C/Users/michael/GameSaves/ /cygdrive/C/Users/michael/Backups/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copying worked without a hitch, but then we added &lt;code&gt;--delete&lt;/code&gt; to the command-line. This should delete any files from the Backups folder that have been deleted from the GameSaves folder since the last sync:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-aL&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--delete&lt;/span&gt; /cygdrive/C/Users/michael/GameSaves/ /cygdrive/C/Users/michael/Backups/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike the copying, this proved difficult to get working. After longer than I care to admit, I listened to my Spidey-sense, and switched the upper-case drive letters to lower-case. Suddenly the deletes started working! The final command-line looked like this:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-aL&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--delete&lt;/span&gt; /cygdrive/c/Users/michael/GameSaves/ /cygdrive/c/Users/michael/Backups/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, it seems that cwRsync (and possibly a full Cygwin rsync installation) doesn&#39;t like upper-cased drive letters in its paths.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Fixing &#39;could not find symbol value for runtime.buildVersion&#39; Delve debugger error</title>
    <link href="https://coofdy.com/blog/2015-09-13-delve-debugger-error/" />
    <updated>2015-09-13T02:40:00Z</updated>
    <id>https://coofdy.com/blog/2015-09-13-delve-debugger-error/</id>
    <content type="html">&lt;h3 id=&quot;tl-dr&quot;&gt;TL;DR&lt;/h3&gt;
&lt;p&gt;If you&#39;re trying to debug Go code using the &lt;a href=&quot;https://github.com/derekparker/delve&quot;&gt;Delve debugger&lt;/a&gt;, or the &lt;a href=&quot;https://github.com/go-lang-plugin-org/go-lang-idea-plugin&quot;&gt;Go plug-in for IntelliJ&lt;/a&gt; (which uses Delve), and you get the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;could not launch process: Could not determine version number:
	could not find symbol value for runtime.buildVersion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you should upgrade your go environment to a newer version.&lt;/p&gt;
&lt;h3 id=&quot;details&quot;&gt;Details&lt;/h3&gt;
&lt;p&gt;Yesterday, I decided I needed a better way to debug Go code than using &lt;code&gt;fmt.Println&lt;/code&gt;. I discovered that there&#39;s a work-in-progress &lt;a href=&quot;https://github.com/go-lang-plugin-org/go-lang-idea-plugin&quot;&gt;Go plug-in for IntelliJ&lt;/a&gt;. After installing the latest &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt; Community Edition, and installing the plug-in, I was dissapointed to be greeted with the &lt;code&gt;could not find symbol value for runtime.buildVersion&lt;/code&gt; error when I tried to debug.&lt;/p&gt;
&lt;p&gt;After searching through the Delve code, I decided that the most likely fix was to upgrade my go installation. At that point I was running version 1.3.3. After upgrading to 1.5.1, I can now happily go about my debugging.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>This time it’s personal</title>
    <link href="https://coofdy.com/blog/2015-05-06-this-time-its-personal/" />
    <updated>2015-05-06T10:00:00Z</updated>
    <id>https://coofdy.com/blog/2015-05-06-this-time-its-personal/</id>
    <content type="html">&lt;h2 id=&quot;a-developer-s-initial-apple-watch-impressions&quot;&gt;A developer’s initial Apple Watch impressions&lt;/h2&gt;
&lt;p&gt;I&#39;ve been building software, in one form or another, for the best part of 40 years. Starting with an 8-bit microprocessor with 256 bytes of RAM, through to several generations of Macs, iPhones, and now Apple Watch, I&#39;ve written code for almost every programmable platform I&#39;ve owned. (Fun fact: The Apple Watch has 2 million times more RAM than that first machine.) Invariably I write one-or-more programs that are for my (or my family&#39;s) personal use on those devices.&lt;/p&gt;
&lt;p&gt;Here&#39;s the thing: The more personal a device is, the more motivated I am to write personally meaningful software for it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I&#39;ve had 6 modern-era Macs, from a PowerBook G4, to my current Retina MacBook Pro. During that time, I&#39;ve written many Python and Ruby scripts to do tasks like photo-management, but I&#39;ve never written a native Cocoa app that was more than an experiment; &lt;em&gt;nothing that was a personal experience&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The iPhone has been my constant companion for the last seven years. During that time, I&#39;ve written several apps that I personally interact with multiple times each day (none on the App Store, but I&#39;m working on it :-) ). &lt;em&gt;I love my iPhone, and I want to extend it for my own ends&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My Apple Watch arrived at lunchtime on launch day. By evening I had one of my most used apps up-and-running on the watch. Since then I&#39;ve continued to refine that app, and I&#39;m working with my son on another. The tools, and some of the interactions on the watch, still seem a little rough around the edges. Despite that, having my own code, right on my wrist, is empowering.  &lt;em&gt;The Apple Watch gives me the chance to build tiny dashboards of actionable personal information, and I&#39;m loving it&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It seems that the more personal (intimate, in Apple&#39;s words) the hardware is, the more eager I am to build personally useful software for it. Hopefully I&#39;ll be super-keen to code for my Apple ContactLens in 2025!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Network Not Starting on Migrated Virtual Machines</title>
    <link href="https://coofdy.com/blog/2013-12-10-network-not-starting-on-migrated-virtual-machines/" />
    <updated>2013-12-09T23:22:19Z</updated>
    <id>https://coofdy.com/blog/2013-12-10-network-not-starting-on-migrated-virtual-machines/</id>
    <content type="html">&lt;p&gt;If you&#39;ve recently migrated a Ubuntu virtual machine file from one computer to another, or from one virtualization product to another, you might have discovered that networking inside the VM was no longer working.&lt;/p&gt;
&lt;p&gt;As described in this &lt;a href=&quot;https://kb.parallels.com/en/8102&quot;&gt;Parallels KB article&lt;/a&gt;, what&#39;s probably happened is that the random MAC address generated by the virtualization host has changed. The file: &lt;code&gt;/etc/udev/rules.d/70-persistent-net.rules&lt;/code&gt; contains a record of the matching MAC address from the previous host. When Ubuntu networking starts, a new interface, perhaps &lt;code&gt;eth1&lt;/code&gt; gets created, but is not listed to start automatically.&lt;/p&gt;
&lt;p&gt;The fix is to edit &lt;code&gt;/etc/udev/rules.d/70-persistent-net.rules&lt;/code&gt; to contain the MAC address from the new virtualization host&#39;s virtual network hardware.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Snowflakes on a Razer Blade</title>
    <link href="https://coofdy.com/blog/2013-12-10-snowflakes-on-a-razer-blade/" />
    <updated>2013-12-09T22:50:05Z</updated>
    <id>https://coofdy.com/blog/2013-12-10-snowflakes-on-a-razer-blade/</id>
    <content type="html">&lt;p&gt;The magnificent set of snowflake photos by Russian photographer &lt;a href=&quot;https://www.flickr.com/photos/chaoticmind75/&quot;&gt;Alexey Kljatov&lt;/a&gt; (recently featured by &lt;a href=&quot;https://kottke.org&quot;&gt;kottke.org&lt;/a&gt;) make for some wonderful desktop images. Michael and I have both just recently updated our desktop images to these snowflake macros.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.flickr.com/photos/chaoticmind75/51233595638&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://coofdy.com/images/scaled/b-U_gVrt9t-368.avif 368w, https://coofdy.com/images/scaled/b-U_gVrt9t-736.avif 736w&quot; sizes=&quot;100vw&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://coofdy.com/images/scaled/b-U_gVrt9t-368.webp 368w, https://coofdy.com/images/scaled/b-U_gVrt9t-736.webp 736w&quot; sizes=&quot;100vw&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://coofdy.com/images/scaled/b-U_gVrt9t-368.jpeg&quot; alt=&quot;Snowflake&quot; width=&quot;736&quot; height=&quot;736&quot; srcset=&quot;https://coofdy.com/images/scaled/b-U_gVrt9t-368.jpeg 368w, https://coofdy.com/images/scaled/b-U_gVrt9t-736.jpeg 736w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Michael&#39;s Razer Blade features an 800x640 display as the trackpad. A resized snowflake makes an ideal trackpad background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; If you&#39;re trying to turn one of these images into a 16:9 or 16:10 wallpaper, Photoshop&#39;s Content-aware Fill (Shift+F5) is your best friend.&lt;/p&gt;
</content>
  </entry>
</feed>